aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorF. Jason Park2023-04-30 07:12:56 -0700
committerF. Jason Park2023-05-05 17:18:01 -0700
commit35dd1ade7f1e583f736e6f707343402fe868daec (patch)
treebc1a0022b297fd89c0acea30172e95ed5a9f0f98 /lisp
parent3a5a6fce957468be5ef0a8ac76fec8507c3e4e99 (diff)
downloademacs-35dd1ade7f1e583f736e6f707343402fe868daec.tar.gz
emacs-35dd1ade7f1e583f736e6f707343402fe868daec.zip
Preprocess prompt input linewise in ERC
* etc/ERC-NEWS: Mention revised role of `erc-pre-send-functions' relative to line splitting. * lisp/erc/erc-common.el (erc-input): Add new slot `refoldp' to allow `erc-pre-send-functions' members to indicate that splitting should occur a second time after all members have had their say. (erc--input-split): Specify some defaults for overridden slots and explicitly declare some types for good measure. * lisp/erc/erc-goodies.el (erc-noncommands-mode, erc-noncommands-enable, erc-noncommands-disable): Replace `erc-pre-send-functions' with `erc--input-review-functions'. * lisp/erc/erc-ring.el (erc-ring-enable, erc-ring-disable, erc-ring-mode): Subscribe to `erc--input-review-functions' instead of `erc-pre-send-functions' for `erc--add-to-input-ring'. * lisp/erc/erc.el (erc-pre-send-functions): Note some nuances regarding line splitting in doc string and note that a new slot is available. (erc--pre-send-split-functions, erc--input-review-functions): Rename former to latter, while also obsoleting. Remove large comment. Add new default member `erc--run-input-validation-checks'. (erc-send-modify-hook): Replace the obsolete `erc-send-pre-hook' and `erc-send-this' with `erc-pre-send-functions' in doc string. (erc--check-prompt-input-for-excess-lines): Don't trim trailing blanks. Rework to also report overages in characters as well as lines. (erc--run-input-validation-hooks): New function to adapt an `erc--input-split' object to `erc--check-prompt-input-functions'. (erc-send-current-line): Run `erc--input-review-functions' in place of the validation hooks they've subsumed. Call `erc--send-input-lines' instead of the now retired but not deprecated `erc-send-input'. (erc--run-send-hooks, erc--send-input-lines): New functions that together form an alternate version of `erc-send-input'. They operate on input linewise but make accommodations for older interfaces. * test/lisp/erc/erc-tests.el (erc-ring-previous-command): Replace `erc-pre-send-functions' with `erc--input-review-functions'. (erc-tests--with-process-input-spy): Shadow `erc--input-review-functions'. (erc-check-prompt-input-for-excess-lines): Don't expect trailing blanks to be trimmed. (erc--run-send-hooks): New test. (Bug#62947)
Diffstat (limited to 'lisp')
-rw-r--r--lisp/erc/erc-common.el14
-rw-r--r--lisp/erc/erc-goodies.el5
-rw-r--r--lisp/erc/erc-ring.el4
-rw-r--r--lisp/erc/erc.el135
4 files changed, 116 insertions, 42 deletions
diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el
index 708cdb0c422..86d78768374 100644
--- a/lisp/erc/erc-common.el
+++ b/lisp/erc/erc-common.el
@@ -30,8 +30,10 @@
30(defvar erc--casemapping-rfc1459-strict) 30(defvar erc--casemapping-rfc1459-strict)
31(defvar erc-channel-users) 31(defvar erc-channel-users)
32(defvar erc-dbuf) 32(defvar erc-dbuf)
33(defvar erc-insert-this)
33(defvar erc-log-p) 34(defvar erc-log-p)
34(defvar erc-modules) 35(defvar erc-modules)
36(defvar erc-send-this)
35(defvar erc-server-process) 37(defvar erc-server-process)
36(defvar erc-server-users) 38(defvar erc-server-users)
37(defvar erc-session-server) 39(defvar erc-session-server)
@@ -49,10 +51,14 @@
49(declare-function widget-type "wid-edit" (widget)) 51(declare-function widget-type "wid-edit" (widget))
50 52
51(cl-defstruct erc-input 53(cl-defstruct erc-input
52 string insertp sendp) 54 string insertp sendp refoldp)
53 55
54(cl-defstruct (erc--input-split (:include erc-input)) 56(cl-defstruct (erc--input-split (:include erc-input
55 lines cmdp) 57 (string :read-only)
58 (insertp erc-insert-this)
59 (sendp erc-send-this)))
60 (lines nil :type (list-of string))
61 (cmdp nil :type boolean))
56 62
57(cl-defstruct (erc-server-user (:type vector) :named) 63(cl-defstruct (erc-server-user (:type vector) :named)
58 ;; User data 64 ;; User data
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index 6235de5f1c0..cc60ba0018b 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -338,8 +338,9 @@ does not appear in the ERC buffer after the user presses ENTER.")
338 "This mode distinguishes non-commands. 338 "This mode distinguishes non-commands.
339Commands listed in `erc-insert-this' know how to display 339Commands listed in `erc-insert-this' know how to display
340themselves." 340themselves."
341 ((add-hook 'erc-pre-send-functions #'erc-send-distinguish-noncommands)) 341 ((add-hook 'erc--input-review-functions #'erc-send-distinguish-noncommands))
342 ((remove-hook 'erc-pre-send-functions #'erc-send-distinguish-noncommands))) 342 ((remove-hook 'erc--input-review-functions
343 #'erc-send-distinguish-noncommands)))
343 344
344(defun erc-send-distinguish-noncommands (state) 345(defun erc-send-distinguish-noncommands (state)
345 "If STR is an ERC non-command, set `insertp' in STATE to nil." 346 "If STR is an ERC non-command, set `insertp' in STATE to nil."
diff --git a/lisp/erc/erc-ring.el b/lisp/erc/erc-ring.el
index 2451ac56f6f..4534e913204 100644
--- a/lisp/erc/erc-ring.el
+++ b/lisp/erc/erc-ring.el
@@ -46,10 +46,10 @@
46(define-erc-module ring nil 46(define-erc-module ring nil
47 "Stores input in a ring so that previous commands and messages can 47 "Stores input in a ring so that previous commands and messages can
48be recalled using M-p and M-n." 48be recalled using M-p and M-n."
49 ((add-hook 'erc-pre-send-functions #'erc-add-to-input-ring) 49 ((add-hook 'erc--input-review-functions #'erc-add-to-input-ring 90)
50 (define-key erc-mode-map "\M-p" #'erc-previous-command) 50 (define-key erc-mode-map "\M-p" #'erc-previous-command)
51 (define-key erc-mode-map "\M-n" #'erc-next-command)) 51 (define-key erc-mode-map "\M-n" #'erc-next-command))
52 ((remove-hook 'erc-pre-send-functions #'erc-add-to-input-ring) 52 ((remove-hook 'erc--input-review-functions #'erc-add-to-input-ring)
53 (define-key erc-mode-map "\M-p" #'undefined) 53 (define-key erc-mode-map "\M-p" #'undefined)
54 (define-key erc-mode-map "\M-n" #'undefined))) 54 (define-key erc-mode-map "\M-n" #'undefined)))
55 55
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index bc2285a5560..72ec8134eab 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1094,34 +1094,40 @@ The struct has three slots:
1094 1094
1095 `string': The current input string. 1095 `string': The current input string.
1096 `insertp': Whether the string should be inserted into the erc buffer. 1096 `insertp': Whether the string should be inserted into the erc buffer.
1097 `sendp': Whether the string should be sent to the irc server." 1097 `sendp': Whether the string should be sent to the irc server.
1098 `refoldp': Whether the string should be re-split per protocol limits.
1099
1100This hook runs after protocol line splitting has taken place, so
1101the value of `string' is originally \"pre-filled\". If you need
1102ERC to refill the entire payload before sending it, set the
1103`refoldp' slot to a non-nil value. Preformatted text and encoded
1104subprotocols should probably be handled manually."
1098 :group 'erc 1105 :group 'erc
1099 :type 'hook 1106 :type 'hook
1100 :version "27.1") 1107 :version "27.1")
1101 1108
1102;; This is being auditioned for possible exporting (as a custom hook 1109(define-obsolete-variable-alias 'erc--pre-send-split-functions
1103;; option). Likewise for (public versions of) `erc--input-split' and 1110 'erc--input-review-functions "30.1")
1104;; `erc--discard-trailing-multiline-nulls'. If unneeded, we'll just 1111(defvar erc--input-review-functions '(erc--discard-trailing-multiline-nulls
1105;; run the latter on the input after `erc-pre-send-functions', and 1112 erc--split-lines
1106;; remove this hook and the struct completely. IOW, if you need this, 1113 erc--run-input-validation-checks)
1107;; please say so. 1114 "Special hook for reviewing and modifying prompt input.
1108 1115ERC runs this before clearing the prompt and before running any
1109(defvar erc--pre-send-split-functions '(erc--discard-trailing-multiline-nulls 1116send-related hooks, such as `erc-pre-send-functions'. Thus, it's
1110 erc--split-lines) 1117quite \"safe\" to bail out of this hook with a `user-error', if
1111 "Special hook for modifying individual lines in multiline prompt input. 1118necessary. The hook's members are called with one argument, an
1112The functions are called with one argument, an `erc--input-split' 1119`erc--input-split' struct, which they can optionally modify.
1113struct, which they can optionally modify.
1114 1120
1115The struct has five slots: 1121The struct has five slots:
1116 1122
1117 `string': the input string delivered by `erc-pre-send-functions' 1123 `string': the original input as a read-only reference
1118 `insertp': whether to insert the lines into the buffer 1124 `insertp': same as in `erc-pre-send-functions'
1119 `sendp': whether the lines should be sent to the IRC server 1125 `sendp': same as in `erc-pre-send-functions'
1126 `refoldp': same as in `erc-pre-send-functions'
1120 `lines': a list of lines to be sent, each one a `string' 1127 `lines': a list of lines to be sent, each one a `string'
1121 `cmdp': whether to interpret input as a command, like /ignore 1128 `cmdp': whether to interpret input as a command, like /ignore
1122 1129
1123The `string' field is effectively read-only. When `cmdp' is 1130When `cmdp' is non-nil, all but the first line will be discarded.")
1124non-nil, all but the first line will be discarded.")
1125 1131
1126(defvar erc-insert-this t 1132(defvar erc-insert-this t
1127 "Insert the text into the target buffer or not. 1133 "Insert the text into the target buffer or not.
@@ -1163,8 +1169,8 @@ preserve point if needed."
1163 1169
1164(defcustom erc-send-modify-hook nil 1170(defcustom erc-send-modify-hook nil
1165 "Sending hook for functions that will change the text's appearance. 1171 "Sending hook for functions that will change the text's appearance.
1166This hook is called just after `erc-send-pre-hook' when the values 1172ERC runs this just after `erc-pre-send-functions' if its shared
1167of `erc-send-this' and `erc-insert-this' are both t. 1173`erc-input' object's `sendp' and `insertp' slots remain non-nil.
1168While this hook is run, narrowing is in effect and `current-buffer' is 1174While this hook is run, narrowing is in effect and `current-buffer' is
1169the buffer where the text got inserted. 1175the buffer where the text got inserted.
1170 1176
@@ -6106,16 +6112,18 @@ is empty or consists of one or more spaces, tabs, or form-feeds."
6106(defun erc--check-prompt-input-for-excess-lines (_ lines) 6112(defun erc--check-prompt-input-for-excess-lines (_ lines)
6107 "Return non-nil when trying to send too many LINES." 6113 "Return non-nil when trying to send too many LINES."
6108 (when erc-inhibit-multiline-input 6114 (when erc-inhibit-multiline-input
6109 ;; Assume `erc--discard-trailing-multiline-nulls' is set to run 6115 (let ((max (if (eq erc-inhibit-multiline-input t)
6110 (let ((reversed (seq-drop-while #'string-empty-p (reverse lines)))
6111 (max (if (eq erc-inhibit-multiline-input t)
6112 2 6116 2
6113 erc-inhibit-multiline-input)) 6117 erc-inhibit-multiline-input))
6114 (seen 0) 6118 (seen 0)
6115 msg) 6119 last msg)
6116 (while (and (pop reversed) (< (cl-incf seen) max))) 6120 (while (and lines (setq last (pop lines)) (< (cl-incf seen) max)))
6117 (when (= seen max) 6121 (when (= seen max)
6118 (setq msg (format "(exceeded by %d)" (1+ (length reversed)))) 6122 (push last lines)
6123 (setq msg
6124 (format "-- exceeded by %d (%d chars)"
6125 (length lines)
6126 (apply #'+ (mapcar #'length lines))))
6119 (unless (and erc-ask-about-multiline-input 6127 (unless (and erc-ask-about-multiline-input
6120 (y-or-n-p (concat "Send input " msg "?"))) 6128 (y-or-n-p (concat "Send input " msg "?")))
6121 (concat "Too many lines " msg)))))) 6129 (concat "Too many lines " msg))))))
@@ -6155,7 +6163,17 @@ is empty or consists of one or more spaces, tabs, or form-feeds."
6155Called with latest input string submitted by user and the list of 6163Called with latest input string submitted by user and the list of
6156lines produced by splitting it. If any member function returns 6164lines produced by splitting it. If any member function returns
6157non-nil, processing is abandoned and input is left untouched. 6165non-nil, processing is abandoned and input is left untouched.
6158When the returned value is a string, pass it to `erc-error'.") 6166When the returned value is a string, ERC passes it to `erc-error'.")
6167
6168(defun erc--run-input-validation-checks (state)
6169 "Run input checkers from STATE, an `erc--input-split' object."
6170 (when-let ((msg (run-hook-with-args-until-success
6171 'erc--check-prompt-input-functions
6172 (erc--input-split-string state)
6173 (erc--input-split-lines state))))
6174 (unless (stringp msg)
6175 (setq msg (format "Input error: %S" msg)))
6176 (user-error msg)))
6159 6177
6160(defun erc-send-current-line () 6178(defun erc-send-current-line ()
6161 "Parse current line and send it to IRC." 6179 "Parse current line and send it to IRC."
@@ -6170,12 +6188,15 @@ When the returned value is a string, pass it to `erc-error'.")
6170 (eolp)) 6188 (eolp))
6171 (expand-abbrev)) 6189 (expand-abbrev))
6172 (widen) 6190 (widen)
6173 (if-let* ((str (erc-user-input)) 6191 (let* ((str (erc-user-input))
6174 (msg (run-hook-with-args-until-success 6192 (state (make-erc--input-split
6175 'erc--check-prompt-input-functions str 6193 :string str
6176 (split-string str erc--input-line-delim-regexp)))) 6194 :insertp erc-insert-this
6177 (when (stringp msg) 6195 :sendp erc-send-this
6178 (erc-error msg)) 6196 :lines (split-string
6197 str erc--input-line-delim-regexp)
6198 :cmdp (string-match erc-command-regexp str))))
6199 (run-hook-with-args 'erc--input-review-functions state)
6179 (let ((inhibit-read-only t) 6200 (let ((inhibit-read-only t)
6180 (old-buf (current-buffer))) 6201 (old-buf (current-buffer)))
6181 (progn ; unprogn this during next major surgery 6202 (progn ; unprogn this during next major surgery
@@ -6183,7 +6204,7 @@ When the returned value is a string, pass it to `erc-error'.")
6183 ;; Kill the input and the prompt 6204 ;; Kill the input and the prompt
6184 (delete-region erc-input-marker (erc-end-of-input-line)) 6205 (delete-region erc-input-marker (erc-end-of-input-line))
6185 (unwind-protect 6206 (unwind-protect
6186 (erc-send-input str 'skip-ws-chk) 6207 (erc--send-input-lines (erc--run-send-hooks state))
6187 ;; Fix the buffer if the command didn't kill it 6208 ;; Fix the buffer if the command didn't kill it
6188 (when (buffer-live-p old-buf) 6209 (when (buffer-live-p old-buf)
6189 (with-current-buffer old-buf 6210 (with-current-buffer old-buf
@@ -6223,6 +6244,52 @@ an `erc--input-split' object."
6223 (setf (erc--input-split-lines state) 6244 (setf (erc--input-split-lines state)
6224 (mapcan #'erc--split-line (erc--input-split-lines state))))) 6245 (mapcan #'erc--split-line (erc--input-split-lines state)))))
6225 6246
6247(defun erc--run-send-hooks (lines-obj)
6248 "Run send-related hooks that operate on the entire prompt input.
6249Sequester some of the back and forth involved in honoring old
6250interfaces, such as the reconstituting and re-splitting of
6251multiline input. Optionally readjust lines to protocol length
6252limits and pad empty ones, knowing full well that additional
6253processing may still corrupt messages before they reach the send
6254queue. Expect LINES-OBJ to be an `erc--input-split' object."
6255 (when (or erc-send-pre-hook erc-pre-send-functions)
6256 (with-suppressed-warnings ((lexical str) (obsolete erc-send-this))
6257 (defvar str) ; see note in string `erc-send-input'.
6258 (let* ((str (string-join (erc--input-split-lines lines-obj) "\n"))
6259 (erc-send-this (erc--input-split-sendp lines-obj))
6260 (erc-insert-this (erc--input-split-insertp lines-obj))
6261 (state (progn
6262 ;; This may change `str' and `erc-*-this'.
6263 (run-hook-with-args 'erc-send-pre-hook str)
6264 (make-erc-input :string str
6265 :insertp erc-insert-this
6266 :sendp erc-send-this))))
6267 (run-hook-with-args 'erc-pre-send-functions state)
6268 (setf (erc--input-split-sendp lines-obj) (erc-input-sendp state)
6269 (erc--input-split-insertp lines-obj) (erc-input-insertp state)
6270 ;; See note in test of same name re trailing newlines.
6271 (erc--input-split-lines lines-obj)
6272 (cl-nsubst " " "" (split-string (erc-input-string state)
6273 erc--input-line-delim-regexp)
6274 :test #'equal))
6275 (when (erc-input-refoldp state)
6276 (erc--split-lines lines-obj)))))
6277 (when (and (erc--input-split-cmdp lines-obj)
6278 (cdr (erc--input-split-lines lines-obj)))
6279 (user-error "Multiline command detected" ))
6280 lines-obj)
6281
6282(defun erc--send-input-lines (lines-obj)
6283 "Send lines in `erc--input-split-lines' object LINES-OBJ."
6284 (when (erc--input-split-sendp lines-obj)
6285 (dolist (line (erc--input-split-lines lines-obj))
6286 (unless (erc--input-split-cmdp lines-obj)
6287 (when (erc--input-split-insertp lines-obj)
6288 (erc-display-msg line)))
6289 (erc-process-input-line (concat line "\n")
6290 (null erc-flood-protect)
6291 (not (erc--input-split-cmdp lines-obj))))))
6292
6226(defun erc-send-input (input &optional skip-ws-chk) 6293(defun erc-send-input (input &optional skip-ws-chk)
6227 "Treat INPUT as typed in by the user. 6294 "Treat INPUT as typed in by the user.
6228It is assumed that the input and the prompt is already deleted. 6295It is assumed that the input and the prompt is already deleted.