mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-12-01 08:17:38 +00:00
597993cf44
Creator: Michael Olson <mwolson@gnu.org> Install ERC.
397 lines
13 KiB
EmacsLisp
397 lines
13 KiB
EmacsLisp
;;; erc-list.el --- Provide a faster channel listing mechanism
|
|
|
|
;; Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
|
|
;; Copyright (C) 2004 Brian Palmer
|
|
|
|
;; Author: Mario Lang <mlang@lexx.delysid.org>
|
|
;; Keywords: comm
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation; either version 2, or (at your option)
|
|
;; any later version.
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
;; Boston, MA 02110-1301, USA.
|
|
|
|
;;; Commentary:
|
|
|
|
;; This file provides a simple derived mode for viewing Channel lists.
|
|
;; It also serves as a demonstration of how the new server hook facility
|
|
;; can be used.
|
|
|
|
;;; Code:
|
|
|
|
(require 'erc)
|
|
(require 'erc-nets)
|
|
(require 'sort)
|
|
(unless (fboundp 'make-overlay)
|
|
(require 'overlay))
|
|
(eval-when-compile (require 'cl))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; User customizable variables.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defgroup erc-list nil
|
|
"Display IRC channels in another window when using /LIST"
|
|
:group 'erc)
|
|
|
|
(defcustom erc-chanlist-progress-message t
|
|
"*Show progress message while accumulating channel list."
|
|
:group 'erc-list
|
|
:type 'boolean)
|
|
|
|
(defcustom erc-no-list-networks nil
|
|
"*A list of network names on which the /LIST command refuses to work."
|
|
:group 'erc-list
|
|
:type '(repeat string))
|
|
|
|
(defcustom erc-chanlist-frame-parameters nil
|
|
"*If nil, the channel list is displayed in a new window; if non-nil,
|
|
this variable holds the frame parameters used to make a frame to
|
|
display the channel list."
|
|
:group 'erc-list
|
|
:type 'list)
|
|
|
|
(defcustom erc-chanlist-hide-modeline nil
|
|
"*If nil, the channel list buffer has a modeline, otherwise the modeline is hidden."
|
|
:group 'erc-list
|
|
:type 'boolean)
|
|
|
|
(defface erc-chanlist-header-face '((t (:bold t)))
|
|
"Face used for the headers in erc's channel list."
|
|
:group 'erc-faces)
|
|
|
|
(defface erc-chanlist-odd-line-face '((t (:inverse-video t)))
|
|
"Face used for the odd lines in erc's channel list."
|
|
:group 'erc-faces)
|
|
|
|
(defface erc-chanlist-even-line-face '((t (:inverse-video nil)))
|
|
"Face used for the even lines in erc's channel list."
|
|
:group 'erc-faces)
|
|
|
|
(defface erc-chanlist-highlight '((t (:foreground "red")))
|
|
"Face used to highlight the current line in the channel list."
|
|
:group 'erc-faces)
|
|
|
|
;; This should perhaps be a defface that inherits values from the highlight face
|
|
;; but xemacs does not support inheritance
|
|
(defcustom erc-chanlist-highlight-face 'erc-chanlist-highlight
|
|
"Face used for highlighting the current line in a list."
|
|
:type 'face
|
|
:group 'erc-faces)
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; All variables below this line are for internal use only.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defvar erc-chanlist-channel-line-regexp "^\\([#&\\*][^ \t\n]*\\)\\s-+[0-9]+"
|
|
"Regexp that matches a channel line in the channel list buffer.")
|
|
|
|
(defvar erc-chanlist-buffer nil)
|
|
(make-variable-buffer-local 'erc-chanlist-buffer)
|
|
|
|
(defvar erc-chanlist-last-time 0
|
|
"A time value used to throttle the progress indicator.")
|
|
|
|
(defvar erc-chanlist-frame nil
|
|
"The frame displaying the most recent channel list buffer.")
|
|
|
|
(defvar erc-chanlist-sort-state 'channel
|
|
"The sort mode of the channel list buffer. Either 'channel or 'users.")
|
|
(make-variable-buffer-local 'erc-chanlist-sort-state)
|
|
|
|
(defvar erc-chanlist-highlight-overlay nil
|
|
"The overlay used for erc chanlist highlighting")
|
|
(make-variable-buffer-local 'erc-chanlist-highlight-overlay)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Define erc-chanlist-mode.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defcustom erc-chanlist-mode-hook nil
|
|
"Hook run by erc-chanlist-mode."
|
|
:group 'erc-list
|
|
:type 'hook)
|
|
|
|
(define-derived-mode erc-chanlist-mode fundamental-mode "ERC Channel List"
|
|
"Mode for viewing a channel list of a particular server.
|
|
|
|
\\{erc-chanlist-mode-map}"
|
|
(local-set-key "\C-c\C-j" 'erc-join-channel)
|
|
(local-set-key "j" 'erc-chanlist-join-channel)
|
|
(local-set-key "n" 'next-line)
|
|
(local-set-key "p" 'previous-line)
|
|
(local-set-key "q" 'erc-chanlist-quit)
|
|
(local-set-key "s" 'erc-chanlist-toggle-sort-state)
|
|
(local-set-key "t" 'toggle-truncate-lines)
|
|
(setq erc-chanlist-sort-state 'channel)
|
|
(setq truncate-lines t)
|
|
(add-hook 'post-command-hook 'erc-chanlist-post-command-hook 'append 'local))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Functions.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;;###autoload
|
|
(defun erc-cmd-LIST (&rest channel)
|
|
"Display a buffer containing a list of channels on the current server.
|
|
Optional argument CHANNEL specifies a single channel to list (instead of every
|
|
available channel)."
|
|
(interactive
|
|
(remove "" (split-string
|
|
(read-from-minibuffer "List channels (RET for all): ") " ")))
|
|
(if (and (null channel)
|
|
(erc-member-ignore-case (erc-network-name) erc-no-list-networks))
|
|
(erc-display-line "ERC is configured not to allow the /LIST command on this network!"
|
|
(current-buffer))
|
|
(erc-display-line (erc-make-notice (concat "Listing channel"
|
|
(if channel
|
|
"."
|
|
"s. This may take a while."))))
|
|
(erc-chanlist channel))
|
|
t)
|
|
|
|
;;;###autoload
|
|
(defun erc-chanlist (&optional channels)
|
|
"Show a channel listing of the current server in a special mode.
|
|
Please note that this function only works with IRC servers which conform
|
|
to RFC and send the LIST header (#321) at start of list transmission."
|
|
(interactive)
|
|
(with-current-buffer (erc-server-buffer)
|
|
(erc-once-with-server-event
|
|
321
|
|
'(progn
|
|
(add-hook 'erc-server-322-functions 'erc-chanlist-322 nil t)
|
|
|
|
(erc-once-with-server-event
|
|
323
|
|
'(progn
|
|
(remove-hook 'erc-server-322-functions 'erc-chanlist-322 t)
|
|
(let ((buf erc-chanlist-buffer))
|
|
(if (not (buffer-live-p buf))
|
|
(error "`erc-chanlist-buffer' does not refer to a live buffer"))
|
|
|
|
(set-buffer buf)
|
|
(buffer-disable-undo)
|
|
(let (buffer-read-only
|
|
(sort-fold-case t))
|
|
(sort-lines nil (point-min) (point-max))
|
|
(setq erc-chanlist-sort-state 'channel)
|
|
|
|
(let ((sum (count-lines (point-min) (point-max))))
|
|
(goto-char (point-min))
|
|
(insert (substitute-command-keys
|
|
(concat "'\\[erc-chanlist-toggle-sort-state]' toggle sort mode.\n"
|
|
"'\\[erc-chanlist-quit]' kill this buffer.\n"
|
|
"'\\[toggle-truncate-lines]' toggle line truncation.\n"
|
|
"'\\[erc-chanlist-join-channel]' join the channel listed on the current line.\n\n")))
|
|
(insert (format "%d channels (sorted by %s).\n\n"
|
|
sum (if (eq erc-chanlist-sort-state 'channel)
|
|
"channel name"
|
|
"number of users"))))
|
|
|
|
(insert (format "%-25s%5s %s\n------------------------ ----- ----------------------------\n"
|
|
"Channel"
|
|
"Users"
|
|
"Topic"))
|
|
|
|
;; Display the channel list buffer.
|
|
(if erc-chanlist-frame-parameters
|
|
(progn
|
|
(if (or (null erc-chanlist-frame)
|
|
(not (frame-live-p erc-chanlist-frame)))
|
|
(setq erc-chanlist-frame
|
|
(make-frame `((name . ,(format "Channels on %s"
|
|
erc-session-server))
|
|
,@erc-chanlist-frame-parameters))))
|
|
(select-frame erc-chanlist-frame)
|
|
(switch-to-buffer buf)
|
|
(erc-prettify-channel-list))
|
|
(pop-to-buffer buf)
|
|
(erc-prettify-channel-list))))
|
|
(goto-char (point-min))
|
|
(search-forward-regexp "^------" nil t)
|
|
(forward-line 1)
|
|
(erc-chanlist-highlight-line)
|
|
(message "")
|
|
t))
|
|
|
|
(setq erc-chanlist-buffer (get-buffer-create
|
|
(format "*Channels on %s*"
|
|
(erc-response.sender parsed))))
|
|
(with-current-buffer erc-chanlist-buffer
|
|
(setq buffer-read-only nil)
|
|
(erase-buffer)
|
|
(erc-chanlist-mode)
|
|
(setq erc-server-process proc)
|
|
(if erc-chanlist-hide-modeline
|
|
(setq mode-line-format nil))
|
|
(setq buffer-read-only t))
|
|
t))
|
|
|
|
;; Now that we've setup our callbacks, pull the trigger.
|
|
(if (interactive-p)
|
|
(message "Collecting channel list for server %s" erc-session-server))
|
|
(erc-server-send (if (null channels)
|
|
"LIST"
|
|
(concat "LIST "
|
|
(mapconcat #'identity channels ","))))))
|
|
|
|
(defun erc-chanlist-322 (proc parsed)
|
|
"Process an IRC 322 message.
|
|
|
|
The message carries information about one channel for the LIST
|
|
command."
|
|
(multiple-value-bind (channel num-users)
|
|
(cdr (erc-response.command-args parsed))
|
|
(let ((topic (erc-response.contents parsed)))
|
|
(with-current-buffer erc-chanlist-buffer
|
|
(save-excursion
|
|
(goto-char (point-max))
|
|
(let (buffer-read-only)
|
|
(insert (format "%-26s%4s %s\n" (erc-controls-strip channel)
|
|
num-users
|
|
(erc-controls-strip topic))))
|
|
|
|
;; Maybe display a progress indicator in the minibuffer.
|
|
(when (and erc-chanlist-progress-message
|
|
(> (erc-time-diff
|
|
erc-chanlist-last-time (erc-current-time))
|
|
3))
|
|
(setq erc-chanlist-last-time (erc-current-time))
|
|
(message "Accumulating channel list ... %c"
|
|
(aref [?/ ?| ?\\ ?- ?! ?O ?o] (random 7))))
|
|
|
|
;; Return success to prevent other hook functions from being run.
|
|
t)))))
|
|
|
|
(defun erc-chanlist-post-command-hook ()
|
|
"Keep the current line highlighted."
|
|
(ignore-errors
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
(if (looking-at erc-chanlist-channel-line-regexp)
|
|
(erc-chanlist-highlight-line)
|
|
(erc-chanlist-dehighlight-line)))))
|
|
|
|
(defun erc-chanlist-highlight-line ()
|
|
"Highlight the current line."
|
|
(unless erc-chanlist-highlight-overlay
|
|
(setq erc-chanlist-highlight-overlay
|
|
(make-overlay (point-min) (point-min)))
|
|
;; Detach it from the buffer.
|
|
(delete-overlay erc-chanlist-highlight-overlay)
|
|
(overlay-put erc-chanlist-highlight-overlay
|
|
'face erc-chanlist-highlight-face)
|
|
;; Expressly put it at a higher priority than the text
|
|
;; properties used for faces later on. Gnu emacs promises that
|
|
;; right now overlays are higher priority than text properties,
|
|
;; but why take chances?
|
|
(overlay-put erc-chanlist-highlight-overlay 'priority 1))
|
|
(move-overlay erc-chanlist-highlight-overlay (point) (1+ (point-at-eol))))
|
|
|
|
(defun erc-chanlist-dehighlight-line ()
|
|
"Remove the line highlighting."
|
|
(delete-overlay erc-chanlist-highlight-overlay))
|
|
|
|
(defun erc-prettify-channel-list ()
|
|
"Make the channel list buffer look pretty.
|
|
When this function runs, the current buffer must be the channel
|
|
list buffer, or it does nothing."
|
|
(if (eq major-mode 'erc-chanlist-mode)
|
|
(save-excursion
|
|
(let ((inhibit-read-only t))
|
|
(goto-char (point-min))
|
|
(when (search-forward-regexp "^-------" nil t)
|
|
(add-text-properties
|
|
(point-min) (1+ (point-at-eol)) '(face erc-chanlist-header-face))
|
|
(forward-line 1))
|
|
|
|
(while (not (eobp))
|
|
(add-text-properties
|
|
(point) (1+ (point-at-eol)) '(face erc-chanlist-odd-line-face))
|
|
(forward-line 1)
|
|
(unless (eobp)
|
|
(add-text-properties
|
|
(point) (1+ (point-at-eol)) '(face erc-chanlist-even-line-face)))
|
|
(forward-line 1))))))
|
|
|
|
(defun erc-chanlist-toggle-sort-state ()
|
|
"Toggle the channel list buffer sorting method.
|
|
Either sort by channel names or by number of users in each channel."
|
|
(interactive)
|
|
(let ((inhibit-read-only t)
|
|
(sort-fold-case t))
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(search-forward-regexp "^-----" nil t)
|
|
(forward-line 1)
|
|
(unless (eobp)
|
|
(if (eq erc-chanlist-sort-state 'channel)
|
|
(progn
|
|
(sort-numeric-fields 2 (point) (point-max))
|
|
(reverse-region (point) (point-max))
|
|
(setq erc-chanlist-sort-state 'users))
|
|
(sort-lines nil (point) (point-max))
|
|
(setq erc-chanlist-sort-state 'channel))
|
|
|
|
(goto-char (point-min))
|
|
(if (search-forward-regexp "^[0-9]+ channels (sorted by \\(.*\\)).$"
|
|
nil t)
|
|
(replace-match (if (eq erc-chanlist-sort-state 'channel)
|
|
"channel name"
|
|
"number of users")
|
|
nil nil nil 1))
|
|
|
|
(goto-char (point-min))
|
|
(search-forward-regexp "^-----" nil t)
|
|
(forward-line 1)
|
|
(recenter -1)
|
|
|
|
(erc-prettify-channel-list)))))
|
|
|
|
(defun erc-chanlist-quit ()
|
|
"Quit Chanlist mode.
|
|
Kill the channel list buffer, window, and frame (if there's a frame
|
|
devoted to the channel list)."
|
|
(interactive)
|
|
(kill-buffer (current-buffer))
|
|
(if (eq (selected-frame) erc-chanlist-frame)
|
|
(delete-frame)
|
|
(delete-window)))
|
|
|
|
(defun erc-chanlist-join-channel ()
|
|
"Join the channel listed on the current line of the channel list buffer.
|
|
Private channels, which are shown as asterisks (*), are ignored."
|
|
(interactive)
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
(when (looking-at erc-chanlist-channel-line-regexp)
|
|
(let ((channel-name (match-string 1)))
|
|
(when (and (stringp channel-name)
|
|
(not (string= channel-name "*")))
|
|
(run-at-time 0.5 nil 'erc-join-channel channel-name))))))
|
|
|
|
(provide 'erc-list)
|
|
|
|
;;; erc-list.el ends here
|
|
;;
|
|
;; Local Variables:
|
|
;; indent-tabs-mode: t
|
|
;; tab-width: 8
|
|
;; End:
|
|
|
|
;; arch-tag: 4a13196a-a61b-465a-9926-044dfbc7e5ff
|