Commit | Line | Data |
---|---|---|
d0fcaff5 | 1 | ;;; erc-list.el --- /list support for ERC -*- lexical-binding:t -*- |
5e56b3fb | 2 | |
ba318903 | 3 | ;; Copyright (C) 2008-2014 Free Software Foundation, Inc. |
5e56b3fb MO |
4 | |
5 | ;; Author: Tom Tromey <tromey@redhat.com> | |
34dc21db | 6 | ;; Maintainer: emacs-devel@gnu.org |
2998fa1b | 7 | ;; Old-Version: 0.1 |
5e56b3fb MO |
8 | ;; Keywords: comm |
9 | ||
eb3fa2cf | 10 | ;; This file is part of GNU Emacs. |
5e56b3fb | 11 | |
eb3fa2cf | 12 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
5e56b3fb | 13 | ;; it under the terms of the GNU General Public License as published by |
eb3fa2cf GM |
14 | ;; the Free Software Foundation, either version 3 of the License, or |
15 | ;; (at your option) any later version. | |
5e56b3fb | 16 | |
eb3fa2cf | 17 | ;; GNU Emacs is distributed in the hope that it will be useful, |
5e56b3fb MO |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
eb3fa2cf | 23 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
5e56b3fb MO |
24 | |
25 | ;;; Commentary: | |
26 | ||
27 | ;; This file provides nice support for /list in ERC. | |
28 | ||
29 | ;;; Code: | |
30 | ||
31 | (require 'erc) | |
32 | ||
ed8be7ff GM |
33 | (defgroup erc-list nil |
34 | "Support for the /list command." | |
35 | :group 'erc) | |
36 | ||
5e56b3fb MO |
37 | ;; This is implicitly the width of the channel name column. Pick |
38 | ;; something small enough that the topic has a chance of being | |
39 | ;; readable, but long enough that most channel names won't make for | |
40 | ;; strange formatting. | |
41 | (defconst erc-list-nusers-column 25) | |
42 | ||
43 | ;; Width of the number-of-users column. | |
44 | (defconst erc-list-topic-column (+ erc-list-nusers-column 10)) | |
45 | ||
46 | ;; The list buffer. This is buffer local in the server buffer. | |
47 | (defvar erc-list-buffer nil) | |
48 | ||
49 | ;; The argument to the last "/list". This is buffer local in the | |
50 | ;; server buffer. | |
51 | (defvar erc-list-last-argument nil) | |
52 | ||
53 | ;; The server buffer corresponding to the list buffer. This is buffer | |
54 | ;; local in the list buffer. | |
55 | (defvar erc-list-server-buffer nil) | |
56 | ||
57 | ;; Define module: | |
58 | ;;;###autoload (autoload 'erc-list-mode "erc-list") | |
59 | (define-erc-module list nil | |
60 | "List channels nicely in a separate buffer." | |
61 | ((remove-hook 'erc-server-321-functions 'erc-server-321-message) | |
62 | (remove-hook 'erc-server-322-functions 'erc-server-322-message)) | |
63 | ((erc-with-all-buffers-of-server nil | |
64 | #'erc-open-server-buffer-p | |
65 | (remove-hook 'erc-server-322-functions 'erc-list-handle-322 t)) | |
66 | (add-hook 'erc-server-321-functions 'erc-server-321-message t) | |
67 | (add-hook 'erc-server-322-functions 'erc-server-322-message t))) | |
68 | ||
69 | ;; Format a record for display. | |
70 | (defun erc-list-make-string (channel users topic) | |
71 | (concat | |
72 | channel | |
73 | (erc-propertize " " | |
74 | 'display (list 'space :align-to erc-list-nusers-column) | |
75 | 'face 'fixed-pitch) | |
76 | users | |
77 | (erc-propertize " " | |
78 | 'display (list 'space :align-to erc-list-topic-column) | |
79 | 'face 'fixed-pitch) | |
80 | topic)) | |
81 | ||
82 | ;; Insert a record into the list buffer. | |
83 | (defun erc-list-insert-item (channel users topic) | |
84 | (save-excursion | |
85 | (let ((buffer-read-only nil)) | |
86 | (goto-char (point-max)) | |
87 | (insert (erc-list-make-string channel users topic) "\n")))) | |
88 | ||
89 | (defun erc-list-join () | |
90 | "Join the irc channel named on this line." | |
91 | (interactive) | |
92 | (unless (eobp) | |
93 | (beginning-of-line) | |
94 | (unless (looking-at "\\([&#+!][^ \n]+\\)") | |
95 | (error "Not looking at channel name?")) | |
96 | (let ((chan (match-string 1))) | |
97 | (with-current-buffer erc-list-server-buffer | |
98 | (erc-join-channel chan))))) | |
99 | ||
100 | (defun erc-list-kill () | |
101 | "Kill the current ERC list buffer." | |
102 | (interactive) | |
103 | (kill-buffer (current-buffer))) | |
104 | ||
105 | (defun erc-list-revert () | |
106 | "Refresh the list of channels." | |
107 | (interactive) | |
108 | (with-current-buffer erc-list-server-buffer | |
109 | (erc-cmd-LIST erc-list-last-argument))) | |
110 | ||
111 | (defun erc-list-menu-sort-by-column (&optional e) | |
112 | "Sort the channel list by the column clicked on." | |
113 | (interactive (list last-input-event)) | |
114 | (if e (mouse-select-window e)) | |
115 | (let* ((pos (event-start e)) | |
116 | (obj (posn-object pos)) | |
117 | (col (if obj | |
118 | (get-text-property (cdr obj) 'column-number (car obj)) | |
119 | (get-text-property (posn-point pos) 'column-number)))) | |
120 | (let ((buffer-read-only nil)) | |
121 | (if (= col 1) | |
122 | (sort-fields col (point-min) (point-max)) | |
123 | (sort-numeric-fields col (point-min) (point-max)))))) | |
124 | ||
b016851c SM |
125 | (defvar erc-list-menu-mode-map |
126 | (let ((map (make-keymap))) | |
127 | (set-keymap-parent map special-mode-map) | |
128 | (define-key map "k" 'erc-list-kill) | |
129 | (define-key map "j" 'erc-list-join) | |
130 | (define-key map "g" 'erc-list-revert) | |
131 | (define-key map "n" 'next-line) | |
132 | (define-key map "p" 'previous-line) | |
133 | map) | |
134 | "Local keymap for `erc-list-mode' buffers.") | |
135 | ||
abef340a | 136 | (defvar erc-list-menu-sort-button-map |
5e56b3fb MO |
137 | (let ((map (make-sparse-keymap))) |
138 | (define-key map [header-line mouse-1] 'erc-list-menu-sort-by-column) | |
139 | (define-key map [follow-link] 'mouse-face) | |
abef340a SS |
140 | map) |
141 | "Local keymap for ERC list menu mode sorting buttons.") | |
5e56b3fb MO |
142 | |
143 | ;; Helper function that makes a buttonized column header. | |
144 | (defun erc-list-button (title column) | |
145 | (erc-propertize title | |
146 | 'column-number column | |
147 | 'help-echo "mouse-1: sort by column" | |
148 | 'mouse-face 'highlight | |
149 | 'keymap erc-list-menu-sort-button-map)) | |
150 | ||
abef340a | 151 | (define-derived-mode erc-list-menu-mode special-mode "ERC-List" |
5e56b3fb MO |
152 | "Major mode for editing a list of irc channels." |
153 | (setq header-line-format | |
154 | (concat | |
155 | (erc-propertize " " | |
156 | 'display '(space :align-to 0) | |
157 | 'face 'fixed-pitch) | |
158 | (erc-list-make-string (erc-list-button "Channel" 1) | |
159 | (erc-list-button "# Users" 2) | |
160 | "Topic"))) | |
161 | (setq truncate-lines t)) | |
162 | ||
163 | (put 'erc-list-menu-mode 'mode-class 'special) | |
164 | ||
165 | ;; Handle a "322" response. This response tells us about a single | |
166 | ;; channel. | |
0bc8d758 GM |
167 | ;; Called via erc-once-with-server-event with two arguments. |
168 | (defun erc-list-handle-322 (_proc parsed) | |
5e56b3fb MO |
169 | (let* ((args (cdr (erc-response.command-args parsed))) |
170 | (channel (car args)) | |
171 | (nusers (car (cdr args))) | |
172 | (topic (erc-response.contents parsed))) | |
173 | (when (buffer-live-p erc-list-buffer) | |
174 | (with-current-buffer erc-list-buffer | |
175 | (erc-list-insert-item channel nusers topic)))) | |
176 | ;; Don't let another hook run. | |
177 | t) | |
178 | ||
179 | ;; Helper function to install our 322 handler and make our buffer. | |
180 | (defun erc-list-install-322-handler (server-buffer) | |
181 | (with-current-buffer server-buffer | |
182 | ;; Arrange for 322 responses to insert into our buffer. | |
183 | (add-hook 'erc-server-322-functions 'erc-list-handle-322 t t) | |
184 | ;; Arrange for 323 (end of list) to end this. | |
185 | (erc-once-with-server-event | |
186 | 323 | |
d0fcaff5 | 187 | (lambda (_proc _parsed) |
5e56b3fb MO |
188 | (remove-hook 'erc-server-322-functions 'erc-list-handle-322 t))) |
189 | ;; Find the list buffer, empty it, and display it. | |
190 | (set (make-local-variable 'erc-list-buffer) | |
191 | (get-buffer-create (concat "*Channels of " | |
192 | erc-server-announced-name | |
193 | "*"))) | |
194 | (with-current-buffer erc-list-buffer | |
195 | (erc-list-menu-mode) | |
196 | (setq buffer-read-only nil) | |
197 | (erase-buffer) | |
198 | (set (make-local-variable 'erc-list-server-buffer) server-buffer) | |
199 | (setq buffer-read-only t)) | |
200 | (pop-to-buffer erc-list-buffer)) | |
201 | t) | |
202 | ||
203 | ;; The main entry point. | |
204 | (defun erc-cmd-LIST (&optional line) | |
205 | "Show a listing of channels on the current server in a separate window. | |
206 | ||
207 | If LINE is specified, include it with the /LIST command. It | |
208 | should usually be one or more channels, separated by commas. | |
209 | ||
210 | Please note that this function only works with IRC servers which conform | |
211 | to RFC and send the LIST header (#321) at start of list transmission." | |
212 | (erc-with-server-buffer | |
d0fcaff5 SM |
213 | (set (make-local-variable 'erc-list-last-argument) line) |
214 | (erc-once-with-server-event | |
215 | 321 | |
216 | (let ((buf (current-buffer))) | |
217 | (lambda (_proc _parsed) | |
218 | (erc-list-install-322-handler buf))))) | |
5e56b3fb MO |
219 | (erc-server-send (concat "LIST :" (or (and line (substring line 1)) |
220 | "")))) | |
221 | (put 'erc-cmd-LIST 'do-not-parse-args t) | |
222 | ||
ed8be7ff GM |
223 | (provide 'erc-list) |
224 | ||
5e56b3fb MO |
225 | ;;; erc-list.el ends here |
226 | ;; | |
227 | ;; Local Variables: | |
228 | ;; indent-tabs-mode: t | |
229 | ;; tab-width: 8 | |
230 | ;; End: | |
f187f57d | 231 |