aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/erc
diff options
context:
space:
mode:
authorF. Jason Park2021-07-12 03:44:28 -0700
committerAmin Bandali2022-11-23 19:56:31 -0500
commit4c4936fab2ecd97ff6e03e5cfe12def4626c718b (patch)
tree573e992298f17fd23709fddafb2979a49646b2d9 /lisp/erc
parentdc6ff142bc1c1a8596436e08ddbccb39d8fdcf39 (diff)
downloademacs-4c4936fab2ecd97ff6e03e5cfe12def4626c718b.tar.gz
emacs-4c4936fab2ecd97ff6e03e5cfe12def4626c718b.zip
Support local ERC modules in erc-mode buffers
* doc/misc/erc.texi: Mention local modules in Modules chapter. * etc/ERC-NEWS: Mention changes to `erc-update-modules'. * lisp/erc/erc.el (erc-migrate-modules): Add some missing mappings. (erc-modules): When a user removes a module, disable it and kill its local variable in all ERC buffers. (erc-update-modules): Move body of `erc-update-modules' to new internal function. (erc--update-modules): Add new function, a renamed and slightly modified version of `erc-update-modules'. Specifically, change return value from nil to a list of minor-mode commands for local modules. Use `custom-variable-p' to detect flavor. (erc--merge-local-modes): Add helper for finding local modules already active as minor modes in an ERC buffer. (erc-open): Replace `erc-update-modules' with `erc--update-modules'. Defer enabling of local modules via `erc--update-modules' until after buffer is initialized with other local vars. Also defer major-mode hooks so they can detect things like whether the buffer is a server or target buffer. Also ensure local module setup code can detect when `erc-open' was called with a non-nil `erc--server-reconnecting'. * lisp/erc/erc-common.el (erc--module-name-migrations, erc--features-to-modules, erc--modules-to-features): Add alists of old-to-new module names to support module-name migrations. (erc--assemble-toggle): Add new helper for constructing mode toggles, like `erc-sasl-enable'. (define-erc-modules): Defer to `erc--assemble-toggle' to create toggle commands. (erc--normalize-module-symbol): Add helper for `erc-migrate-modules'. * lisp/erc/erc-goodies.el: Require cl-lib. * test/lisp/erc/erc-tests.el (erc-migrate-modules, erc--update-modules): Add rudimentary unit tests asserting correct module-name mappings. (erc--merge-local-modes): Add test for helper. (define-erc-module--global, define-erc-module--local): Add tests asserting module-creation macro. (Bug#57955.)
Diffstat (limited to 'lisp/erc')
-rw-r--r--lisp/erc/erc-common.el82
-rw-r--r--lisp/erc/erc-goodies.el1
-rw-r--r--lisp/erc/erc.el104
3 files changed, 136 insertions, 51 deletions
diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el
index 23a19337986..a4046ba9b39 100644
--- a/lisp/erc/erc-common.el
+++ b/lisp/erc/erc-common.el
@@ -88,6 +88,65 @@
88 (contents "" :type string) 88 (contents "" :type string)
89 (tags '() :type list)) 89 (tags '() :type list))
90 90
91;; TODO move goodies modules here after 29 is released.
92(defconst erc--features-to-modules
93 '((erc-pcomplete completion pcomplete)
94 (erc-capab capab-identify)
95 (erc-join autojoin)
96 (erc-page page ctcp-page)
97 (erc-sound sound ctcp-sound)
98 (erc-stamp stamp timestamp)
99 (erc-services services nickserv))
100 "Migration alist mapping a library feature to module names.
101Keys need not be unique: a library may define more than one
102module. Sometimes a module's downcased alias will be its
103canonical name.")
104
105(defconst erc--modules-to-features
106 (let (pairs)
107 (pcase-dolist (`(,feature . ,names) erc--features-to-modules)
108 (dolist (name names)
109 (push (cons name feature) pairs)))
110 (nreverse pairs))
111 "Migration alist mapping a module's name to its home library feature.")
112
113(defconst erc--module-name-migrations
114 (let (pairs)
115 (pcase-dolist (`(,_ ,canonical . ,rest) erc--features-to-modules)
116 (dolist (obsolete rest)
117 (push (cons obsolete canonical) pairs)))
118 pairs)
119 "Association list of obsolete module names to canonical names.")
120
121(defun erc--normalize-module-symbol (symbol)
122 "Return preferred SYMBOL for `erc-modules'."
123 (setq symbol (intern (downcase (symbol-name symbol))))
124 (or (cdr (assq symbol erc--module-name-migrations)) symbol))
125
126(defun erc--assemble-toggle (localp name ablsym mode val body)
127 (let ((arg (make-symbol "arg")))
128 `(defun ,ablsym ,(if localp `(&optional ,arg) '())
129 ,(concat
130 (if val "Enable" "Disable")
131 " ERC " (symbol-name name) " mode."
132 (when localp
133 "\nWith ARG, do so in all buffers for the current connection."))
134 (interactive ,@(when localp '("p")))
135 ,@(if localp
136 `((when (derived-mode-p 'erc-mode)
137 (if ,arg
138 (erc-with-all-buffers-of-server erc-server-process nil
139 (,ablsym))
140 (setq ,mode ,val)
141 ,@body)))
142 `(,(if val
143 `(cl-pushnew ',(erc--normalize-module-symbol name)
144 erc-modules)
145 `(setq erc-modules (delq ',(erc--normalize-module-symbol name)
146 erc-modules)))
147 (setq ,mode ,val)
148 ,@body)))))
149
91(defmacro define-erc-module (name alias doc enable-body disable-body 150(defmacro define-erc-module (name alias doc enable-body disable-body
92 &optional local-p) 151 &optional local-p)
93 "Define a new minor mode using ERC conventions. 152 "Define a new minor mode using ERC conventions.
@@ -103,6 +162,13 @@ This will define a minor mode called erc-NAME-mode, possibly
103an alias erc-ALIAS-mode, as well as the helper functions 162an alias erc-ALIAS-mode, as well as the helper functions
104erc-NAME-enable, and erc-NAME-disable. 163erc-NAME-enable, and erc-NAME-disable.
105 164
165With LOCAL-P, these helpers take on an optional argument that,
166when non-nil, causes them to act on all buffers of a connection.
167This feature is mainly intended for interactive use and does not
168carry over to their respective minor-mode toggles. Beware that
169for global modules, these helpers and toggles all mutate
170`erc-modules'.
171
106Example: 172Example:
107 173
108 ;;;###autoload(autoload \\='erc-replace-mode \"erc-replace\") 174 ;;;###autoload(autoload \\='erc-replace-mode \"erc-replace\")
@@ -133,20 +199,8 @@ if ARG is omitted or nil.
133 (if ,mode 199 (if ,mode
134 (,enable) 200 (,enable)
135 (,disable))) 201 (,disable)))
136 (defun ,enable () 202 ,(erc--assemble-toggle local-p name enable mode t enable-body)
137 ,(format "Enable ERC %S mode." 203 ,(erc--assemble-toggle local-p name disable mode nil disable-body)
138 name)
139 (interactive)
140 (add-to-list 'erc-modules (quote ,name))
141 (setq ,mode t)
142 ,@enable-body)
143 (defun ,disable ()
144 ,(format "Disable ERC %S mode."
145 name)
146 (interactive)
147 (setq erc-modules (delq (quote ,name) erc-modules))
148 (setq ,mode nil)
149 ,@disable-body)
150 ,(when (and alias (not (eq name alias))) 204 ,(when (and alias (not (eq name alias)))
151 `(defalias 205 `(defalias
152 ',(intern 206 ',(intern
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index 59b5f01f236..1af83b58ba7 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -31,6 +31,7 @@
31 31
32;;; Imenu support 32;;; Imenu support
33 33
34(eval-when-compile (require 'cl-lib))
34(require 'erc-common) 35(require 'erc-common)
35 36
36(defvar erc-controls-highlight-regexp) 37(defvar erc-controls-highlight-regexp)
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 352f72e6178..384d92e624f 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1791,10 +1791,7 @@ buffer rather than a server buffer.")
1791 "Migrate old names of ERC modules to new ones." 1791 "Migrate old names of ERC modules to new ones."
1792 ;; modify `transforms' to specify what needs to be changed 1792 ;; modify `transforms' to specify what needs to be changed
1793 ;; each item is in the format '(old . new) 1793 ;; each item is in the format '(old . new)
1794 (let ((transforms '((pcomplete . completion)))) 1794 (delete-dups (mapcar #'erc--normalize-module-symbol mods)))
1795 (delete-dups
1796 (mapcar (lambda (m) (or (cdr (assoc m transforms)) m))
1797 mods))))
1798 1795
1799(defcustom erc-modules '(netsplit fill button match track completion readonly 1796(defcustom erc-modules '(netsplit fill button match track completion readonly
1800 networks ring autojoin noncommands irccontrols 1797 networks ring autojoin noncommands irccontrols
@@ -1813,9 +1810,16 @@ removed from the list will be disabled."
1813 (dolist (module erc-modules) 1810 (dolist (module erc-modules)
1814 (unless (member module val) 1811 (unless (member module val)
1815 (let ((f (intern-soft (format "erc-%s-mode" module)))) 1812 (let ((f (intern-soft (format "erc-%s-mode" module))))
1816 (when (and (fboundp f) (boundp f) (symbol-value f)) 1813 (when (and (fboundp f) (boundp f))
1817 (message "Disabling `erc-%s'" module) 1814 (when (symbol-value f)
1818 (funcall f 0)))))) 1815 (message "Disabling `erc-%s'" module)
1816 (funcall f 0))
1817 (unless (or (custom-variable-p f)
1818 (not (fboundp 'erc-buffer-filter)))
1819 (erc-buffer-filter (lambda ()
1820 (when (symbol-value f)
1821 (funcall f 0))
1822 (kill-local-variable f)))))))))
1819 (set sym val) 1823 (set sym val)
1820 ;; this test is for the case where erc hasn't been loaded yet 1824 ;; this test is for the case where erc hasn't been loaded yet
1821 (when (fboundp 'erc-update-modules) 1825 (when (fboundp 'erc-update-modules)
@@ -1873,27 +1877,23 @@ removed from the list will be disabled."
1873 :group 'erc) 1877 :group 'erc)
1874 1878
1875(defun erc-update-modules () 1879(defun erc-update-modules ()
1876 "Run this to enable erc-foo-mode for all modules in `erc-modules'." 1880 "Enable minor mode for every module in `erc-modules'.
1877 (let (req) 1881Except ignore all local modules, which were introduced in ERC 5.5."
1878 (dolist (mod erc-modules) 1882 (erc--update-modules)
1879 (setq req (concat "erc-" (symbol-name mod))) 1883 nil)
1880 (cond 1884
1881 ;; yuck. perhaps we should bring the filenames into sync? 1885(defun erc--update-modules ()
1882 ((string= req "erc-capab-identify") 1886 (let (local-modes)
1883 (setq req "erc-capab")) 1887 (dolist (module erc-modules local-modes)
1884 ((string= req "erc-completion") 1888 (require (or (alist-get module erc--modules-to-features)
1885 (setq req "erc-pcomplete")) 1889 (intern (concat "erc-" (symbol-name module))))
1886 ((string= req "erc-pcomplete") 1890 nil 'noerror) ; some modules don't have a corresponding feature
1887 (setq mod 'completion)) 1891 (let ((mode (intern-soft (concat "erc-" (symbol-name module) "-mode"))))
1888 ((string= req "erc-autojoin") 1892 (unless (and mode (fboundp mode))
1889 (setq req "erc-join"))) 1893 (error "`%s' is not a known ERC module" module))
1890 (condition-case nil 1894 (if (custom-variable-p mode)
1891 (require (intern req)) 1895 (funcall mode 1)
1892 (error nil)) 1896 (push mode local-modes))))))
1893 (let ((sym (intern-soft (concat "erc-" (symbol-name mod) "-mode"))))
1894 (if (fboundp sym)
1895 (funcall sym 1)
1896 (error "`%s' is not a known ERC module" mod))))))
1897 1897
1898(defun erc-setup-buffer (buffer) 1898(defun erc-setup-buffer (buffer)
1899 "Consults `erc-join-buffer' to find out how to display `BUFFER'." 1899 "Consults `erc-join-buffer' to find out how to display `BUFFER'."
@@ -1924,6 +1924,24 @@ removed from the list will be disabled."
1924 (display-buffer buffer) 1924 (display-buffer buffer)
1925 (switch-to-buffer buffer))))) 1925 (switch-to-buffer buffer)))))
1926 1926
1927(defun erc--merge-local-modes (new-modes old-vars)
1928 "Return a cons of two lists, each containing local-module modes.
1929In the first, put modes to be enabled in a new ERC buffer by
1930calling their associated functions. In the second, put modes to
1931be marked as disabled by setting their associated variables to
1932nil."
1933 (if old-vars
1934 (let ((out (list (reverse new-modes))))
1935 (pcase-dolist (`(,k . ,v) old-vars)
1936 (when (and (string-prefix-p "erc-" (symbol-name k))
1937 (string-suffix-p "-mode" (symbol-name k)))
1938 (if v
1939 (cl-pushnew k (car out))
1940 (setf (car out) (delq k (car out)))
1941 (cl-pushnew k (cdr out)))))
1942 (cons (nreverse (car out)) (nreverse (cdr out))))
1943 (list new-modes)))
1944
1927(defun erc-open (&optional server port nick full-name 1945(defun erc-open (&optional server port nick full-name
1928 connect passwd tgt-list channel process 1946 connect passwd tgt-list channel process
1929 client-certificate user id) 1947 client-certificate user id)
@@ -1951,18 +1969,25 @@ Returns the buffer for the given server or channel."
1951 (let* ((target (and channel (erc--target-from-string channel))) 1969 (let* ((target (and channel (erc--target-from-string channel)))
1952 (buffer (erc-get-buffer-create server port nil target id)) 1970 (buffer (erc-get-buffer-create server port nil target id))
1953 (old-buffer (current-buffer)) 1971 (old-buffer (current-buffer))
1954 old-point 1972 (old-vars (and (not connect) (buffer-local-variables)))
1973 (old-recon-count erc-server-reconnect-count)
1974 (old-point nil)
1975 (delayed-modules nil)
1955 (continued-session (and erc--server-reconnecting 1976 (continued-session (and erc--server-reconnecting
1956 (with-suppressed-warnings 1977 (with-suppressed-warnings
1957 ((obsolete erc-reuse-buffers)) 1978 ((obsolete erc-reuse-buffers))
1958 erc-reuse-buffers)))) 1979 erc-reuse-buffers))))
1959 (when connect (run-hook-with-args 'erc-before-connect server port nick)) 1980 (when connect (run-hook-with-args 'erc-before-connect server port nick))
1960 (erc-update-modules)
1961 (set-buffer buffer) 1981 (set-buffer buffer)
1962 (setq old-point (point)) 1982 (setq old-point (point))
1963 (let ((old-recon-count erc-server-reconnect-count)) 1983 (setq delayed-modules
1964 (erc-mode) 1984 (erc--merge-local-modes (erc--update-modules)
1965 (setq erc-server-reconnect-count old-recon-count)) 1985 (or erc--server-reconnecting old-vars)))
1986
1987 (delay-mode-hooks (erc-mode))
1988
1989 (setq erc-server-reconnect-count old-recon-count)
1990
1966 (when (setq erc-server-connected (not connect)) 1991 (when (setq erc-server-connected (not connect))
1967 (setq erc-server-announced-name 1992 (setq erc-server-announced-name
1968 (buffer-local-value 'erc-server-announced-name old-buffer))) 1993 (buffer-local-value 'erc-server-announced-name old-buffer)))
@@ -2019,14 +2044,21 @@ Returns the buffer for the given server or channel."
2019 (setq erc-session-client-certificate client-certificate) 2044 (setq erc-session-client-certificate client-certificate)
2020 (setq erc-networks--id 2045 (setq erc-networks--id
2021 (if connect 2046 (if connect
2022 (or (and continued-session 2047 (or (and erc--server-reconnecting
2023 (buffer-local-value 'erc-networks--id old-buffer)) 2048 (alist-get 'erc-networks--id erc--server-reconnecting))
2024 (and id (erc-networks--id-create id))) 2049 (and id (erc-networks--id-create id)))
2025 (buffer-local-value 'erc-networks--id old-buffer))) 2050 (buffer-local-value 'erc-networks--id old-buffer)))
2026 ;; debug output buffer 2051 ;; debug output buffer
2027 (setq erc-dbuf 2052 (setq erc-dbuf
2028 (when erc-log-p 2053 (when erc-log-p
2029 (get-buffer-create (concat "*ERC-DEBUG: " server "*")))) 2054 (get-buffer-create (concat "*ERC-DEBUG: " server "*"))))
2055
2056 (erc-determine-parameters server port nick full-name user passwd)
2057
2058 (save-excursion (run-mode-hooks))
2059 (dolist (mod (car delayed-modules)) (funcall mod +1))
2060 (dolist (var (cdr delayed-modules)) (set var nil))
2061
2030 ;; set up prompt 2062 ;; set up prompt
2031 (unless continued-session 2063 (unless continued-session
2032 (goto-char (point-max)) 2064 (goto-char (point-max))
@@ -2038,8 +2070,6 @@ Returns the buffer for the given server or channel."
2038 (erc-display-prompt) 2070 (erc-display-prompt)
2039 (goto-char (point-max))) 2071 (goto-char (point-max)))
2040 2072
2041 (erc-determine-parameters server port nick full-name user passwd)
2042
2043 ;; Saving log file on exit 2073 ;; Saving log file on exit
2044 (run-hook-with-args 'erc-connect-pre-hook buffer) 2074 (run-hook-with-args 'erc-connect-pre-hook buffer)
2045 2075