diff options
Diffstat (limited to 'lisp/erc')
-rw-r--r-- | lisp/erc/erc-autoaway.el | 4 | ||||
-rw-r--r-- | lisp/erc/erc-backend.el | 85 | ||||
-rw-r--r-- | lisp/erc/erc-capab.el | 16 | ||||
-rw-r--r-- | lisp/erc/erc-compat.el | 161 | ||||
-rw-r--r-- | lisp/erc/erc-dcc.el | 36 | ||||
-rw-r--r-- | lisp/erc/erc-ezbounce.el | 2 | ||||
-rw-r--r-- | lisp/erc/erc-fill.el | 2 | ||||
-rw-r--r-- | lisp/erc/erc-goodies.el | 30 | ||||
-rw-r--r-- | lisp/erc/erc-join.el | 26 | ||||
-rw-r--r-- | lisp/erc/erc-list.el | 28 | ||||
-rw-r--r-- | lisp/erc/erc-log.el | 4 | ||||
-rw-r--r-- | lisp/erc/erc-match.el | 92 | ||||
-rw-r--r-- | lisp/erc/erc-networks.el | 6 | ||||
-rw-r--r-- | lisp/erc/erc-notify.el | 2 | ||||
-rw-r--r-- | lisp/erc/erc-pcomplete.el | 1 | ||||
-rw-r--r-- | lisp/erc/erc-speedbar.el | 5 | ||||
-rw-r--r-- | lisp/erc/erc-stamp.el | 1 | ||||
-rw-r--r-- | lisp/erc/erc-status-sidebar.el | 304 | ||||
-rw-r--r-- | lisp/erc/erc-track.el | 12 | ||||
-rw-r--r-- | lisp/erc/erc.el | 260 |
20 files changed, 658 insertions, 419 deletions
diff --git a/lisp/erc/erc-autoaway.el b/lisp/erc/erc-autoaway.el index 0950cec4f7f..0923ed6e735 100644 --- a/lisp/erc/erc-autoaway.el +++ b/lisp/erc/erc-autoaway.el @@ -54,7 +54,7 @@ If `erc-autoaway-idle-method' is `emacs', you must call this function each time you change `erc-autoaway-idle-seconds'." (interactive) (when erc-autoaway-idletimer - (erc-cancel-timer erc-autoaway-idletimer)) + (cancel-timer erc-autoaway-idletimer)) (setq erc-autoaway-idletimer (run-with-idle-timer erc-autoaway-idle-seconds t @@ -133,7 +133,7 @@ Related variables: `erc-public-away-p' and `erc-away-nickname'." (remove-hook 'erc-after-connect 'erc-autoaway-insinuate-maybe) (remove-hook 'erc-disconnected-hook 'erc-autoaway-remove-maybe)) ((eq erc-autoaway-idle-method 'emacs) - (erc-cancel-timer erc-autoaway-idletimer) + (cancel-timer erc-autoaway-idletimer) (setq erc-autoaway-idletimer nil))) (remove-hook 'erc-timer-hook 'erc-autoaway-possibly-set-away) (remove-hook 'erc-server-305-functions 'erc-autoaway-reset-indicators)))) diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 526e854beca..1cf0bb49217 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -98,7 +98,6 @@ ;;; Code: -(require 'erc-compat) (eval-when-compile (require 'cl-lib)) ;; There's a fairly strong mutual dependency between erc.el and erc-backend.el. ;; Luckily, erc.el does not need erc-backend.el for macroexpansion whereas the @@ -375,7 +374,7 @@ Example: If you know that the channel #linux-ru uses the coding-system `cyrillic-koi8', then add (\"#linux-ru\" . cyrillic-koi8) to the alist." :group 'erc-server - :type '(repeat (cons (string :tag "Target") + :type '(repeat (cons (regexp :tag "Target") coding-system))) (defcustom erc-server-connect-function #'erc-open-network-stream @@ -520,7 +519,8 @@ If no subword-mode is active, then this is "Set up a timer to periodically ping the current server. The current buffer is given by BUFFER." (with-current-buffer buffer - (and erc-server-ping-handler (erc-cancel-timer erc-server-ping-handler)) + (when erc-server-ping-handler + (cancel-timer erc-server-ping-handler)) (when erc-server-send-ping-interval (setq erc-server-ping-handler (run-with-timer 4 erc-server-send-ping-interval @@ -533,7 +533,7 @@ The current buffer is given by BUFFER." (if timer-tuple ;; this buffer already has a timer. Cancel it and set the new one (progn - (erc-cancel-timer (cdr timer-tuple)) + (cancel-timer (cdr timer-tuple)) (setf (cdr (assq buffer erc-server-ping-timer-alist)) erc-server-ping-handler)) ;; no existing timer for this buffer. Add new one @@ -731,7 +731,7 @@ Conditionally try to reconnect and take appropriate action." (erc-with-all-buffers-of-server cproc nil (setq erc-server-connected nil)) (when erc-server-ping-handler - (progn (erc-cancel-timer erc-server-ping-handler) + (progn (cancel-timer erc-server-ping-handler) (setq erc-server-ping-handler nil))) (run-hook-with-args 'erc-disconnected-hook (erc-current-nick) (system-name) "") @@ -781,7 +781,7 @@ value of `erc-server-coding-system'." (pop precedence)) (when precedence (setq coding (car precedence))))) - (erc-decode-coding-string str coding))) + (decode-coding-string str coding t))) ;; proposed name, not used by anything yet (defun erc-send-line (text display-fn) @@ -856,7 +856,7 @@ Additionally, detect whether the IRC process has hung." ;; remove timer if the server buffer has been killed (let ((timer (assq buf erc-server-ping-timer-alist))) (when timer - (erc-cancel-timer (cdr timer)) + (cancel-timer (cdr timer)) (setcdr timer nil))))) ;; From Circe @@ -864,41 +864,42 @@ Additionally, detect whether the IRC process has hung." "Send messages in `erc-server-flood-queue'. See `erc-server-flood-margin' for an explanation of the flood protection algorithm." - (with-current-buffer buffer - (let ((now (current-time))) - (when erc-server-flood-timer - (erc-cancel-timer erc-server-flood-timer) - (setq erc-server-flood-timer nil)) - (when (time-less-p erc-server-flood-last-message now) - (setq erc-server-flood-last-message (erc-emacs-time-to-erc-time now))) - (while (and erc-server-flood-queue - (time-less-p erc-server-flood-last-message - (time-add now erc-server-flood-margin))) - (let ((msg (caar erc-server-flood-queue)) - (encoding (cdar erc-server-flood-queue))) - (setq erc-server-flood-queue (cdr erc-server-flood-queue) - erc-server-flood-last-message - (+ erc-server-flood-last-message - erc-server-flood-penalty)) - (erc-log-irc-protocol msg 'outbound) - (erc-log (concat "erc-server-send-queue: " - msg "(" (buffer-name buffer) ")")) - (when (erc-server-process-alive) - (condition-case nil - ;; Set encoding just before sending the string - (progn - (when (fboundp 'set-process-coding-system) - (set-process-coding-system erc-server-process - 'raw-text encoding)) - (process-send-string erc-server-process msg)) - ;; Sometimes the send can occur while the process is - ;; being killed, which results in a weird SIGPIPE error. - ;; Catch this and ignore it. - (error nil))))) - (when erc-server-flood-queue - (setq erc-server-flood-timer - (run-at-time (+ 0.2 erc-server-flood-penalty) - nil #'erc-server-send-queue buffer)))))) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (let ((now (current-time))) + (when erc-server-flood-timer + (cancel-timer erc-server-flood-timer) + (setq erc-server-flood-timer nil)) + (when (time-less-p erc-server-flood-last-message now) + (setq erc-server-flood-last-message (erc-emacs-time-to-erc-time now))) + (while (and erc-server-flood-queue + (time-less-p erc-server-flood-last-message + (time-add now erc-server-flood-margin))) + (let ((msg (caar erc-server-flood-queue)) + (encoding (cdar erc-server-flood-queue))) + (setq erc-server-flood-queue (cdr erc-server-flood-queue) + erc-server-flood-last-message + (+ erc-server-flood-last-message + erc-server-flood-penalty)) + (erc-log-irc-protocol msg 'outbound) + (erc-log (concat "erc-server-send-queue: " + msg "(" (buffer-name buffer) ")")) + (when (erc-server-process-alive) + (condition-case nil + ;; Set encoding just before sending the string + (progn + (when (fboundp 'set-process-coding-system) + (set-process-coding-system erc-server-process + 'raw-text encoding)) + (process-send-string erc-server-process msg)) + ;; Sometimes the send can occur while the process is + ;; being killed, which results in a weird SIGPIPE error. + ;; Catch this and ignore it. + (error nil))))) + (when erc-server-flood-queue + (setq erc-server-flood-timer + (run-at-time (+ 0.2 erc-server-flood-penalty) + nil #'erc-server-send-queue buffer))))))) (defun erc-message (message-command line &optional force) "Send LINE to the server as a privmsg or a notice. diff --git a/lisp/erc/erc-capab.el b/lisp/erc/erc-capab.el index fc45725f789..4afe6a7614b 100644 --- a/lisp/erc/erc-capab.el +++ b/lisp/erc/erc-capab.el @@ -170,11 +170,11 @@ PARSED is an `erc-parsed' response struct." (string-match "^\\([-\\+]\\)\\(.+\\)$" msg)) (setf (erc-response.contents parsed) (if erc-capab-identify-mode - (erc-propertize (match-string 2 msg) - 'erc-identified - (if (string= (match-string 1 msg) "+") - 1 - 0)) + (propertize (match-string 2 msg) + 'erc-identified + (if (string= (match-string 1 msg) "+") + 1 + 0)) (match-string 2 msg))) nil))) @@ -190,9 +190,9 @@ PARSED is an `erc-parsed' response struct." ;; assuming the first use of `nickname' is the sender's nick (re-search-forward (regexp-quote nickname) nil t)) (goto-char (match-beginning 0)) - (insert (erc-propertize erc-capab-identify-prefix - 'font-lock-face - 'erc-capab-identify-unidentified)))))) + (insert (propertize erc-capab-identify-prefix + 'font-lock-face + 'erc-capab-identify-unidentified)))))) (defun erc-capab-identify-get-unidentified-nickname (parsed) "Return the nickname of the user if unidentified. diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el deleted file mode 100644 index c77d5abf2e4..00000000000 --- a/lisp/erc/erc-compat.el +++ /dev/null @@ -1,161 +0,0 @@ -;;; erc-compat.el --- ERC compatibility code for XEmacs - -;; Copyright (C) 2002-2003, 2005-2020 Free Software Foundation, Inc. - -;; Author: Alex Schroeder <alex@gnu.org> -;; Maintainer: Amin Bandali <bandali@gnu.org> -;; URL: https://www.emacswiki.org/emacs/ERC - -;; 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 3 of the License, 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. If not, see <https://www.gnu.org/licenses/>. - -;;; Commentary: - -;; This mostly defines stuff that cannot be worked around easily. - -;;; Code: - -(require 'format-spec) - -;;;###autoload(autoload 'erc-define-minor-mode "erc-compat") -(defalias 'erc-define-minor-mode 'define-minor-mode) -(put 'erc-define-minor-mode 'edebug-form-spec 'define-minor-mode) - -(defun erc-decode-coding-string (s coding-system) - "Decode S using CODING-SYSTEM." - (decode-coding-string s coding-system t)) - -(defun erc-encode-coding-string (s coding-system) - "Encode S using CODING-SYSTEM. -Return the same string, if the encoding operation is trivial. -See `erc-encoding-coding-alist'." - (encode-coding-string s coding-system t)) - -(defalias 'erc-propertize 'propertize) -(defalias 'erc-view-mode-enter 'view-mode-enter) -(autoload 'help-function-arglist "help-fns") -(defalias 'erc-function-arglist 'help-function-arglist) -(defalias 'erc-delete-dups 'delete-dups) -(defalias 'erc-replace-regexp-in-string 'replace-regexp-in-string) - -(defun erc-set-write-file-functions (new-val) - (set (make-local-variable 'write-file-functions) new-val)) - -(defvar erc-emacs-build-time - (if (or (stringp emacs-build-time) (not emacs-build-time)) - emacs-build-time - (format-time-string "%Y-%m-%d" emacs-build-time)) - "Time at which Emacs was dumped out, or nil if not available.") - -;; Emacs 21 and XEmacs do not have user-emacs-directory, but XEmacs -;; has user-init-directory. -(defvar erc-user-emacs-directory - (cond ((boundp 'user-emacs-directory) - user-emacs-directory) - ((boundp 'user-init-directory) - user-init-directory) - (t "~/.emacs.d/")) - "Directory beneath which additional per-user Emacs-specific files -are placed. -Note that this should end with a directory separator.") - -(defun erc-replace-match-subexpression-in-string - (newtext string match subexp start &optional fixedcase literal) - "Replace the subexpression SUBEXP of the last match in STRING with NEWTEXT. -MATCH is the text which matched the subexpression (see `match-string'). -START is the beginning position of the last match (see `match-beginning'). -See `replace-match' for explanations of FIXEDCASE and LITERAL." - (replace-match newtext fixedcase literal string subexp)) - -(defalias 'erc-with-selected-window 'with-selected-window) -(defalias 'erc-cancel-timer 'cancel-timer) -(defalias 'erc-make-obsolete 'make-obsolete) -(defalias 'erc-make-obsolete-variable 'make-obsolete-variable) - -;; Provide a simpler replacement for `member-if' -(defun erc-member-if (predicate list) - "Find the first item satisfying PREDICATE in LIST. -Return the sublist of LIST whose car matches." - (let ((ptr list)) - (catch 'found - (while ptr - (when (funcall predicate (car ptr)) - (throw 'found ptr)) - (setq ptr (cdr ptr)))))) - -;; Provide a simpler replacement for `delete-if' -(defun erc-delete-if (predicate seq) - "Remove all items satisfying PREDICATE in SEQ. -This is a destructive function: it reuses the storage of SEQ -whenever possible." - ;; remove from car - (while (when (funcall predicate (car seq)) - (setq seq (cdr seq)))) - ;; remove from cdr - (let ((ptr seq) - (next (cdr seq))) - (while next - (when (funcall predicate (car next)) - (setcdr ptr (if (consp next) - (cdr next) - nil))) - (setq ptr (cdr ptr)) - (setq next (cdr ptr)))) - seq) - -;; Provide a simpler replacement for `remove-if-not' -(defun erc-remove-if-not (predicate seq) - "Remove all items not satisfying PREDICATE in SEQ. -This is a non-destructive function; it makes a copy of SEQ to -avoid corrupting the original SEQ." - (let (newseq) - (dolist (el seq) - (when (funcall predicate el) - (setq newseq (cons el newseq)))) - (nreverse newseq))) - -;; Copied from cl-extra.el -(defun erc-subseq (seq start &optional end) - "Return the subsequence of SEQ from START to END. -If END is omitted, it defaults to the length of the sequence. -If START or END is negative, it counts from the end." - (if (stringp seq) (substring seq start end) - (let (len) - (and end (< end 0) (setq end (+ end (setq len (length seq))))) - (if (< start 0) (setq start (+ start (or len (setq len (length seq)))))) - (cond ((listp seq) - (if (> start 0) (setq seq (nthcdr start seq))) - (if end - (let ((res nil)) - (while (>= (setq end (1- end)) start) - (push (pop seq) res)) - (nreverse res)) - (copy-sequence seq))) - (t - (or end (setq end (or len (length seq)))) - (let ((res (make-vector (max (- end start) 0) nil)) - (i 0)) - (while (< start end) - (aset res i (aref seq start)) - (setq i (1+ i) start (1+ start))) - res)))))) - -(provide 'erc-compat) - -;;; erc-compat.el ends here -;; -;; Local Variables: -;; generated-autoload-file: "erc-loaddefs.el" -;; End: diff --git a/lisp/erc/erc-dcc.el b/lisp/erc/erc-dcc.el index 26701cec1e4..1bce986a806 100644 --- a/lisp/erc/erc-dcc.el +++ b/lisp/erc/erc-dcc.el @@ -419,15 +419,15 @@ where FOO is one of CLOSE, GET, SEND, LIST, CHAT, etc." (pcomplete-here (pcase (intern (downcase (pcomplete-arg 1))) ('chat (mapcar (lambda (elt) (plist-get elt :nick)) - (erc-remove-if-not + (cl-remove-if-not #'(lambda (elt) (eq (plist-get elt :type) 'CHAT)) erc-dcc-list))) - ('close (erc-delete-dups + ('close (delete-dups (mapcar (lambda (elt) (symbol-name (plist-get elt :type))) erc-dcc-list))) ('get (mapcar #'erc-dcc-nick - (erc-remove-if-not + (cl-remove-if-not #'(lambda (elt) (eq (plist-get elt :type) 'GET)) erc-dcc-list))) @@ -435,7 +435,7 @@ where FOO is one of CLOSE, GET, SEND, LIST, CHAT, etc." (pcomplete-here (pcase (intern (downcase (pcomplete-arg 2))) ('get (mapcar (lambda (elt) (plist-get elt :file)) - (erc-remove-if-not + (cl-remove-if-not #'(lambda (elt) (and (eq (plist-get elt :type) 'GET) (erc-nick-equal-p (erc-extract-nick @@ -443,7 +443,7 @@ where FOO is one of CLOSE, GET, SEND, LIST, CHAT, etc." (pcomplete-arg 1)))) erc-dcc-list))) ('close (mapcar #'erc-dcc-nick - (erc-remove-if-not + (cl-remove-if-not #'(lambda (elt) (eq (plist-get elt :type) (intern (upcase (pcomplete-arg 1))))) @@ -516,8 +516,8 @@ PROC is the server process." (filename (or file (plist-get elt :file) "unknown"))) (if elt (let* ((file (read-file-name - (format "Local filename (default %s): " - (file-name-nondirectory filename)) + (format-prompt "Local filename" + (file-name-nondirectory filename)) (or erc-dcc-get-default-directory default-directory) (expand-file-name (file-name-nondirectory filename) @@ -627,17 +627,17 @@ that subcommand." ?q query ?n nick ?u login ?h host)))) (defconst erc-dcc-ctcp-query-send-regexp - (concat "^DCC SEND \\(" + (concat "^DCC SEND \\(?:" ;; Following part matches either filename without spaces ;; or filename enclosed in double quotes with any number ;; of escaped double quotes inside. - "\"\\(\\(.*?\\(\\\\\"\\)?\\)+?\\)\"\\|\\([^ ]+\\)" + "\"\\(\\(?:\\\\\"\\|[^\"\\]\\)+\\)\"\\|\\([^ ]+\\)" "\\) \\([0-9]+\\) \\([0-9]+\\) *\\([0-9]*\\)")) (define-inline erc-dcc-unquote-filename (filename) (inline-quote - (erc-replace-regexp-in-string "\\\\\\\\" "\\" - (erc-replace-regexp-in-string "\\\\\"" "\"" ,filename t t) t t))) + (replace-regexp-in-string "\\\\\\\\" "\\" + (replace-regexp-in-string "\\\\\"" "\"" ,filename t t) t t))) (defun erc-dcc-handle-ctcp-send (proc query nick login host to) "This is called if a CTCP DCC SEND subcommand is sent to the client. @@ -653,11 +653,11 @@ It extracts the information about the dcc request and adds it to ?r "SEND" ?n nick ?u login ?h host)) ((string-match erc-dcc-ctcp-query-send-regexp query) (let ((filename - (or (match-string 5 query) - (erc-dcc-unquote-filename (match-string 2 query)))) - (ip (erc-decimal-to-ip (match-string 6 query))) - (port (match-string 7 query)) - (size (match-string 8 query))) + (or (match-string 2 query) + (erc-dcc-unquote-filename (match-string 1 query)))) + (ip (erc-decimal-to-ip (match-string 3 query))) + (port (match-string 4 query)) + (size (match-string 5 query))) ;; FIXME: a warning really should also be sent ;; if the ip address != the host the dcc sender is on. (erc-display-message @@ -1193,8 +1193,8 @@ other client." (setq posn (match-end 0)) (erc-display-message nil nil proc - 'dcc-chat-privmsg ?n (erc-propertize erc-dcc-from 'font-lock-face - 'erc-nick-default-face) ?m line)) + 'dcc-chat-privmsg ?n (propertize erc-dcc-from 'font-lock-face + 'erc-nick-default-face) ?m line)) (setq erc-dcc-unprocessed-output (substring str posn))))) (defun erc-dcc-chat-buffer-killed () diff --git a/lisp/erc/erc-ezbounce.el b/lisp/erc/erc-ezbounce.el index 1032af7a304..5c2faff96de 100644 --- a/lisp/erc/erc-ezbounce.el +++ b/lisp/erc/erc-ezbounce.el @@ -34,7 +34,7 @@ (defcustom erc-ezb-regexp "^ezbounce!srv$" "Regexp used by the EZBouncer to identify itself to the user." :group 'erc-ezbounce - :type 'string) + :type 'regexp) (defcustom erc-ezb-login-alist '() "Alist of logins suitable for the server we're connecting to. diff --git a/lisp/erc/erc-fill.el b/lisp/erc/erc-fill.el index 39a8be5e0cf..d09caf7aa12 100644 --- a/lisp/erc/erc-fill.el +++ b/lisp/erc/erc-fill.el @@ -38,7 +38,7 @@ :group 'erc) ;;;###autoload(autoload 'erc-fill-mode "erc-fill" nil t) -(erc-define-minor-mode erc-fill-mode +(define-minor-mode erc-fill-mode "Toggle ERC fill mode. With a prefix argument ARG, enable ERC fill mode if ARG is positive, and disable it otherwise. If called from Lisp, enable diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index 94d5de280c6..a475f0a1770 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -232,6 +232,10 @@ The value `erc-interpret-controls-p' must also be t for this to work." "ERC bold face." :group 'erc-faces) +(defface erc-italic-face '((t :slant italic)) + "ERC italic face." + :group 'erc-faces) + (defface erc-inverse-face '((t :foreground "White" :background "Black")) "ERC inverse face." @@ -383,6 +387,7 @@ See `erc-interpret-controls-p' and `erc-interpret-mirc-color' for options." (erc-controls-strip s)) (erc-interpret-controls-p (let ((boldp nil) + (italicp nil) (inversep nil) (underlinep nil) (fg nil) @@ -394,13 +399,14 @@ See `erc-interpret-controls-p' and `erc-interpret-mirc-color' for options." (start (match-beginning 0)) (end (+ (match-beginning 0) (length (match-string 5 s))))) - (setq s (erc-replace-match-subexpression-in-string - "" s control 1 start)) + (setq s (replace-match "" nil nil s 1)) (cond ((and erc-interpret-mirc-color (or fg-color bg-color)) (setq fg fg-color) (setq bg bg-color)) ((string= control "\C-b") (setq boldp (not boldp))) + ((string= control "\C-]") + (setq italicp (not italicp))) ((string= control "\C-v") (setq inversep (not inversep))) ((string= control "\C-_") @@ -413,13 +419,14 @@ See `erc-interpret-controls-p' and `erc-interpret-mirc-color' for options." (ding))) ((string= control "\C-o") (setq boldp nil + italicp nil inversep nil underlinep nil fg nil bg nil)) (t nil)) (erc-controls-propertize - start end boldp inversep underlinep fg bg s))) + start end boldp italicp inversep underlinep fg bg s))) s)) (t s))))) @@ -432,13 +439,13 @@ See `erc-interpret-controls-p' and `erc-interpret-mirc-color' for options." s))) (defvar erc-controls-remove-regexp - "\C-b\\|\C-_\\|\C-v\\|\C-g\\|\C-o\\|\C-c[0-9]?[0-9]?\\(,[0-9][0-9]?\\)?" + "\C-b\\|\C-]\\|\C-_\\|\C-v\\|\C-g\\|\C-o\\|\C-c[0-9]?[0-9]?\\(,[0-9][0-9]?\\)?" "Regular expression which matches control characters to remove.") (defvar erc-controls-highlight-regexp - (concat "\\(\C-b\\|\C-v\\|\C-_\\|\C-g\\|\C-o\\|" + (concat "\\(\C-b\\|\C-]\\|\C-v\\|\C-_\\|\C-g\\|\C-o\\|" "\C-c\\([0-9][0-9]?\\)?\\(,\\([0-9][0-9]?\\)\\)?\\)" - "\\([^\C-b\C-v\C-_\C-c\C-g\C-o\n]*\\)") + "\\([^\C-b\C-]\C-v\C-_\C-c\C-g\C-o\n]*\\)") "Regular expression which matches control chars and the text to highlight.") (defun erc-controls-highlight () @@ -451,6 +458,7 @@ Also see `erc-interpret-controls-p' and `erc-interpret-mirc-color'." (replace-match ""))) (erc-interpret-controls-p (let ((boldp nil) + (italicp nil) (inversep nil) (underlinep nil) (fg nil) @@ -467,6 +475,8 @@ Also see `erc-interpret-controls-p' and `erc-interpret-mirc-color'." (setq bg bg-color)) ((string= control "\C-b") (setq boldp (not boldp))) + ((string= control "\C-]") + (setq italicp (not italicp))) ((string= control "\C-v") (setq inversep (not inversep))) ((string= control "\C-_") @@ -479,16 +489,17 @@ Also see `erc-interpret-controls-p' and `erc-interpret-mirc-color'." (ding))) ((string= control "\C-o") (setq boldp nil + italicp nil inversep nil underlinep nil fg nil bg nil)) (t nil)) (erc-controls-propertize start end - boldp inversep underlinep fg bg))))) + boldp italicp inversep underlinep fg bg))))) (t nil))) -(defun erc-controls-propertize (from to boldp inversep underlinep fg bg +(defun erc-controls-propertize (from to boldp italicp inversep underlinep fg bg &optional str) "Prepend properties from IRC control characters between FROM and TO. If optional argument STR is provided, apply to STR, otherwise prepend properties @@ -500,6 +511,9 @@ to a region in the current buffer." (append (if boldp '(erc-bold-face) nil) + (if italicp + '(erc-italic-face) + nil) (if inversep '(erc-inverse-face) nil) diff --git a/lisp/erc/erc-join.el b/lisp/erc/erc-join.el index 280d6bfe0f1..79c111082f6 100644 --- a/lisp/erc/erc-join.el +++ b/lisp/erc/erc-join.el @@ -113,7 +113,7 @@ servers, presumably in the same domain." This is called from a timer set up by `erc-autojoin-channels'." (if erc--autojoin-timer (setq erc--autojoin-timer - (erc-cancel-timer erc--autojoin-timer))) + (cancel-timer erc--autojoin-timer))) (with-current-buffer buffer ;; Don't kick of another delayed autojoin or try to wait for ;; another ident response: @@ -127,7 +127,7 @@ This is called from a timer set up by `erc-autojoin-channels'." This function is run from `erc-nickserv-identified-hook'." (if erc--autojoin-timer (setq erc--autojoin-timer - (erc-cancel-timer erc--autojoin-timer))) + (cancel-timer erc--autojoin-timer))) (when (eq erc-autojoin-timing 'ident) (let ((server (or erc-session-server erc-server-announced-name)) (joined (mapcar (lambda (buf) @@ -153,18 +153,20 @@ This function is run from `erc-nickserv-identified-hook'." 'erc-autojoin-channels-delayed server nick (current-buffer)))) ;; `erc-autojoin-timing' is `connect': - (dolist (l erc-autojoin-channels-alist) - (when (string-match (car l) server) - (let ((server (or erc-session-server erc-server-announced-name))) + (let ((server (or erc-session-server erc-server-announced-name))) + (dolist (l erc-autojoin-channels-alist) + (when (string-match-p (car l) server) (dolist (chan (cdr l)) - (let ((buffer (erc-get-buffer chan))) - ;; Only auto-join the channels that we aren't already in - ;; using a different nick. + (let ((buffer + (car (erc-buffer-filter + (lambda () + (let ((current (erc-default-target))) + (and (stringp current) + (string-match-p (car l) + (or erc-session-server erc-server-announced-name)) + (string-equal (erc-downcase chan) + (erc-downcase current))))))))) (when (or (not buffer) - ;; If the same channel is joined on another - ;; server the best-effort is to just join - (not (string-match (car l) - (process-name erc-server-process))) (not (with-current-buffer buffer (erc-server-process-alive)))) (erc-server-join-channel server chan)))))))) diff --git a/lisp/erc/erc-list.el b/lisp/erc/erc-list.el index 5faeabb721a..036d7733ed7 100644 --- a/lisp/erc/erc-list.el +++ b/lisp/erc/erc-list.el @@ -71,13 +71,13 @@ (defun erc-list-make-string (channel users topic) (concat channel - (erc-propertize " " - 'display (list 'space :align-to erc-list-nusers-column) - 'face 'fixed-pitch) + (propertize " " + 'display (list 'space :align-to erc-list-nusers-column) + 'face 'fixed-pitch) users - (erc-propertize " " - 'display (list 'space :align-to erc-list-topic-column) - 'face 'fixed-pitch) + (propertize " " + 'display (list 'space :align-to erc-list-topic-column) + 'face 'fixed-pitch) topic)) ;; Insert a record into the list buffer. @@ -143,19 +143,19 @@ ;; Helper function that makes a buttonized column header. (defun erc-list-button (title column) - (erc-propertize title - 'column-number column - 'help-echo "mouse-1: sort by column" - 'mouse-face 'header-line-highlight - 'keymap erc-list-menu-sort-button-map)) + (propertize title + 'column-number column + 'help-echo "mouse-1: sort by column" + 'mouse-face 'header-line-highlight + 'keymap erc-list-menu-sort-button-map)) (define-derived-mode erc-list-menu-mode special-mode "ERC-List" "Major mode for editing a list of irc channels." (setq header-line-format (concat - (erc-propertize " " - 'display '(space :align-to 0) - 'face 'fixed-pitch) + (propertize " " + 'display '(space :align-to 0) + 'face 'fixed-pitch) (erc-list-make-string (erc-list-button "Channel" 1) (erc-list-button "# Users" 2) "Topic"))) diff --git a/lisp/erc/erc-log.el b/lisp/erc/erc-log.el index 1bad6d16c87..2166123e674 100644 --- a/lisp/erc/erc-log.el +++ b/lisp/erc/erc-log.el @@ -267,7 +267,7 @@ The current buffer is given by BUFFER." (with-current-buffer buffer (auto-save-mode -1) (setq buffer-file-name nil) - (erc-set-write-file-functions '(erc-save-buffer-in-logs)) + (set (make-local-variable 'write-file-functions) '(erc-save-buffer-in-logs)) (when erc-log-insert-log-on-open (ignore-errors (save-excursion @@ -334,7 +334,7 @@ This will not work with full paths, only names. Any unsafe characters in the name are replaced with \"!\". The filename is downcased." - (downcase (erc-replace-regexp-in-string + (downcase (replace-regexp-in-string "[/\\]" "!" (convert-standard-filename filename)))) (defun erc-current-logfile (&optional buffer) diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el index 3107ff2ccd1..b3145674f29 100644 --- a/lisp/erc/erc-match.el +++ b/lisp/erc/erc-match.el @@ -94,7 +94,9 @@ The following values are allowed: `nick-or-keyword' - highlight the nick of the user who typed your nickname, or all instances of the current nickname if there was no sending user - `all' - highlight the entire message where current nickname occurs + `message' - highlight the entire message where current nickname occurs + `all' - highlight the entire message (including the nick) where + current nickname occurs Any other value disables highlighting of current nickname altogether." :group 'erc-match @@ -102,6 +104,7 @@ Any other value disables highlighting of current nickname altogether." (const nick) (const keyword) (const nick-or-keyword) + (const message) (const all))) (defcustom erc-pal-highlight-type 'nick @@ -110,14 +113,17 @@ See `erc-pals'. The following values are allowed: - nil - do not highlight the message at all - `nick' - highlight pal's nickname only - `all' - highlight the entire message from pal + nil - do not highlight the message at all + `nick' - highlight pal's nickname only + `message' - highlight the entire message from pal + `all' - highlight the entire message (including the nick) + from pal Any other value disables pal highlighting altogether." :group 'erc-match :type '(choice (const nil) (const nick) + (const message) (const all))) (defcustom erc-fool-highlight-type 'nick @@ -126,14 +132,17 @@ See `erc-fools'. The following values are allowed: - nil - do not highlight the message at all - `nick' - highlight fool's nickname only - `all' - highlight the entire message from fool + nil - do not highlight the message at all + `nick' - highlight fool's nickname only + `message' - highlight the entire message from fool + `all' - highlight the entire message (including the nick) + from fool Any other value disables fool highlighting altogether." :group 'erc-match :type '(choice (const nil) (const nick) + (const message) (const all))) (defcustom erc-keyword-highlight-type 'keyword @@ -143,12 +152,15 @@ See variable `erc-keywords'. The following values are allowed: `keyword' - highlight keyword only - `all' - highlight the entire message containing keyword + `message' - highlight the entire message containing keyword + `all' - highlight the entire message (including the nick) + containing keyword Any other value disables keyword highlighting altogether." :group 'erc-match :type '(choice (const nil) (const keyword) + (const message) (const all))) (defcustom erc-dangerous-host-highlight-type 'nick @@ -157,13 +169,16 @@ See `erc-dangerous-hosts'. The following values are allowed: - `nick' - highlight nick from dangerous-host only - `all' - highlight the entire message from dangerous-host + `nick' - highlight nick from dangerous-host only + `message' - highlight the entire message from dangerous-host + `all' - highlight the entire message (including the nick) + from dangerous-host Any other value disables dangerous-host highlighting altogether." :group 'erc-match :type '(choice (const nil) (const nick) + (const message) (const all))) @@ -449,19 +464,18 @@ Use this defun with `erc-insert-modify-hook'." (match-beginning 0))) (nick-end (when nick-beg (match-end 0))) - (message (buffer-substring - (if (and nick-end - (<= (+ 2 nick-end) (point-max))) - ;; Message starts 2 characters after the nick - ;; except for CTCP ACTION messages. Nick - ;; surrounded by angle brackets only in normal - ;; messages. - (+ nick-end - (if (eq ?> (char-after nick-end)) - 2 - 1)) - (point-min)) - (point-max)))) + (message-beg (if (and nick-end + (<= (+ 2 nick-end) (point-max))) + ;; Message starts 2 characters after the + ;; nick except for CTCP ACTION messages. + ;; Nick surrounded by angle brackets only in + ;; normal messages. + (+ nick-end + (if (eq ?> (char-after nick-end)) + 2 + 1)) + (point-min))) + (message (buffer-substring message-beg (point-max)))) (when (and vector (not (and erc-match-exclude-server-buffer (erc-server-buffer-p)))) @@ -498,7 +512,12 @@ Use this defun with `erc-insert-modify-hook'." (while (re-search-forward match-regex nil t) (erc-put-text-property (match-beginning 0) (match-end 0) 'font-lock-face match-face)))) - ;; Highlight the whole message + ;; Highlight the whole message (not including the nick) + ((eq match-htype 'message) + (erc-put-text-property + message-beg (point-max) + 'font-lock-face match-face (current-buffer))) + ;; Highlight the whole message (including the nick) ((eq match-htype 'all) (erc-put-text-property (point-min) (point-max) @@ -555,16 +574,15 @@ See `erc-log-match-format'." (and (eq erc-log-matches-flag 'away) (erc-away-time))) match-buffer-name) - (let ((line (format-spec erc-log-match-format - (format-spec-make - ?n nick - ?t (format-time-string - (or (and (boundp 'erc-timestamp-format) - erc-timestamp-format) - "[%Y-%m-%d %H:%M] ")) - ?c (or (erc-default-target) "") - ?m message - ?u nickuserhost)))) + (let ((line (format-spec + erc-log-match-format + `((?n . ,nick) + (?t . ,(format-time-string + (or (bound-and-true-p erc-timestamp-format) + "[%Y-%m-%d %H:%M] "))) + (?c . ,(or (erc-default-target) "")) + (?m . ,message) + (?u . ,nickuserhost))))) (with-current-buffer (erc-log-matches-make-buffer match-buffer-name) (let ((inhibit-read-only t)) (goto-char (point-max)) @@ -578,9 +596,9 @@ See `erc-log-match-format'." (with-current-buffer buffer (unless buffer-already (insert " == Type \"q\" to dismiss messages ==\n") - (erc-view-mode-enter nil (lambda (buffer) - (when (y-or-n-p "Discard messages? ") - (kill-buffer buffer))))) + (view-mode-enter nil (lambda (buffer) + (when (y-or-n-p "Discard messages? ") + (kill-buffer buffer))))) buffer))) (defun erc-log-matches-come-back (proc parsed) diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el index 1234962c51c..8551cdd1dee 100644 --- a/lisp/erc/erc-networks.el +++ b/lisp/erc/erc-networks.el @@ -756,8 +756,8 @@ Return the name of this server's network as a symbol." (erc-with-server-buffer (intern (downcase (symbol-name erc-network))))) -(erc-make-obsolete 'erc-current-network 'erc-network - "Obsolete since erc-networks 1.5") +(make-obsolete 'erc-current-network 'erc-network + "Obsolete since erc-networks 1.5") (defun erc-network-name () "Return the name of the current network as a string." @@ -812,7 +812,7 @@ As an example: (let* ((completion-ignore-case t) (net (intern (completing-read "Network: " - (erc-delete-dups + (delete-dups (mapcar (lambda (x) (list (symbol-name (nth 1 x)))) erc-server-alist))))) diff --git a/lisp/erc/erc-notify.el b/lisp/erc/erc-notify.el index 1b092c8a6a9..144a981f832 100644 --- a/lisp/erc/erc-notify.el +++ b/lisp/erc/erc-notify.el @@ -181,7 +181,7 @@ nick from `erc-last-ison' to prevent any further notifications." (let ((nick (erc-extract-nick (erc-response.sender parsed)))) (when (and (erc-member-ignore-case nick erc-notify-list) (erc-member-ignore-case nick erc-last-ison)) - (setq erc-last-ison (erc-delete-if + (setq erc-last-ison (cl-delete-if (let ((nick-down (erc-downcase nick))) (lambda (el) (string= nick-down (erc-downcase el)))) diff --git a/lisp/erc/erc-pcomplete.el b/lisp/erc/erc-pcomplete.el index 7643fa85b96..f8b7e13be02 100644 --- a/lisp/erc/erc-pcomplete.el +++ b/lisp/erc/erc-pcomplete.el @@ -41,7 +41,6 @@ (require 'pcomplete) (require 'erc) -(require 'erc-compat) (require 'time-date) (defgroup erc-pcomplete nil diff --git a/lisp/erc/erc-speedbar.el b/lisp/erc/erc-speedbar.el index 5a469aa4e4e..b64e42b7ee4 100644 --- a/lisp/erc/erc-speedbar.el +++ b/lisp/erc/erc-speedbar.el @@ -90,9 +90,8 @@ nil - Do not sort users" "Additional menu-items to add to speedbar frame.") ;; Make sure our special speedbar major mode is loaded -(if (featurep 'speedbar) - (erc-install-speedbar-variables) - (add-hook 'speedbar-load-hook 'erc-install-speedbar-variables)) +(with-eval-after-load 'speedbar + (erc-install-speedbar-variables)) ;;; ERC hierarchy display method ;;;###autoload diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el index cbab2f9da2b..08970f2d70e 100644 --- a/lisp/erc/erc-stamp.el +++ b/lisp/erc/erc-stamp.el @@ -35,7 +35,6 @@ ;;; Code: (require 'erc) -(require 'erc-compat) (defgroup erc-stamp nil "For long conversation on IRC it is sometimes quite diff --git a/lisp/erc/erc-status-sidebar.el b/lisp/erc/erc-status-sidebar.el new file mode 100644 index 00000000000..033c7d600f9 --- /dev/null +++ b/lisp/erc/erc-status-sidebar.el @@ -0,0 +1,304 @@ +;;; erc-status-sidebar.el --- HexChat-like activity overview for ERC + +;; Copyright (C) 2017, 2020 Free Software Foundation, Inc. + +;; Author: Andrew Barbarello +;; Maintainer: Amin Bandali <bandali@gnu.org> +;; URL: https://github.com/drewbarbs/erc-status-sidebar + +;; 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 3 of the License, 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. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This package provides a HexChat-like sidebar for joined channels in +;; ERC. It relies on the `erc-track' module, and displays all of the +;; same information that `erc-track' does in the mode line, but in an +;; alternative format in form of a sidebar. + +;; Shout out to sidebar.el <https://github.com/sebastiencs/sidebar.el> +;; and outline-toc.el <https://github.com/abingham/outline-toc.el> for +;; the sidebar window management ideas. + +;; Usage: + +;; Use M-x erc-status-sidebar-open RET to open the ERC status sidebar +;; in the current frame. Make sure that the `erc-track' module is +;; active (this is the default). + +;; Use M-x erc-status-sidebar-close RET to close the sidebar on the +;; current frame. With a prefix argument, it closes the sidebar on +;; all frames. + +;; Use M-x erc-status-sidebar-kill RET to kill the sidebar buffer and +;; close the sidebar on all frames. + +;;; Code: + +(require 'erc) +(require 'erc-track) +(require 'fringe) +(require 'seq) + +(defgroup erc-status-sidebar nil + "A sidebar for ERC channel status." + :group 'convenience) + +(defcustom erc-status-sidebar-buffer-name "*ERC Status*" + "Name of the sidebar buffer." + :type 'string + :group 'erc-status-sidebar) + +(defcustom erc-status-sidebar-mode-line-format "ERC Status" + "Mode line format for the status sidebar." + :type 'string + :group 'erc-status-sidebar) + +(defcustom erc-status-sidebar-header-line-format nil + "Header line format for the status sidebar." + :type 'string + :group 'erc-status-sidebar) + +(defcustom erc-status-sidebar-width 15 + "Default width of the sidebar (in columns)." + :type 'number + :group 'erc-status-sidebar) + +(defcustom erc-status-sidebar-channel-sort + 'erc-status-sidebar-default-chansort + "Sorting function used to determine order of channels in the sidebar." + :type 'function + :group 'erc-status-sidebar) + +(defcustom erc-status-sidebar-channel-format + 'erc-status-sidebar-default-chan-format + "Function used to format channel names for display in the sidebar." + :type 'function + :group 'erc-status-sidebar) + +(defun erc-status-sidebar-display-window () + "Display the status buffer in a side window. Return the new window." + (display-buffer + (erc-status-sidebar-get-buffer) + `(display-buffer-in-side-window . ((side . left) + (window-width . ,erc-status-sidebar-width))))) + +(defun erc-status-sidebar-get-window (&optional no-creation) + "Return the created/existing window displaying the status buffer. + +If NO-CREATION is non-nil, the window is not created." + (let ((sidebar-window (get-buffer-window erc-status-sidebar-buffer-name))) + (unless (or sidebar-window no-creation) + (with-current-buffer (erc-status-sidebar-get-buffer) + (setq-local vertical-scroll-bar nil)) + (setq sidebar-window (erc-status-sidebar-display-window)) + (set-window-dedicated-p sidebar-window t) + (set-window-parameter sidebar-window 'no-delete-other-windows t) + ;; Don't cycle to this window with `other-window'. + (set-window-parameter sidebar-window 'no-other-window t) + (internal-show-cursor sidebar-window nil) + (set-window-fringes sidebar-window 0 0) + ;; Set a custom display table so the window doesn't show a + ;; truncation symbol when a channel name is too big. + (let ((dt (make-display-table))) + (set-window-display-table sidebar-window dt) + (set-display-table-slot dt 'truncation ?\ ))) + sidebar-window)) + +(defun erc-status-sidebar-buffer-exists-p () + "Check if the sidebar buffer exists." + (get-buffer erc-status-sidebar-buffer-name)) + +(defun erc-status-sidebar-get-buffer () + "Return the sidebar buffer, creating it if it doesn't exist." + (get-buffer-create erc-status-sidebar-buffer-name)) + +(defun erc-status-sidebar-close (&optional all-frames) + "Close the sidebar. + +If called with prefix argument (ALL-FRAMES non-nil), the sidebar +will be closed on all frames. + +The erc-status-sidebar buffer is left alone, but the window +containing it on the current frame is closed. See +`erc-status-sidebar-kill'." + (interactive "P") + (mapcar #'delete-window + (get-buffer-window-list (erc-status-sidebar-get-buffer) + nil (if all-frames t)))) + +(defmacro erc-status-sidebar-writable (&rest body) + "Make the status buffer writable while executing BODY." + `(let ((buffer-read-only nil)) + ,@body)) + +;;;###autoload +(defun erc-status-sidebar-open () + "Open or create a sidebar." + (interactive) + (save-excursion + (let ((sidebar-exists (erc-status-sidebar-buffer-exists-p)) + (sidebar-buffer (erc-status-sidebar-get-buffer)) + (sidebar-window (erc-status-sidebar-get-window))) + (unless sidebar-exists + (with-current-buffer sidebar-buffer + (erc-status-sidebar-mode) + (erc-status-sidebar-refresh)))))) + +;;;###autoload +(defun erc-status-sidebar-toggle () + "Toggle the sidebar open/closed on the current frame." + (interactive) + (if (get-buffer-window erc-status-sidebar-buffer-name nil) + (erc-status-sidebar-close) + (erc-status-sidebar-open))) + +(defun erc-status-sidebar-get-channame (buffer) + "Return name of BUFFER with all leading \"#\" characters removed." + (let ((s (buffer-name buffer))) + (if (string-match "^#\\{1,2\\}" s) + (setq s (replace-match "" t t s))) + (downcase s))) + +(defun erc-status-sidebar-default-chansort (chanlist) + "Sort CHANLIST case-insensitively for display in the sidebar." + (sort chanlist (lambda (x y) + (string< (erc-status-sidebar-get-channame x) + (erc-status-sidebar-get-channame y))))) + +(defun erc-status-sidebar-default-chan-format (channame + &optional num-messages erc-face) + "Format CHANNAME for display in the sidebar. + +If NUM-MESSAGES is non-nil, append it to the channel name. If +ERC-FACE is non-nil, apply it to channel name. If it is equal to +`erc-default-face', also apply bold property to make the channel +name stand out." + (when num-messages + (setq channame (format "%s [%d]" channame num-messages))) + (when erc-face + (put-text-property 0 (length channame) 'face erc-face channame) + (when (eq erc-face 'erc-default-face) + (add-face-text-property 0 (length channame) 'bold t channame))) + channame) + +(defun erc-status-sidebar-refresh () + "Update the content of the sidebar." + (interactive) + (let ((chanlist (apply erc-status-sidebar-channel-sort + (erc-channel-list nil) nil))) + (with-current-buffer (erc-status-sidebar-get-buffer) + (erc-status-sidebar-writable + (delete-region (point-min) (point-max)) + (goto-char (point-min)) + (dolist (chanbuf chanlist) + (let* ((tup (seq-find (lambda (tup) (eq (car tup) chanbuf)) + erc-modified-channels-alist)) + (count (if tup (cadr tup))) + (face (if tup (cddr tup))) + (channame (apply erc-status-sidebar-channel-format + (buffer-name chanbuf) count face nil)) + (cnlen (length channame))) + (put-text-property 0 cnlen 'erc-buf chanbuf channame) + (put-text-property 0 cnlen 'mouse-face 'highlight channame) + (put-text-property + 0 cnlen 'help-echo + "mouse-1: switch to buffer in other window" channame) + (insert channame "\n"))))))) + +(defun erc-status-sidebar-kill () + "Close the ERC status sidebar and its buffer." + (interactive) + (ignore-errors (kill-buffer erc-status-sidebar-buffer-name))) + +(defun erc-status-sidebar-click (event) + "Handle click EVENT in `erc-status-sidebar-mode-map'." + (interactive "e") + (save-excursion + (let ((window (posn-window (event-end event))) + (pos (posn-point (event-end event)))) + (set-buffer (window-buffer window)) + (let ((buf (get-text-property pos 'erc-buf))) + (when buf + (select-window window) + (switch-to-buffer-other-window buf)))))) + +(defvar erc-status-sidebar-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map special-mode-map) + (define-key map [mouse-1] #'erc-status-sidebar-click) + map)) + +(defvar erc-status-sidebar-refresh-triggers + '(erc-track-list-changed-hook + erc-join-hook + erc-part-hook + erc-kill-buffer-hook + erc-kill-channel-hook + erc-kill-server-hook + erc-kick-hook + erc-disconnected-hook + erc-quit-hook)) + +(defun erc-status-sidebar--post-refresh (&rest ignore) + "Schedule sidebar refresh for execution after command stack is cleared. + +Ignore arguments in IGNORE, allowing this function to be added to +hooks that invoke it with arguments." + (run-at-time 0 nil #'erc-status-sidebar-refresh)) + +(defun erc-status-sidebar-mode--unhook () + "Remove hooks installed by `erc-status-sidebar-mode'." + (dolist (hk erc-status-sidebar-refresh-triggers) + (remove-hook hk #'erc-status-sidebar--post-refresh)) + (remove-hook 'window-configuration-change-hook + #'erc-status-sidebar-set-window-preserve-size)) + +(defun erc-status-sidebar-set-window-preserve-size () + "Tell Emacs to preserve the current height/width of the ERC sidebar window. + +Note that preserve status needs to be reset when the window is +manually resized, so `erc-status-sidebar-mode' adds this function +to the `window-configuration-change-hook'." + (when (and (eq (selected-window) (erc-status-sidebar-get-window)) + (fboundp 'window-preserve-size)) + (unless (eq (window-total-width) (window-min-size nil t)) + (apply 'window-preserve-size (selected-window) t t nil)))) + +(define-derived-mode erc-status-sidebar-mode special-mode "ERC Sidebar" + "Major mode for ERC status sidebar" + ;; Don't scroll the buffer horizontally, if a channel name is + ;; obscured then the window can be resized. + (setq-local auto-hscroll-mode nil) + (setq cursor-type nil + buffer-read-only t + mode-line-format erc-status-sidebar-mode-line-format + header-line-format erc-status-sidebar-header-line-format) + (erc-status-sidebar-set-window-preserve-size) + + (add-hook 'window-configuration-change-hook + #'erc-status-sidebar-set-window-preserve-size nil t) + (dolist (hk erc-status-sidebar-refresh-triggers) + (add-hook hk #'erc-status-sidebar--post-refresh)) + + ;; `change-major-mode-hook' is run *before* the + ;; erc-status-sidebar-mode initialization code, so it won't undo the + ;; add-hook's we did in the previous expressions. + (add-hook 'change-major-mode-hook #'erc-status-sidebar-mode--unhook nil t) + (add-hook 'kill-buffer-hook #'erc-status-sidebar-mode--unhook nil t) + :group 'erc-status-sidebar) + +(provide 'erc-status-sidebar) +;;; erc-status-sidebar.el ends here diff --git a/lisp/erc/erc-track.el b/lisp/erc/erc-track.el index 41d8fc1a98f..3398c8b9d0c 100644 --- a/lisp/erc/erc-track.el +++ b/lisp/erc/erc-track.el @@ -36,7 +36,6 @@ (eval-when-compile (require 'cl-lib)) (require 'erc) -(require 'erc-compat) (require 'erc-match) ;;; Code: @@ -329,9 +328,8 @@ important." (defun erc-track-remove-from-mode-line () "Remove `erc-track-modified-channels' from the mode-line." - (when (boundp 'mode-line-modes) - (setq mode-line-modes - (remove '(t erc-modified-channels-object) mode-line-modes))) + (setq mode-line-modes + (remove '(t erc-modified-channels-object) mode-line-modes)) (when (consp global-mode-string) (setq global-mode-string (delq 'erc-modified-channels-object global-mode-string)))) @@ -341,12 +339,10 @@ important." See `erc-track-position-in-mode-line' for possible values." ;; CVS Emacs has a new format string, and global-mode-string ;; is very far to the right. - (cond ((and (eq position 'before-modes) - (boundp 'mode-line-modes)) + (cond ((eq position 'before-modes) (add-to-list 'mode-line-modes '(t erc-modified-channels-object))) - ((and (eq position 'after-modes) - (boundp 'mode-line-modes)) + ((eq position 'after-modes) (add-to-list 'mode-line-modes '(t erc-modified-channels-object) t)) ((eq position t) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index cfde84e19aa..e7e43f87347 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -57,12 +57,14 @@ (load "erc-loaddefs" nil t) -(eval-when-compile (require 'cl-lib)) +(require 'cl-lib) (require 'font-lock) +(require 'format-spec) (require 'pp) (require 'thingatpt) (require 'auth-source) -(require 'erc-compat) +(require 'time-date) +(require 'iso8601) (eval-when-compile (require 'subr-x)) (defvar erc-official-location @@ -875,8 +877,8 @@ See `erc-server-flood-margin' for other flood-related parameters.") ;; Script parameters (defcustom erc-startup-file-list - (list (concat erc-user-emacs-directory ".ercrc.el") - (concat erc-user-emacs-directory ".ercrc") + (list (concat user-emacs-directory ".ercrc.el") + (concat user-emacs-directory ".ercrc") "~/.ercrc.el" "~/.ercrc" ".ercrc.el" ".ercrc") "List of files to try for a startup script. The first existent and readable one will get executed. @@ -1212,7 +1214,7 @@ which the local user typed." :group 'erc-faces) (defface erc-header-line - '((t :foreground "grey20" :background "grey90")) + '((t :inherit header-line)) "ERC face used for the header line. This will only be used if `erc-header-line-face-method' is non-nil." @@ -1304,7 +1306,7 @@ Example: (enable (intern (format "erc-%s-enable" (downcase sn)))) (disable (intern (format "erc-%s-disable" (downcase sn))))) `(progn - (erc-define-minor-mode + (define-minor-mode ,mode ,(format "Toggle ERC %S mode. With a prefix argument ARG, enable %s if ARG is positive, @@ -1487,8 +1489,7 @@ Defaults to the server buffer." (define-derived-mode erc-mode fundamental-mode "ERC" "Major mode for Emacs IRC." (setq local-abbrev-table erc-mode-abbrev-table) - (when (boundp 'next-line-add-newlines) - (set (make-local-variable 'next-line-add-newlines) nil)) + (set (make-local-variable 'next-line-add-newlines) nil) (setq line-move-ignore-invisible t) (set (make-local-variable 'paragraph-separate) (concat "\C-l\\|\\(^" (regexp-quote (erc-prompt)) "\\)")) @@ -1606,33 +1607,47 @@ symbol, it may have these values: (defun erc-generate-new-buffer-name (server port target) "Create a new buffer name based on the arguments." (when (numberp port) (setq port (number-to-string port))) - (let ((buf-name (or target - (or (let ((name (concat server ":" port))) - (when (> (length name) 1) - name)) - ;; This fallback should in fact never happen - "*erc-server-buffer*"))) - buffer-name) + (let* ((buf-name (or target + (let ((name (concat server ":" port))) + (when (> (length name) 1) + name)) + ;; This fallback should in fact never happen. + "*erc-server-buffer*")) + (full-buf-name (concat buf-name "/" server)) + (dup-buf-name (buffer-name (car (erc-channel-list nil)))) + buffer-name) ;; Reuse existing buffers, but not if the buffer is a connected server ;; buffer and not if its associated with a different server than the ;; current ERC buffer. - ;; if buf-name is taken by a different connection (or by something !erc) - ;; then see if "buf-name/server" meets the same criteria - (dolist (candidate (list buf-name (concat buf-name "/" server))) - (if (and (not buffer-name) - erc-reuse-buffers - (or (not (get-buffer candidate)) - (or target - (with-current-buffer (get-buffer candidate) - (and (erc-server-buffer-p) - (not (erc-server-process-alive))))) - (with-current-buffer (get-buffer candidate) - (and (string= erc-session-server server) - (erc-port-equal erc-session-port port))))) - (setq buffer-name candidate))) - ;; if buffer-name is unset, neither candidate worked out for us, + ;; If buf-name is taken by a different connection (or by something !erc) + ;; then see if "buf-name/server" meets the same criteria. + (if (and dup-buf-name (string-match-p (concat buf-name "/") dup-buf-name)) + (setq buffer-name full-buf-name) ; ERC buffer with full name already exists. + (dolist (candidate (list buf-name full-buf-name)) + (if (and (not buffer-name) + erc-reuse-buffers + (or (not (get-buffer candidate)) + ;; Looking for a server buffer, so there's no target. + (and (not target) + (with-current-buffer (get-buffer candidate) + (and (erc-server-buffer-p) + (not (erc-server-process-alive))))) + ;; Channel buffer; check that it's from the right server. + (and target + (with-current-buffer (get-buffer candidate) + (and (string= erc-session-server server) + (erc-port-equal erc-session-port port)))))) + (setq buffer-name candidate) + (when (and (not buffer-name) (get-buffer buf-name) erc-reuse-buffers) + ;; A new buffer will be created with the name buf-name/server, rename + ;; the existing name-duplicated buffer with the same format as well. + (with-current-buffer (get-buffer buf-name) + (when (derived-mode-p 'erc-mode) ; ensure it's an erc buffer + (rename-buffer + (concat buf-name "/" (or erc-session-server erc-server-announced-name))))))))) + ;; If buffer-name is unset, neither candidate worked out for us, ;; fallback to the old <N> uniquification method: - (or buffer-name (generate-new-buffer-name (concat buf-name "/" server))))) + (or buffer-name (generate-new-buffer-name full-buf-name)))) (defun erc-get-buffer-create (server port target) "Create a new buffer based on the arguments." @@ -1858,7 +1873,7 @@ buffer rather than a server buffer.") ;; modify `transforms' to specify what needs to be changed ;; each item is in the format '(old . new) (let ((transforms '((pcomplete . completion)))) - (erc-delete-dups + (delete-dups (mapcar (lambda (m) (or (cdr (assoc m transforms)) m)) mods)))) @@ -2311,7 +2326,7 @@ and appears in face `erc-input-face' in the buffer." (setq result (concat result network-name " << " line "\n"))) result) - (erc-propertize + (propertize (concat network-name " >> " string (if (/= ?\n (aref string @@ -2334,7 +2349,7 @@ If ARG is non-nil, show the *erc-protocol* buffer." (interactive "P") (let* ((buf (get-buffer-create "*erc-protocol*"))) (with-current-buffer buf - (erc-view-mode-enter) + (view-mode-enter) (when (null (current-local-map)) (let ((inhibit-read-only t)) (insert (erc-make-notice "This buffer displays all IRC protocol traffic exchanged with each server.\n")) @@ -2672,7 +2687,7 @@ displayed hostnames." otherwise `erc-server-announced-name'. SERVER is matched against `erc-common-server-suffixes'." (when server - (or (cdar (erc-remove-if-not + (or (cdar (cl-remove-if-not (lambda (net) (string-match (car net) server)) erc-common-server-suffixes)) erc-server-announced-name))) @@ -2768,7 +2783,7 @@ See also `erc-server-send'." (defun erc-get-arglist (fun) "Return the argument list of a function without the parens." - (let ((arglist (format "%S" (erc-function-arglist fun)))) + (let ((arglist (format "%S" (help-function-arglist fun)))) (if (string-match "\\`(\\(.*\\))\\'" arglist) (match-string 1 arglist) arglist))) @@ -2903,6 +2918,44 @@ therefore has to contain the command itself as well." (erc-server-send (substring line 1)) t) +(defvar erc--read-time-period-history nil) + +(defun erc--read-time-period (prompt) + "Read a time period on the \"2h\" format. +If there's no letter spec, the input is interpreted as a number of seconds. + +If input is blank, this function returns nil. Otherwise it +returns the time spec converted to a number of seconds." + (let ((period (string-trim + (read-string prompt nil 'erc--read-time-period-history)))) + (cond + ;; Blank input. + ((zerop (length period)) + nil) + ;; All-number -- interpret as seconds. + ((string-match-p "\\`[0-9]+\\'" period) + (string-to-number period)) + ;; Parse as a time spec. + (t + (let ((time (condition-case nil + (iso8601-parse-duration + (concat (cond + ((string-match-p "\\`P" (upcase period)) + ;; Somebody typed in a full ISO8601 period. + (upcase period)) + ((string-match-p "[YD]" (upcase period)) + ;; If we have a year/day element, + ;; we have a full spec. + "P") + (t + ;; Otherwise it's just a sub-day spec. + "PT")) + (upcase period))) + (wrong-type-argument nil)))) + (unless time + (user-error "%s is not a valid time period" period)) + (decoded-time-period time)))))) + (defun erc-cmd-IGNORE (&optional user) "Ignore USER. This should be a regexp matching nick!user@host. If no USER argument is specified, list the contents of `erc-ignore-list'." @@ -2912,10 +2965,18 @@ If no USER argument is specified, list the contents of `erc-ignore-list'." (y-or-n-p (format "Use regexp-quoted form (%s) instead? " quoted))) (setq user quoted)) - (erc-display-line - (erc-make-notice (format "Now ignoring %s" user)) - 'active) - (erc-with-server-buffer (add-to-list 'erc-ignore-list user))) + (let ((timeout + (erc--read-time-period + "Add a timeout? (Blank for no, or a time spec like 2h): ")) + (buffer (current-buffer))) + (when timeout + (run-at-time timeout nil + (lambda () + (erc--unignore-user user buffer)))) + (erc-display-line + (erc-make-notice (format "Now ignoring %s" user)) + 'active) + (erc-with-server-buffer (add-to-list 'erc-ignore-list user)))) (if (null (erc-with-server-buffer erc-ignore-list)) (erc-display-line (erc-make-notice "Ignore list is empty") 'active) (erc-display-line (erc-make-notice "Ignore list:") 'active) @@ -2939,12 +3000,17 @@ If no USER argument is specified, list the contents of `erc-ignore-list'." (erc-make-notice (format "%s is not currently ignored!" user)) 'active))) (when ignored-nick + (erc--unignore-user user (current-buffer)))) + t) + +(defun erc--unignore-user (user buffer) + (when (buffer-live-p buffer) + (with-current-buffer buffer (erc-display-line (erc-make-notice (format "No longer ignoring %s" user)) 'active) (erc-with-server-buffer - (setq erc-ignore-list (delete ignored-nick erc-ignore-list))))) - t) + (setq erc-ignore-list (delete user erc-ignore-list)))))) (defun erc-cmd-CLEAR () "Clear the window content." @@ -3097,16 +3163,18 @@ were most recently invited. See also `invitation'." (setq chnl (erc-ensure-channel-name channel))) (when chnl ;; Prevent double joining of same channel on same server. - (let ((joined-channels - (mapcar #'(lambda (chanbuf) - (with-current-buffer chanbuf (erc-default-target))) - (erc-channel-list erc-server-process)))) - (if (erc-member-ignore-case chnl joined-channels) - (switch-to-buffer (car (erc-member-ignore-case chnl - joined-channels))) - (let ((server (with-current-buffer (process-buffer erc-server-process) - (or erc-session-server erc-server-announced-name)))) - (erc-server-join-channel server chnl key)))))) + (let* ((joined-channels + (mapcar #'(lambda (chanbuf) + (with-current-buffer chanbuf (erc-default-target))) + (erc-channel-list erc-server-process))) + (server (with-current-buffer (process-buffer erc-server-process) + (or erc-session-server erc-server-announced-name))) + (chnl-name (car (erc-member-ignore-case chnl joined-channels)))) + (if chnl-name + (switch-to-buffer (if (get-buffer chnl-name) + chnl-name + (concat chnl-name "/" server))) + (erc-server-join-channel server chnl key))))) t) (defalias 'erc-cmd-CHANNEL 'erc-cmd-JOIN) @@ -3502,7 +3570,7 @@ If S is non-nil, it will be used as the quit reason." If S is non-nil, it will be used as the quit reason." (or s (if (fboundp 'yow) - (erc-replace-regexp-in-string "\n" "" (yow)) + (replace-regexp-in-string "\n" "" (yow)) (erc-quit/part-reason-default)))) (make-obsolete 'erc-quit-reason-zippy "it will be removed." "24.4") @@ -3529,7 +3597,7 @@ If S is non-nil, it will be used as the part reason." If S is non-nil, it will be used as the quit reason." (or s (if (fboundp 'yow) - (erc-replace-regexp-in-string "\n" "" (yow)) + (replace-regexp-in-string "\n" "" (yow)) (erc-quit/part-reason-default)))) (make-obsolete 'erc-part-reason-zippy "it will be removed." "24.4") @@ -3656,8 +3724,9 @@ the message given by REASON." x-toolkit-scroll-bars))) "") (if (featurep 'multi-tty) ", multi-tty" "")) - (if erc-emacs-build-time - (concat " of " erc-emacs-build-time) + (if emacs-build-time + (concat " of " (format-time-string + "%Y-%m-%d" emacs-build-time)) ""))) t) @@ -3945,13 +4014,13 @@ If FACE is non-nil, it will be used to propertize the prompt. If it is nil, ;; Do not extend the text properties when typing at the end ;; of the prompt, but stuff typed in front of the prompt ;; shall remain part of the prompt. - (setq prompt (erc-propertize prompt - 'start-open t ; XEmacs - 'rear-nonsticky t ; Emacs - 'erc-prompt t - 'field t - 'front-sticky t - 'read-only t)) + (setq prompt (propertize prompt + 'start-open t ; XEmacs + 'rear-nonsticky t ; Emacs + 'erc-prompt t + 'field t + 'front-sticky t + 'read-only t)) (erc-put-text-property 0 (1- (length prompt)) 'font-lock-face (or face 'erc-prompt-face) prompt) @@ -4003,7 +4072,8 @@ If `point' is at the beginning of a channel name, use that as default." (table (when (erc-server-buffer-live-p) (set-buffer (process-buffer erc-server-process)) erc-channel-list))) - (completing-read "Join channel: " table nil nil nil nil chnl)) + (completing-read (format-prompt "Join channel" chnl) + table nil nil nil nil chnl)) (when (or current-prefix-arg erc-prompt-for-channel-key) (read-from-minibuffer "Channel key (RET for none): " nil)))) (erc-cmd-JOIN channel (when (>= (length key) 1) key))) @@ -4334,15 +4404,15 @@ See also `erc-format-nick-function'." (defun erc-get-user-mode-prefix (user) (when user (cond ((erc-channel-user-owner-p user) - (erc-propertize "~" 'help-echo "owner")) + (propertize "~" 'help-echo "owner")) ((erc-channel-user-admin-p user) - (erc-propertize "&" 'help-echo "admin")) + (propertize "&" 'help-echo "admin")) ((erc-channel-user-op-p user) - (erc-propertize "@" 'help-echo "operator")) + (propertize "@" 'help-echo "operator")) ((erc-channel-user-halfop-p user) - (erc-propertize "%" 'help-echo "half-op")) + (propertize "%" 'help-echo "half-op")) ((erc-channel-user-voice-p user) - (erc-propertize "+" 'help-echo "voice")) + (propertize "+" 'help-echo "voice")) (t "")))) (defun erc-format-@nick (&optional user _channel-data) @@ -4353,7 +4423,7 @@ prefix. Use CHANNEL-DATA to determine op and voice status. See also `erc-format-nick-function'." (when user (let ((nick (erc-server-user-nickname user))) - (concat (erc-propertize + (concat (propertize (erc-get-user-mode-prefix nick) 'font-lock-face 'erc-nick-prefix-face) nick)))) @@ -4366,12 +4436,12 @@ also `erc-format-nick-function'." (nick (erc-current-nick)) (mode (erc-get-user-mode-prefix nick))) (concat - (erc-propertize open 'font-lock-face 'erc-default-face) - (erc-propertize mode 'font-lock-face 'erc-my-nick-prefix-face) - (erc-propertize nick 'font-lock-face 'erc-my-nick-face) - (erc-propertize close 'font-lock-face 'erc-default-face))) + (propertize open 'font-lock-face 'erc-default-face) + (propertize mode 'font-lock-face 'erc-my-nick-prefix-face) + (propertize nick 'font-lock-face 'erc-my-nick-face) + (propertize close 'font-lock-face 'erc-default-face))) (let ((prefix "> ")) - (erc-propertize prefix 'font-lock-face 'erc-default-face)))) + (propertize prefix 'font-lock-face 'erc-default-face)))) (defun erc-echo-notice-in-default-buffer (s parsed buffer _sender) "Echos a private notice in the default buffer, namely the @@ -4504,7 +4574,7 @@ See also: `erc-echo-notice-in-user-buffers', ((string-match "^-" mode) ;; Remove the unbanned masks from the ban list (setq erc-channel-banlist - (erc-delete-if + (cl-delete-if #'(lambda (y) (member (upcase (cdr y)) (mapcar #'upcase @@ -4525,7 +4595,7 @@ See also: `erc-echo-notice-in-user-buffers', "Group LIST into sublists of length N." (cond ((null list) nil) ((null (nthcdr n list)) (list list)) - (t (cons (erc-subseq list 0 n) (erc-group-list (nthcdr n list) n))))) + (t (cons (cl-subseq list 0 n) (erc-group-list (nthcdr n list) n))))) ;;; MOTD numreplies @@ -6114,8 +6184,7 @@ non-nil value is found. output (apply #'format format-args)) ;; Change all "1 units" to "1 unit". (while (string-match "\\([^0-9]\\|^\\)1 \\S-+\\(s\\)" output) - (setq output (erc-replace-match-subexpression-in-string - "" output (match-string 2 output) 2 (match-beginning 2)))) + (setq output (replace-match "" nil nil output 2))) output)) @@ -6391,17 +6460,16 @@ if `erc-away' is non-nil." (defun erc-update-mode-line-buffer (buffer) "Update the mode line in a single ERC buffer BUFFER." (with-current-buffer buffer - (let ((spec (format-spec-make - ?a (erc-format-away-status) - ?l (erc-format-lag-time) - ?m (erc-format-channel-modes) - ?n (or (erc-current-nick) "") - ?N (erc-format-network) - ?o (or (erc-controls-strip erc-channel-topic) "") - ?p (erc-port-to-string erc-session-port) - ?s (erc-format-target-and/or-server) - ?S (erc-format-target-and/or-network) - ?t (erc-format-target))) + (let ((spec `((?a . ,(erc-format-away-status)) + (?l . ,(erc-format-lag-time)) + (?m . ,(erc-format-channel-modes)) + (?n . ,(or (erc-current-nick) "")) + (?N . ,(erc-format-network)) + (?o . ,(or (erc-controls-strip erc-channel-topic) "")) + (?p . ,(erc-port-to-string erc-session-port)) + (?s . ,(erc-format-target-and/or-server)) + (?S . ,(erc-format-target-and/or-network)) + (?t . ,(erc-format-target)))) (process-status (cond ((and (erc-server-process-alive) (not erc-server-connected)) ":connecting") @@ -6434,16 +6502,16 @@ if `erc-away' is non-nil." (fill-region (point-min) (point-max)) (buffer-string)))) (setq header-line-format - (erc-replace-regexp-in-string + (replace-regexp-in-string "%" "%%" (if face - (erc-propertize header 'help-echo help-echo - 'face face) - (erc-propertize header 'help-echo help-echo)))))) + (propertize header 'help-echo help-echo + 'face face) + (propertize header 'help-echo help-echo)))))) (t (setq header-line-format (if face - (erc-propertize header 'face face) + (propertize header 'face face) header))))))) (force-mode-line-update))) @@ -6710,7 +6778,7 @@ functions." nick user host channel (if (not (string= reason "")) (format ": %s" - (erc-replace-regexp-in-string "%" "%%" reason)) + (replace-regexp-in-string "%" "%%" reason)) ""))))) |