From 2f66c3f4dcc41195e5578d6a9cf38d98fc1a05d2 Mon Sep 17 00:00:00 2001 From: dickmao Date: Fri, 1 Jul 2022 11:06:51 -0400 Subject: [PATCH 1/5] Move ERC's core dependencies to separate file Asking people to order require's is about as effective as asking kids to keep off the grass. * lisp/erc/erc-backend.el (erc--target, erc-auto-query, erc-channel-list, erc-channel-users, erc-default-nicks, erc-default-recipients, erc-format-nick-function, erc-format-query-as-channel-p, erc-hide-prompt, erc-input-marker, erc-insert-marker, erc-invitation, erc-join-buffer, erc-kill-buffer-on-part, erc-kill-server-buffer-on-quit, erc-log-p, erc-minibuffer-ignored, erc-networks--id, erc-nick, erc-nick-change-attempt-count, erc-prompt-for-channel-key, erc-prompt-hidden, erc-reuse-buffers, erc-verbose-server-ping, erc-whowas-on-nosuchnick): Forward-declare variables. (erc--open-target, erc--target-from-string, erc-active-buffer, erc-add-default-channel, erc-banlist-update, erc-buffer-filter, erc-buffer-list-with-nick, erc-channel-begin-receiving-names, erc-channel-end-receiving-names, erc-channel-p, erc-channel-receive-names, erc-cmd-JOIN, erc-connection-established, erc-current-nick, erc-current-nick-p, erc-current-time, erc-default-target, erc-delete-default-channel, erc-display-error-notice, erc-display-server-message, erc-emacs-time-to-erc-time, erc-format-message, erc-format-privmessage, erc-get-buffer, erc-handle-login, erc-handle-user-status-change, erc-ignored-reply-p, erc-ignored-user-p, erc-is-message-ctcp-and-not-action-p, erc-is-message-ctcp-p, erc-log-irc-protocol, erc-login, erc-make-notice, erc-network, erc-networks--id-given, erc-networks--id-reload, erc-nickname-in-use, erc-parse-user, erc-process-away, erc-process-ctcp-query, erc-query-buffer-p, erc-remove-channel-member, erc-remove-channel-users, erc-remove-user, erc-sec-to-time, erc-server-buffer, erc-set-active-buffer, erc-set-current-nick, erc-set-modes, erc-time-diff, erc-trim-string, erc-update-mode-line, erc-update-mode-line-buffer, erc-wash-quit-reason, erc-display-message, erc-get-buffer-create, erc-process-ctcp-reply, erc-update-channel-topic, erc-update-modes, erc-update-user-nick, erc-open, erc-update-channel-member): Forward-declare functions. (erc-response): Move to lisp/erc/erc-common.el. (erc-compat--with-memoization): Use "erc-compat-" prefixed macro. * lisp/erc/erc-common.el: New file. Change indentation for `erc-with-all-buffers-of-server' from 1 to 2. * lisp/erc/erc-compat.el (erc-compat--with-memoization): Migrate macro from `erc-common' and rename. * lisp/erc/erc-goodies.el: Require `erc-common' instead of `erc'. (erc-controls-highlight-regexp, erc-controls-remove-regexp, erc-input-marker, erc-insert-marker, erc-server-process, erc-modules, erc-log-p): Forward declare variables. (erc-buffer-list, erc-error, erc-extract-command-from-line): Forward-declare functions. * lisp/erc/erc-networks.el (erc--target, erc-insert-marker, erc-kill-buffer-hook, erc-kill-server-hook, erc-modules, erc-rename-buffers, erc-reuse-buffers, erc-server-announced-name, erc-server-connected, erc-server-parameters, erc-server-process, erc-session-server): Forward declare variables. (erc--default-target, erc--get-isupport-entry, erc-buffer-filter, erc-current-nick, erc-display-error-notice, erc-error, erc-get-buffer, erc-server-buffer, erc-server-process-alive): Forward-declare functions. (erc-obsolete-var): Also suppress free-variable warnings. * lisp/erc/erc.el: Require `erc-networks', `erc-goodies', and `erc-backend' at top of file. Don't require `erc-compat'. (erc--server-last-reconnect-count, erc--server-reconnecting, erc-channel-members-changed-hook, erc-network, erc-networks--id, erc-server-367-functions, erc-server-announced-name, erc-server-connect-function, erc-server-connected, erc-server-current-nick, erc-server-lag, erc-server-last-sent-time, erc-server-process, erc-server-quitting, erc-server-reconnect-count, erc-server-reconnecting, erc-session-client-certificate, erc-session-connector, erc-session-port, erc-session-server, erc-session-user-full-name) Remove superfluous forward declarations. (erc-message-parsed, tabbar--local-hlf, motif-version-string): Relocate forward declares to central location. (erc-session-password): Move to `erc-backend'. (erc-downcase, erc-with-server-buffer, erc-server-user, erc-channel-user, erc-get-channel-user, erc-get-server-user): Move to lisp/erc/erc-common.el. (erc-add-server-user, erc-remove-server-user, erc-channel-user-owner-p, erc-channel-user-admin-p, erc-channel-user-op-p, erc-channel-user-halfop-p, erc-channel-user-voice-p): Convert from inline functions to normal functions. (define-erc-module, erc--target, erc--target-channel, erc--target-channel-local, erc-log, erc-log-aux, erc-with-buffer, erc-with-all-buffers-of-server): Move to lisp/erc/erc-common.el. (erc-channel-members-changed-hook): Relocate option to avoid compiler warning. (erc-input, erc--input-split): Move to lisp/erc/erc-common.el. (erc-controls-strip): Remove forward declaration temporarily until this file stops requiring `erc-goodies'. * test/lisp/erc/erc-networks-tests.el: Require `erc' instead of `erc-networks'. * test/lisp/erc/erc.el (erc--meta--backend-dependencies): Remove unused test. Don't require `erc-networks'. Bug#56340. --- lisp/erc/erc-backend.el | 129 ++++++++-- lisp/erc/erc-common.el | 271 +++++++++++++++++++++ lisp/erc/erc-compat.el | 12 + lisp/erc/erc-goodies.el | 17 +- lisp/erc/erc-networks.el | 28 ++- lisp/erc/erc.el | 363 ++++------------------------ test/lisp/erc/erc-networks-tests.el | 2 +- test/lisp/erc/erc-tests.el | 22 -- 8 files changed, 476 insertions(+), 368 deletions(-) create mode 100644 lisp/erc/erc-common.el diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index df9efe4b0c..026b34849a 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -99,24 +99,117 @@ ;;; Code: (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 -;; reverse is true: -(require 'erc) +(require 'erc-common) + +(defvar erc--target) +(defvar erc-auto-query) +(defvar erc-channel-list) +(defvar erc-channel-users) +(defvar erc-default-nicks) +(defvar erc-default-recipients) +(defvar erc-format-nick-function) +(defvar erc-format-query-as-channel-p) +(defvar erc-hide-prompt) +(defvar erc-input-marker) +(defvar erc-insert-marker) +(defvar erc-invitation) +(defvar erc-join-buffer) +(defvar erc-kill-buffer-on-part) +(defvar erc-kill-server-buffer-on-quit) +(defvar erc-log-p) +(defvar erc-minibuffer-ignored) +(defvar erc-networks--id) +(defvar erc-nick) +(defvar erc-nick-change-attempt-count) +(defvar erc-prompt-for-channel-key) +(defvar erc-prompt-hidden) +(defvar erc-reuse-buffers) +(defvar erc-verbose-server-ping) +(defvar erc-whowas-on-nosuchnick) + +(declare-function erc--open-target "erc" (target)) +(declare-function erc--target-from-string "erc" (string)) +(declare-function erc-active-buffer "erc" nil) +(declare-function erc-add-default-channel "erc" (channel)) +(declare-function erc-banlist-update "erc" (proc parsed)) +(declare-function erc-buffer-filter "erc" (predicate &optional proc)) +(declare-function erc-buffer-list-with-nick "erc" (nick proc)) +(declare-function erc-channel-begin-receiving-names "erc" nil) +(declare-function erc-channel-end-receiving-names "erc" nil) +(declare-function erc-channel-p "erc" (channel)) +(declare-function erc-channel-receive-names "erc" (names-string)) +(declare-function erc-cmd-JOIN "erc" (channel &optional key)) +(declare-function erc-connection-established "erc" (proc parsed)) +(declare-function erc-current-nick "erc" nil) +(declare-function erc-current-nick-p "erc" (nick)) +(declare-function erc-current-time "erc" (&optional specified-time)) +(declare-function erc-default-target "erc" nil) +(declare-function erc-delete-default-channel "erc" (channel &optional buffer)) +(declare-function erc-display-error-notice "erc" (parsed string)) +(declare-function erc-display-server-message "erc" (_proc parsed)) +(declare-function erc-emacs-time-to-erc-time "erc" (&optional specified-time)) +(declare-function erc-format-message "erc" (msg &rest args)) +(declare-function erc-format-privmessage "erc" (nick msg privp msgp)) +(declare-function erc-get-buffer "erc" (target &optional proc)) +(declare-function erc-handle-login "erc" nil) +(declare-function erc-handle-user-status-change "erc" (type nlh &optional l)) +(declare-function erc-ignored-reply-p "erc" (msg tgt proc)) +(declare-function erc-ignored-user-p "erc" (spec)) +(declare-function erc-is-message-ctcp-and-not-action-p "erc" (message)) +(declare-function erc-is-message-ctcp-p "erc" (message)) +(declare-function erc-log-irc-protocol "erc" (string &optional outbound)) +(declare-function erc-login "erc" nil) +(declare-function erc-make-notice "erc" (message)) +(declare-function erc-network "erc-networks" nil) +(declare-function erc-networks--id-given "erc-networks" (arg &rest args)) +(declare-function erc-networks--id-reload "erc-networks" (arg &rest args)) +(declare-function erc-nickname-in-use "erc" (nick reason)) +(declare-function erc-parse-user "erc" (string)) +(declare-function erc-process-away "erc" (proc away-p)) +(declare-function erc-process-ctcp-query "erc" (proc parsed nick login host)) +(declare-function erc-query-buffer-p "erc" (&optional buffer)) +(declare-function erc-remove-channel-member "erc" (channel nick)) +(declare-function erc-remove-channel-users "erc" nil) +(declare-function erc-remove-user "erc" (nick)) +(declare-function erc-sec-to-time "erc" (ns)) +(declare-function erc-server-buffer "erc" nil) +(declare-function erc-set-active-buffer "erc" (buffer)) +(declare-function erc-set-current-nick "erc" (nick)) +(declare-function erc-set-modes "erc" (tgt mode-string)) +(declare-function erc-time-diff "erc" (t1 t2)) +(declare-function erc-trim-string "erc" (s)) +(declare-function erc-update-mode-line "erc" (&optional buffer)) +(declare-function erc-update-mode-line-buffer "erc" (buffer)) +(declare-function erc-wash-quit-reason "erc" (reason nick login host)) + +(declare-function erc-display-message "erc" + (parsed type buffer msg &rest args)) +(declare-function erc-get-buffer-create "erc" + (server port target &optional tgt-info id)) +(declare-function erc-process-ctcp-reply "erc" + (proc parsed nick login host msg)) +(declare-function erc-update-channel-topic "erc" + (channel topic &optional modify)) +(declare-function erc-update-modes "erc" + (tgt mode-string &optional _nick _host _login)) +(declare-function erc-update-user-nick "erc" + (nick &optional new-nick host login full-name info)) +(declare-function erc-open "erc" + (&optional server port nick full-name connect passwd tgt-list + channel process client-certificate user id)) +(declare-function erc-update-channel-member "erc" + (channel nick new-nick + &optional add voice halfop op admin owner host + login full-name info update-message-time)) ;;;; Variables and options +(defvar-local erc-session-password nil + "The password used for the current session.") + (defvar erc-server-responses (make-hash-table :test #'equal) "Hash table mapping server responses to their handler hooks.") -(cl-defstruct (erc-response (:conc-name erc-response.)) - (unparsed "" :type string) - (sender "" :type string) - (command "" :type string) - (command-args '() :type list) - (contents "" :type string) - (tags '() :type list)) - ;;; User data (defvar-local erc-server-current-nick nil @@ -1662,16 +1755,6 @@ erc--parse-isupport-value (split-string value ",") (list value))))) -(defmacro erc--with-memoization (table &rest forms) - "Adapter to be migrated to erc-compat." - (declare (indent defun)) - `(cond - ((fboundp 'with-memoization) - (with-memoization ,table ,@forms)) ; 29.1 - ((fboundp 'cl--generic-with-memoization) - (cl--generic-with-memoization ,table ,@forms)) - (t ,@forms))) - (defun erc--get-isupport-entry (key &optional single) "Return an item for \"ISUPPORT\" token KEY, a symbol. When a lookup fails return nil. Otherwise return a list whose @@ -1681,7 +1764,7 @@ erc--get-isupport-entry primitive value." (if-let* ((table (or erc--isupport-params (erc-with-server-buffer erc--isupport-params))) - (value (erc--with-memoization (gethash key table) + (value (erc-compat--with-memoization (gethash key table) (when-let ((v (assoc (symbol-name key) erc-server-parameters))) (if (cdr v) diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el new file mode 100644 index 0000000000..d8aac36eab --- /dev/null +++ b/lisp/erc/erc-common.el @@ -0,0 +1,271 @@ +;;; erc-common.el --- Macros and types for ERC -*- lexical-binding:t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; Maintainer: Amin Bandali , F. Jason Park +;; Keywords: comm, IRC, chat, client, internet +;; +;; 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 . + +;;; Commentary: +;;; Code: + +(eval-when-compile (require 'cl-lib) (require 'subr-x)) +(require 'erc-compat) + +(defvar erc--casemapping-rfc1459) +(defvar erc--casemapping-rfc1459-strict) +(defvar erc-channel-users) +(defvar erc-dbuf) +(defvar erc-log-p) +(defvar erc-server-users) +(defvar erc-session-server) + +(declare-function erc--get-isupport-entry "erc-backend" (key &optional single)) +(declare-function erc-get-buffer "erc" (target &optional proc)) +(declare-function erc-server-buffer "erc" nil) + +(cl-defstruct erc-input + string insertp sendp) + +(cl-defstruct (erc--input-split (:include erc-input)) + lines cmdp) + +(cl-defstruct (erc-server-user (:type vector) :named) + ;; User data + nickname host login full-name info + ;; Buffers + ;; + ;; This is an alist of the form (BUFFER . CHANNEL-DATA), where + ;; CHANNEL-DATA is either nil or an erc-channel-user struct. + (buffers nil)) + +(cl-defstruct (erc-channel-user (:type vector) :named) + voice halfop op admin owner + ;; Last message time (in the form of the return value of + ;; (current-time) + ;; + ;; This is useful for ordered name completion. + (last-message-time nil)) + +(cl-defstruct erc--target + (string "" :type string :documentation "Received name of target.") + (symbol nil :type symbol :documentation "Case-mapped name as symbol.")) + +;; At some point, it may make sense to add a query type with an +;; account field, which may help support reassociation across +;; reconnects and nick changes (likely requires v3 extensions). +;; +;; These channel variants should probably take on a `joined' field to +;; track "joinedness", which `erc-server-JOIN', `erc-server-PART', +;; etc. should toggle. Functions like `erc--current-buffer-joined-p' +;; may find it useful. + +(cl-defstruct (erc--target-channel (:include erc--target))) +(cl-defstruct (erc--target-channel-local (:include erc--target-channel))) + +(cl-defstruct (erc-response (:conc-name erc-response.)) + (unparsed "" :type string) + (sender "" :type string) + (command "" :type string) + (command-args '() :type list) + (contents "" :type string) + (tags '() :type list)) + +(defmacro define-erc-module (name alias doc enable-body disable-body + &optional local-p) + "Define a new minor mode using ERC conventions. +Symbol NAME is the name of the module. +Symbol ALIAS is the alias to use, or nil. +DOC is the documentation string to use for the minor mode. +ENABLE-BODY is a list of expressions used to enable the mode. +DISABLE-BODY is a list of expressions used to disable the mode. +If LOCAL-P is non-nil, the mode will be created as a buffer-local +mode, rather than a global one. + +This will define a minor mode called erc-NAME-mode, possibly +an alias erc-ALIAS-mode, as well as the helper functions +erc-NAME-enable, and erc-NAME-disable. + +Example: + + ;;;###autoload(autoload \\='erc-replace-mode \"erc-replace\") + (define-erc-module replace nil + \"This mode replaces incoming text according to `erc-replace-alist'.\" + ((add-hook \\='erc-insert-modify-hook + #\\='erc-replace-insert)) + ((remove-hook \\='erc-insert-modify-hook + #\\='erc-replace-insert)))" + (declare (doc-string 3) (indent defun)) + (let* ((sn (symbol-name name)) + (mode (intern (format "erc-%s-mode" (downcase sn)))) + (group (intern (format "erc-%s" (downcase sn)))) + (enable (intern (format "erc-%s-enable" (downcase sn)))) + (disable (intern (format "erc-%s-disable" (downcase sn))))) + `(progn + (define-minor-mode + ,mode + ,(format "Toggle ERC %S mode. +With a prefix argument ARG, enable %s if ARG is positive, +and disable it otherwise. If called from Lisp, enable the mode +if ARG is omitted or nil. +%s" name name doc) + ;; FIXME: We don't know if this group exists, so this `:group' may + ;; actually just silence a valid warning about the fact that the var + ;; is not associated with any group. + :global ,(not local-p) :group (quote ,group) + (if ,mode + (,enable) + (,disable))) + (defun ,enable () + ,(format "Enable ERC %S mode." + name) + (interactive) + (add-to-list 'erc-modules (quote ,name)) + (setq ,mode t) + ,@enable-body) + (defun ,disable () + ,(format "Disable ERC %S mode." + name) + (interactive) + (setq erc-modules (delq (quote ,name) erc-modules)) + (setq ,mode nil) + ,@disable-body) + ,(when (and alias (not (eq name alias))) + `(defalias + ',(intern + (format "erc-%s-mode" + (downcase (symbol-name alias)))) + #',mode)) + ;; For find-function and find-variable. + (put ',mode 'definition-name ',name) + (put ',enable 'definition-name ',name) + (put ',disable 'definition-name ',name)))) + +(defmacro erc-with-buffer (spec &rest body) + "Execute BODY in the buffer associated with SPEC. + +SPEC should have the form + + (TARGET [PROCESS]) + +If TARGET is a buffer, use it. Otherwise, use the buffer +matching TARGET in the process specified by PROCESS. + +If PROCESS is nil, use the current `erc-server-process'. +See `erc-get-buffer' for details. + +See also `with-current-buffer'. + +\(fn (TARGET [PROCESS]) BODY...)" + (declare (indent 1) (debug ((form &optional form) body))) + (let ((buf (make-symbol "buf")) + (proc (make-symbol "proc")) + (target (make-symbol "target")) + (process (make-symbol "process"))) + `(let* ((,target ,(car spec)) + (,process ,(cadr spec)) + (,buf (if (bufferp ,target) + ,target + (let ((,proc (or ,process + (and (processp erc-server-process) + erc-server-process)))) + (if (and ,target ,proc) + (erc-get-buffer ,target ,proc)))))) + (when (buffer-live-p ,buf) + (with-current-buffer ,buf + ,@body))))) + +(defmacro erc-with-server-buffer (&rest body) + "Execute BODY in the current ERC server buffer. +If no server buffer exists, return nil." + (declare (indent 0) (debug (body))) + (let ((buffer (make-symbol "buffer"))) + `(let ((,buffer (erc-server-buffer))) + (when (buffer-live-p ,buffer) + (with-current-buffer ,buffer + ,@body))))) + +(defmacro erc-with-all-buffers-of-server (process pred &rest forms) + "Execute FORMS in all buffers which have same process as this server. +FORMS will be evaluated in all buffers having the process PROCESS and +where PRED matches or in all buffers of the server process if PRED is +nil." + (declare (indent 2) (debug (form form body))) + (macroexp-let2 nil pred pred + `(erc-buffer-filter (lambda () + (when (or (not ,pred) (funcall ,pred)) + ,@forms)) + ,process))) + +(defun erc-log-aux (string) + "Do the debug logging of STRING." + (let ((cb (current-buffer)) + (point 1) + (was-eob nil) + (session-buffer (erc-server-buffer))) + (if session-buffer + (progn + (set-buffer session-buffer) + (if (not (and erc-dbuf (bufferp erc-dbuf) (buffer-live-p erc-dbuf))) + (progn + (setq erc-dbuf (get-buffer-create + (concat "*ERC-DEBUG: " + erc-session-server "*"))))) + (set-buffer erc-dbuf) + (setq point (point)) + (setq was-eob (eobp)) + (goto-char (point-max)) + (insert (concat "** " string "\n")) + (if was-eob (goto-char (point-max)) + (goto-char point)) + (set-buffer cb)) + (message "ERC: ** %s" string)))) + +(define-inline erc-log (string) + "Logs STRING if logging is on (see `erc-log-p')." + (inline-quote + (when erc-log-p + (erc-log-aux ,string)))) + +(defun erc-downcase (string) + "Return a downcased copy of STRING with properties. +Use the CASEMAPPING ISUPPORT parameter to determine the style." + (let* ((mapping (erc--get-isupport-entry 'CASEMAPPING 'single)) + (inhibit-read-only t)) + (if (equal mapping "ascii") + (downcase string) + (with-temp-buffer + (insert string) + (translate-region (point-min) (point-max) + (if (equal mapping "rfc1459-strict") + erc--casemapping-rfc1459-strict + erc--casemapping-rfc1459)) + (buffer-string))))) + +(define-inline erc-get-channel-user (nick) + "Find NICK in the current buffer's `erc-channel-users' hash table." + (inline-quote (gethash (erc-downcase ,nick) erc-channel-users))) + +(define-inline erc-get-server-user (nick) + "Find NICK in the current server's `erc-server-users' hash table." + (inline-letevals (nick) + (inline-quote (erc-with-server-buffer + (gethash (erc-downcase ,nick) erc-server-users))))) + +(provide 'erc-common) + +;;; erc-common.el ends here diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el index 8a00e711ac..03bd8f1352 100644 --- a/lisp/erc/erc-compat.el +++ b/lisp/erc/erc-compat.el @@ -156,6 +156,18 @@ erc-subseq (setq i (1+ i) start (1+ start))) res)))))) + +;;;; Misc 29.1 + +(defmacro erc-compat--with-memoization (table &rest forms) + (declare (indent defun)) + (cond + ((fboundp 'with-memoization) + `(with-memoization ,table ,@forms)) ; 29.1 + ((fboundp 'cl--generic-with-memoization) + `(cl--generic-with-memoization ,table ,@forms)) + (t `(progn ,@forms)))) + (provide 'erc-compat) ;;; erc-compat.el ends here diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el index 8fef23945d..59b5f01f23 100644 --- a/lisp/erc/erc-goodies.el +++ b/lisp/erc/erc-goodies.el @@ -29,10 +29,23 @@ ;;; Code: -(require 'erc) - ;;; Imenu support +(require 'erc-common) + +(defvar erc-controls-highlight-regexp) +(defvar erc-controls-remove-regexp) +(defvar erc-input-marker) +(defvar erc-insert-marker) +(defvar erc-server-process) +(defvar erc-modules) +(defvar erc-log-p) + +(declare-function erc-buffer-list "erc" (&optional predicate proc)) +(declare-function erc-error "erc" (&rest args)) +(declare-function erc-extract-command-from-line "erc" (line)) +(declare-function erc-beg-of-input-line "erc" nil) + (defun erc-imenu-setup () "Setup Imenu support in an ERC buffer." (setq-local imenu-create-index-function #'erc-create-imenu-index)) diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el index 2c8f8fb72b..667b0c3d76 100644 --- a/lisp/erc/erc-networks.el +++ b/lisp/erc/erc-networks.el @@ -39,8 +39,32 @@ ;;; Code: -(require 'erc) (eval-when-compile (require 'cl-lib)) +(require 'erc-common) + +(defvar erc--target) +(defvar erc-insert-marker) +(defvar erc-kill-buffer-hook) +(defvar erc-kill-server-hook) +(defvar erc-modules) +(defvar erc-rename-buffers) +(defvar erc-reuse-buffers) +(defvar erc-server-announced-name) +(defvar erc-server-connected) +(defvar erc-server-parameters) +(defvar erc-server-process) +(defvar erc-session-server) + +(declare-function erc--default-target "erc" nil) +(declare-function erc--get-isupport-entry "erc-backend" (key &optional single)) +(declare-function erc-buffer-filter "erc" (predicate &optional proc)) +(declare-function erc-current-nick "erc" nil) +(declare-function erc-display-error-notice "erc" (parsed string)) +(declare-function erc-error "erc" (&rest args)) +(declare-function erc-get-buffer "erc" (target &optional proc)) +(declare-function erc-server-buffer "erc" nil) +(declare-function erc-server-process-alive "erc-backend" (&optional buffer)) +(declare-function erc-set-active-buffer "erc" (buffer)) ;; Variables @@ -813,7 +837,7 @@ erc-networks--id-given (erc-networks--id-symbol nid)) (cl-generic-define-context-rewriter erc-obsolete-var (var spec) - `((with-suppressed-warnings ((obsolete ,var)) ,var) ,spec)) + `((with-suppressed-warnings ((obsolete ,var) (free-vars ,var)) ,var) ,spec)) ;; As a catch-all, derive the symbol from the unquoted printed repr. (cl-defgeneric erc-networks--id-create (id) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index db39e341b2..e0a4bd3001 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -60,6 +60,9 @@ (load "erc-loaddefs" 'noerror 'nomessage) +(require 'erc-networks) +(require 'erc-goodies) +(require 'erc-backend) (require 'cl-lib) (require 'format-spec) (require 'pp) @@ -69,8 +72,6 @@ (require 'iso8601) (eval-when-compile (require 'subr-x)) -(require 'erc-compat) - (defconst erc-version "5.4.1" "This version of ERC.") @@ -132,29 +133,12 @@ erc-scripts "Running scripts at startup and with /LOAD." :group 'erc) -;; Defined in erc-backend -(defvar erc--server-last-reconnect-count) -(defvar erc--server-reconnecting) -(defvar erc-channel-members-changed-hook) -(defvar erc-network) -(defvar erc-networks--id) -(defvar erc-server-367-functions) -(defvar erc-server-announced-name) -(defvar erc-server-connect-function) -(defvar erc-server-connected) -(defvar erc-server-current-nick) -(defvar erc-server-lag) -(defvar erc-server-last-sent-time) -(defvar erc-server-process) -(defvar erc-server-quitting) -(defvar erc-server-reconnect-count) -(defvar erc-server-reconnecting) -(defvar erc-session-client-certificate) -(defvar erc-session-connector) -(defvar erc-session-port) -(defvar erc-session-server) -(defvar erc-session-user-full-name) -(defvar erc-session-username) +;; Forward declarations +(defvar erc-message-parsed) + +(defvar tabbar--local-hlf) +(defvar motif-version-string) +(defvar gtk-version-string) ;; tunable connection and authentication parameters @@ -349,9 +333,6 @@ erc-channel-hide-list :group 'erc-ignore :type 'erc-message-type) -(defvar-local erc-session-password nil - "The password used for the current session.") - (defcustom erc-disconnected-hook nil "Run this hook with arguments (NICK IP REASON) when disconnected. This happens before automatic reconnection. Note, that @@ -436,69 +417,14 @@ erc--casemapping-rfc1459-strict '((?\[ . ?\{) (?\] . ?\}) (?\\ . ?\|)) (mapcar (lambda (c) (cons c (+ c 32))) "ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) -(defun erc-downcase (string) - "Return a downcased copy of STRING with properties. -Use the CASEMAPPING ISUPPORT parameter to determine the style." - (let* ((mapping (erc--get-isupport-entry 'CASEMAPPING 'single)) - (inhibit-read-only t)) - (if (equal mapping "ascii") - (downcase string) - (with-temp-buffer - (insert string) - (translate-region (point-min) (point-max) - (if (equal mapping "rfc1459-strict") - erc--casemapping-rfc1459-strict - erc--casemapping-rfc1459)) - (buffer-string))))) - -(defmacro erc-with-server-buffer (&rest body) - "Execute BODY in the current ERC server buffer. -If no server buffer exists, return nil." - (declare (indent 0) (debug (body))) - (let ((buffer (make-symbol "buffer"))) - `(let ((,buffer (erc-server-buffer))) - (when (buffer-live-p ,buffer) - (with-current-buffer ,buffer - ,@body))))) - -(cl-defstruct (erc-server-user (:type vector) :named) - ;; User data - nickname host login full-name info - ;; Buffers - ;; - ;; This is an alist of the form (BUFFER . CHANNEL-DATA), where - ;; CHANNEL-DATA is either nil or an erc-channel-user struct. - (buffers nil) - ) - -(cl-defstruct (erc-channel-user (:type vector) :named) - voice halfop op admin owner - ;; Last message time (in the form of the return value of - ;; (current-time) - ;; - ;; This is useful for ordered name completion. - (last-message-time nil)) - -(define-inline erc-get-channel-user (nick) - "Find NICK in the current buffer's `erc-channel-users' hash table." - (inline-quote (gethash (erc-downcase ,nick) erc-channel-users))) - -(define-inline erc-get-server-user (nick) - "Find NICK in the current server's `erc-server-users' hash table." - (inline-letevals (nick) - (inline-quote (erc-with-server-buffer - (gethash (erc-downcase ,nick) erc-server-users))))) - -(define-inline erc-add-server-user (nick user) +(defun erc-add-server-user (nick user) "This function is for internal use only. Adds USER with nickname NICK to the `erc-server-users' hash table." - (inline-letevals (nick user) - (inline-quote - (erc-with-server-buffer - (puthash (erc-downcase ,nick) ,user erc-server-users))))) + (erc-with-server-buffer + (puthash (erc-downcase nick) user erc-server-users))) -(define-inline erc-remove-server-user (nick) +(defun erc-remove-server-user (nick) "This function is for internal use only. Removes the user with nickname NICK from the `erc-server-users' @@ -506,10 +432,8 @@ erc-remove-server-user `erc-channel-users' lists of other buffers. See also: `erc-remove-user'." - (inline-letevals (nick) - (inline-quote - (erc-with-server-buffer - (remhash (erc-downcase ,nick) erc-server-users))))) + (erc-with-server-buffer + (remhash (erc-downcase nick) erc-server-users))) (defun erc-change-user-nickname (user new-nick) "This function is for internal use only. @@ -580,55 +504,45 @@ erc-remove-channel-users erc-channel-users) (clrhash erc-channel-users))) -(define-inline erc-channel-user-owner-p (nick) +(defun erc-channel-user-owner-p (nick) "Return non-nil if NICK is an owner of the current channel." - (inline-letevals (nick) - (inline-quote - (and ,nick - (hash-table-p erc-channel-users) - (let ((cdata (erc-get-channel-user ,nick))) - (and cdata (cdr cdata) - (erc-channel-user-owner (cdr cdata)))))))) - -(define-inline erc-channel-user-admin-p (nick) + (and nick + (hash-table-p erc-channel-users) + (let ((cdata (erc-get-channel-user nick))) + (and cdata (cdr cdata) + (erc-channel-user-owner (cdr cdata)))))) + +(defun erc-channel-user-admin-p (nick) "Return non-nil if NICK is an admin in the current channel." - (inline-letevals (nick) - (inline-quote - (and ,nick + (and nick (hash-table-p erc-channel-users) - (let ((cdata (erc-get-channel-user ,nick))) + (let ((cdata (erc-get-channel-user nick))) (and cdata (cdr cdata) - (erc-channel-user-admin (cdr cdata)))))))) + (erc-channel-user-admin (cdr cdata)))))) -(define-inline erc-channel-user-op-p (nick) +(defun erc-channel-user-op-p (nick) "Return non-nil if NICK is an operator in the current channel." - (inline-letevals (nick) - (inline-quote - (and ,nick + (and nick (hash-table-p erc-channel-users) - (let ((cdata (erc-get-channel-user ,nick))) + (let ((cdata (erc-get-channel-user nick))) (and cdata (cdr cdata) - (erc-channel-user-op (cdr cdata)))))))) + (erc-channel-user-op (cdr cdata)))))) -(define-inline erc-channel-user-halfop-p (nick) +(defun erc-channel-user-halfop-p (nick) "Return non-nil if NICK is a half-operator in the current channel." - (inline-letevals (nick) - (inline-quote - (and ,nick + (and nick (hash-table-p erc-channel-users) - (let ((cdata (erc-get-channel-user ,nick))) + (let ((cdata (erc-get-channel-user nick))) (and cdata (cdr cdata) - (erc-channel-user-halfop (cdr cdata)))))))) + (erc-channel-user-halfop (cdr cdata)))))) -(define-inline erc-channel-user-voice-p (nick) +(defun erc-channel-user-voice-p (nick) "Return non-nil if NICK has voice in the current channel." - (inline-letevals (nick) - (inline-quote - (and ,nick + (and nick (hash-table-p erc-channel-users) - (let ((cdata (erc-get-channel-user ,nick))) + (let ((cdata (erc-get-channel-user nick))) (and cdata (cdr cdata) - (erc-channel-user-voice (cdr cdata)))))))) + (erc-channel-user-voice (cdr cdata)))))) (defun erc-get-channel-user-list () "Return a list of users in the current channel. @@ -1377,96 +1291,6 @@ erc-debug-log-file (defvar-local erc-dbuf nil) -(defmacro define-erc-module (name alias doc enable-body disable-body - &optional local-p) - "Define a new minor mode using ERC conventions. -Symbol NAME is the name of the module. -Symbol ALIAS is the alias to use, or nil. -DOC is the documentation string to use for the minor mode. -ENABLE-BODY is a list of expressions used to enable the mode. -DISABLE-BODY is a list of expressions used to disable the mode. -If LOCAL-P is non-nil, the mode will be created as a buffer-local -mode, rather than a global one. - -This will define a minor mode called erc-NAME-mode, possibly -an alias erc-ALIAS-mode, as well as the helper functions -erc-NAME-enable, and erc-NAME-disable. - -Example: - - ;;;###autoload(autoload \\='erc-replace-mode \"erc-replace\") - (define-erc-module replace nil - \"This mode replaces incoming text according to `erc-replace-alist'.\" - ((add-hook \\='erc-insert-modify-hook - #\\='erc-replace-insert)) - ((remove-hook \\='erc-insert-modify-hook - #\\='erc-replace-insert)))" - (declare (doc-string 3) (indent defun)) - (let* ((sn (symbol-name name)) - (mode (intern (format "erc-%s-mode" (downcase sn)))) - (group (intern (format "erc-%s" (downcase sn)))) - (enable (intern (format "erc-%s-enable" (downcase sn)))) - (disable (intern (format "erc-%s-disable" (downcase sn))))) - `(progn - (define-minor-mode - ,mode - ,(format "Toggle ERC %S mode. -With a prefix argument ARG, enable %s if ARG is positive, -and disable it otherwise. If called from Lisp, enable the mode -if ARG is omitted or nil. -%s" name name doc) - ;; FIXME: We don't know if this group exists, so this `:group' may - ;; actually just silence a valid warning about the fact that the var - ;; is not associated with any group. - :global ,(not local-p) :group (quote ,group) - (if ,mode - (,enable) - (,disable))) - (defun ,enable () - ,(format "Enable ERC %S mode." - name) - (interactive) - (add-to-list 'erc-modules (quote ,name)) - (setq ,mode t) - ,@enable-body) - (defun ,disable () - ,(format "Disable ERC %S mode." - name) - (interactive) - (setq erc-modules (delq (quote ,name) erc-modules)) - (setq ,mode nil) - ,@disable-body) - ,(when (and alias (not (eq name alias))) - `(defalias - ',(intern - (format "erc-%s-mode" - (downcase (symbol-name alias)))) - #',mode)) - ;; For find-function and find-variable. - (put ',mode 'definition-name ',name) - (put ',enable 'definition-name ',name) - (put ',disable 'definition-name ',name)))) - -;; The rationale for favoring inheritance here (nicer dispatch) is -;; kinda flimsy since there aren't yet any actual methods. - -(cl-defstruct erc--target - (string "" :type string :documentation "Received name of target.") - (symbol nil :type symbol :documentation "Case-mapped name as symbol.")) - -;; These should probably take on a `joined' field to track joinedness, -;; which should be toggled by `erc-server-JOIN', `erc-server-PART', -;; etc. Functions like `erc--current-buffer-joined-p' (bug#48598) may -;; find it useful. - -(cl-defstruct (erc--target-channel (:include erc--target))) - -(cl-defstruct (erc--target-channel-local (:include erc--target-channel))) - -;; At some point, it may make sense to add a query type with an -;; account field, which may help support reassociation across -;; reconnects and nick changes (likely requires v3 extensions). - (defun erc--target-from-string (string) "Construct an `erc--target' variant from STRING." (funcall (if (erc-channel-p string) @@ -1516,12 +1340,6 @@ erc-once-with-server-event (add-hook hook fun nil t) fun)) -(define-inline erc-log (string) - "Logs STRING if logging is on (see `erc-log-p')." - (inline-quote - (when erc-log-p - (erc-log-aux ,string)))) - (defun erc-server-buffer () "Return the server buffer for the current buffer's process. The buffer-local variable `erc-server-process' is used to find @@ -1577,29 +1395,7 @@ erc-ison-p (if erc-online-p "" "not ")) erc-online-p)))) -(defun erc-log-aux (string) - "Do the debug logging of STRING." - (let ((cb (current-buffer)) - (point 1) - (was-eob nil) - (session-buffer (erc-server-buffer))) - (if session-buffer - (progn - (set-buffer session-buffer) - (if (not (and erc-dbuf (bufferp erc-dbuf) (buffer-live-p erc-dbuf))) - (progn - (setq erc-dbuf (get-buffer-create - (concat "*ERC-DEBUG: " - erc-session-server "*"))))) - (set-buffer erc-dbuf) - (setq point (point)) - (setq was-eob (eobp)) - (goto-char (point-max)) - (insert (concat "** " string "\n")) - (if was-eob (goto-char (point-max)) - (goto-char point)) - (set-buffer cb)) - (message "ERC: ** %s" string)))) + ;; Last active buffer, to print server messages in the right place @@ -1841,40 +1637,6 @@ erc-member-ignore-case (throw 'result list) (setq list (cdr list)))))) -(defmacro erc-with-buffer (spec &rest body) - "Execute BODY in the buffer associated with SPEC. - -SPEC should have the form - - (TARGET [PROCESS]) - -If TARGET is a buffer, use it. Otherwise, use the buffer -matching TARGET in the process specified by PROCESS. - -If PROCESS is nil, use the current `erc-server-process'. -See `erc-get-buffer' for details. - -See also `with-current-buffer'. - -\(fn (TARGET [PROCESS]) BODY...)" - (declare (indent 1) (debug ((form &optional form) body))) - (let ((buf (make-symbol "buf")) - (proc (make-symbol "proc")) - (target (make-symbol "target")) - (process (make-symbol "process"))) - `(let* ((,target ,(car spec)) - (,process ,(cadr spec)) - (,buf (if (bufferp ,target) - ,target - (let ((,proc (or ,process - (and (processp erc-server-process) - erc-server-process)))) - (if (and ,target ,proc) - (erc-get-buffer ,target ,proc)))))) - (when (buffer-live-p ,buf) - (with-current-buffer ,buf - ,@body))))) - (defun erc-get-buffer (target &optional proc) "Return the buffer matching TARGET in the process PROC. If PROC is not supplied, all processes are searched." @@ -1921,18 +1683,6 @@ erc-buffer-list (setq predicate (lambda () t))) (erc-buffer-filter predicate proc)) -(defmacro erc-with-all-buffers-of-server (process pred &rest forms) - "Execute FORMS in all buffers which have same process as this server. -FORMS will be evaluated in all buffers having the process PROCESS and -where PRED matches or in all buffers of the server process if PRED is -nil." - (declare (indent 1) (debug (form form body))) - (macroexp-let2 nil pred pred - `(erc-buffer-filter (lambda () - (when (or (not ,pred) (funcall ,pred)) - ,@forms)) - ,process))) - (define-obsolete-function-alias 'erc-iswitchb #'erc-switch-to-buffer "25.1") (defun erc--switch-to-buffer (&optional arg) (read-buffer "Switch to ERC buffer: " @@ -2877,8 +2627,6 @@ erc-lurker-cleanup-interval consumption of lurker state during long Emacs sessions and/or ERC sessions with large numbers of incoming PRIVMSGs.") -(defvar erc-message-parsed) - (defun erc-lurker-update-status (_message) "Update `erc-lurker-state' if necessary. @@ -4090,9 +3838,6 @@ erc-cmd-SERVER t) (put 'erc-cmd-SERVER 'process-not-needed t) -(defvar motif-version-string) -(defvar gtk-version-string) - (defun erc-cmd-SV () "Say the current ERC and Emacs version into channel." (erc-send-message (format "I'm using ERC %s with GNU Emacs %s (%s%s)%s." @@ -5349,6 +5094,12 @@ erc-parse-prefix (setq i (1+ i))) alist)))) +(defcustom erc-channel-members-changed-hook nil + "This hook is called every time the variable `channel-members' changes. +The buffer where the change happened is current while this hook is called." + :group 'erc-hooks + :type 'hook) + (defun erc-channel-receive-names (names-string) "This function is for internal use only. @@ -5392,13 +5143,6 @@ erc-channel-receive-names name name t voice halfop op admin owner))))) (run-hooks 'erc-channel-members-changed-hook))) - -(defcustom erc-channel-members-changed-hook nil - "This hook is called every time the variable `channel-members' changes. -The buffer where the change happened is current while this hook is called." - :group 'erc-hooks - :type 'hook) - (defun erc-update-user-nick (nick &optional new-nick host login full-name info) "Update the stored user information for the user with nickname NICK. @@ -6008,12 +5752,6 @@ erc-user-input (defvar erc-command-regexp "^/\\([A-Za-z']+\\)\\(\\s-+.*\\|\\s-*\\)$" "Regular expression used for matching commands in ERC.") -(cl-defstruct erc-input - string insertp sendp) - -(cl-defstruct (erc--input-split (:include erc-input)) - lines cmdp) - (defun erc--discard-trailing-multiline-nulls (state) "Ensure last line of STATE's string is non-null. But only when `erc-send-whitespace-lines' is non-nil. STATE is @@ -6957,9 +6695,6 @@ erc-format-lag-time (t "")))) ;; erc-goodies is required at end of this file. -(declare-function erc-controls-strip "erc-goodies" (str)) - -(defvar tabbar--local-hlf) ;; FIXME when 29.1 is cut and `format-spec' is added to ELPA Compat, ;; remove the function invocations from the spec form below. @@ -7448,12 +7183,4 @@ erc-handle-irc-url (provide 'erc) -(require 'erc-backend) - -;; Deprecated. We might eventually stop requiring the goodies automatically. -;; IMPORTANT: This require must appear _after_ the above (provide 'erc) to -;; avoid a recursive require error when byte-compiling the entire package. -(require 'erc-goodies) -(require 'erc-networks) - ;;; erc.el ends here diff --git a/test/lisp/erc/erc-networks-tests.el b/test/lisp/erc/erc-networks-tests.el index 66a334b709..32bdfa11ff 100644 --- a/test/lisp/erc/erc-networks-tests.el +++ b/test/lisp/erc/erc-networks-tests.el @@ -20,7 +20,7 @@ ;;; Code: (require 'ert-x) ; cl-lib -(require 'erc-networks) +(require 'erc) (defun erc-networks-tests--create-dead-proc (&optional buf) (let ((p (start-process "true" (or buf (current-buffer)) "true"))) diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index b2ed29e80e..c88dd9888d 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -24,7 +24,6 @@ (require 'ert-x) (require 'erc) (require 'erc-ring) -(require 'erc-networks) (ert-deftest erc--read-time-period () (cl-letf (((symbol-function 'read-string) (lambda (&rest _) ""))) @@ -48,27 +47,6 @@ erc--read-time-period (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "1d"))) (should (equal (erc--read-time-period "foo: ") 86400)))) -(ert-deftest erc--meta--backend-dependencies () - (with-temp-buffer - (insert-file-contents-literally - (concat (file-name-sans-extension (symbol-file 'erc)) ".el")) - (let ((beg (search-forward ";; Defined in erc-backend")) - (end (search-forward "\n\n")) - vars) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - (with-syntax-table lisp-data-mode-syntax-table - (condition-case _ - (while (push (cadr (read (current-buffer))) vars)) - (end-of-file))))) - (should (= (point) end)) - (dolist (var vars) - (setq var (concat "\\_<" (symbol-name var) "\\_>")) - (ert-info (var) - (should (save-excursion (search-forward-regexp var nil t)))))))) - (ert-deftest erc-with-all-buffers-of-server () (let (proc-exnet proc-onet -- 2.37.3