From 5c3a1e966876d8d25e3916c0cde21d387e995014 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Sun, 15 Oct 2023 17:22:22 -0700 Subject: [PATCH 2/2] [5.6] Restore missing metadata props in erc-display-line * etc/ERC-NEWS: Mention `erc-display-message' as favored means of inserting messages. * lisp/erc/erc-stamp.el (erc-stamp--current-time): Use an existing `erc-ts' text property, when present, for the current message time. * lisp/erc/erc.el (erc-display-line): Update doc string. Copy `erc--msg-props' hash table when inserting a message in multiple buffers. At present, only `erc-server-QUIT' uses this facility. Also, improve readability with at most one recursive call for the fall-through case. (erc-display-message): Update doc string. * test/lisp/erc/erc-scenarios-display-message.el: New file. * test/lisp/erc/erc-tests.el (erc-display-line): New test. * test/lisp/erc/resources/base/display-message/multibuf.eld: New test data. (Bug#60936) --- etc/ERC-NEWS | 11 +++ lisp/erc/erc-stamp.el | 4 +- lisp/erc/erc.el | 67 +++++++++++-------- .../lisp/erc/erc-scenarios-display-message.el | 64 ++++++++++++++++++ test/lisp/erc/erc-tests.el | 62 +++++++++++++++++ .../base/display-message/multibuf.eld | 45 +++++++++++++ 6 files changed, 224 insertions(+), 29 deletions(-) create mode 100644 test/lisp/erc/erc-scenarios-display-message.el create mode 100644 test/lisp/erc/resources/base/display-message/multibuf.eld diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS index 2e56539f210..404d735b9f6 100644 --- a/etc/ERC-NEWS +++ b/etc/ERC-NEWS @@ -288,6 +288,17 @@ ERC also provisionally reserves the same depth interval for continue to modify non-ERC hooks locally whenever possible, especially in new code. +*** ERC strongly favors 'erc-display-message' for message insertion. +Although less common these days, folks still sometimes resort to using +the insertion function 'erc-display-line' because it's admittedly less +awkward than the supposedly higher level 'erc-display-message'. Thus, +ancient patterns, like preformatting text with 'erc-make-notice', +still occasionally appear in newer code. However, beginning in ERC +5.6, certain preparatory business necessary for the eventual move to a +richer UI has taken up residence in 'erc-display-message'. If you +find this development disturbing, by all means voice your concerns on +the tracker. (Patches for user-friendly wrappers are most welcome.) + *** ERC now manages timestamp-related properties a bit differently. For starters, the 'cursor-sensor-functions' text property is absent by default unless the option 'erc-echo-timestamps' is already enabled on diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el index 394643c03cb..57fd7f39e50 100644 --- a/lisp/erc/erc-stamp.el +++ b/lisp/erc/erc-stamp.el @@ -219,7 +219,9 @@ erc-stamp--current-time (erc-compat--current-lisp-time)) (cl-defmethod erc-stamp--current-time :around () - (or erc-stamp--current-time (cl-call-next-method))) + (or erc-stamp--current-time + (and erc--msg-props (gethash 'erc-ts erc--msg-props)) + (cl-call-next-method))) (defvar erc-stamp--skip nil "Non-nil means inhibit `erc-add-timestamp' completely.") diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 5bf6496e926..7edf735eb43 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -3092,36 +3092,46 @@ erc-is-valid-nick-p (string-match (concat "\\`" erc-valid-nick-regexp "\\'") nick)) (defun erc-display-line (string &optional buffer) - "Display STRING in the ERC BUFFER. -All screen output must be done through this function. If BUFFER is nil -or omitted, the default ERC buffer for the `erc-session-server' is used. -The BUFFER can be an actual buffer, a list of buffers, `all' or `active'. -If BUFFER = `all', the string is displayed in all the ERC buffers for the -current session. `active' means the current active buffer -\(`erc-active-buffer'). If the buffer can't be resolved, the current -buffer is used. `erc-display-line-1' is used to display STRING. - -If STRING is nil, the function does nothing." - (let (new-bufs) + "Insert STRING in BUFFER. +Expect BUFFER to be a live `erc-mode' buffer, a list of such +buffers, or the symbols `all' or `active'. If `all', insert +STRING in all buffers for the current session. If `active', +defer to the function `erc-active-buffer', which may return the +session's server buffer if the previously active buffer has been +killed. If BUFFER is nil or a network process, pretend it's set +to the appropriate server buffer. Otherwise, use the current +buffer. + +In most cases, expect to be called from a higher-level insertion +function, like `erc-display-message', especially when modules +should consider STRING as a candidate for formatting with +indentation, fontification, timestamping, etc. Otherwise, allow +built-in modules to ignore STRING, which may make it appear +incongruous in situ (unless anticipated by third-party hook +members or otherwise preformatted)." + (let (seen msg-props) (dolist (buf (cond ((bufferp buffer) (list buffer)) - ((listp buffer) buffer) + ((consp buffer) + (setq msg-props erc--msg-props) + buffer) ((processp buffer) (list (process-buffer buffer))) ((eq 'all buffer) ;; Hmm, or all of the same session server? (erc-buffer-list nil erc-server-process)) - ((and (eq 'active buffer) (erc-active-buffer)) - (list (erc-active-buffer))) + ((and-let* (((eq 'active buffer)) + (b (erc-active-buffer))) + (list b))) ((erc-server-buffer-live-p) (list (process-buffer erc-server-process))) (t (list (current-buffer))))) (when (buffer-live-p buf) + (when msg-props + (setq erc--msg-props (copy-hash-table msg-props))) (erc-display-line-1 string buf) - (push buf new-bufs))) - (when (null new-bufs) - (erc-display-line-1 string (if (erc-server-buffer-live-p) - (process-buffer erc-server-process) - (current-buffer)))))) + (setq seen t))) + (unless (or seen (null buffer)) + (erc-display-line string nil)))) (defvar erc--compose-text-properties nil "Non-nil when `erc-put-text-property' defers to `erc--merge-prop'.") @@ -3432,14 +3442,15 @@ erc-display-message Insert MSG or text derived from MSG into an ERC buffer, possibly after applying formatting by way of either a `format-spec' known to a message-catalog entry or a TYPE known to a specialized -string handler. Additionally, derive internal metadata, faces, -and other text properties from the various overloaded parameters, -such as PARSED, when it's an `erc-response' object, and MSG, when -it's a key (symbol) for a \"message catalog\" entry. Expect -ARGS, when applicable, to be `format-spec' args known to such an -entry, and TYPE, when non-nil, to be a symbol handled by +string handler. Additionally, derive metadata, faces, and other +text properties from the various overloaded parameters, such as +PARSED, when it's an `erc-response' object, and MSG, when it's a +key (symbol) for a \"message catalog\" entry. Expect ARGS, when +applicable, to be `format-spec' args known to such an entry, and +TYPE, when non-nil, to be a symbol handled by `erc-display-message-highlight' (necessarily accompanied by a -string MSG). +string MSG). Expect BUFFER to be among the sort accepted by the +function `erc-display-line'. When TYPE is a list of symbols, call handlers from left to right without influencing how they behave when encountering existing @@ -3455,8 +3466,8 @@ erc-display-message `erc-display-line' when it's important that insert hooks treat MSG in a manner befitting messages received from a server. That is, expect to process most nontrivial informational messages, for -which PARSED is typically nil, when the caller desires -buttonizing and other effects." +which PARSED is typically nil, when the caller desires the +inserted message to feature buttonizing and other effects." (let ((string (if (symbolp msg) (apply #'erc-format-message msg args) msg)) diff --git a/test/lisp/erc/erc-scenarios-display-message.el b/test/lisp/erc/erc-scenarios-display-message.el new file mode 100644 index 00000000000..51bdf305ad5 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-display-message.el @@ -0,0 +1,64 @@ +;;; erc-scenarios-display-message.el --- erc-display-message -*- lexical-binding: t -*- + +;; Copyright (C) 2023 Free Software Foundation, Inc. + +;; 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 . + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-display-message--multibuf () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/display-message") + (dumb-server (erc-d-run "localhost" t 'multibuf)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (erc-modules (cons 'fill-wrap erc-modules)) + (erc-autojoin-channels-alist '((foonet "#chan"))) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (funcall expect 10 "debug mode"))) + + (ert-info ("User dummy is a member of #chan") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 10 "dummy"))) + + (ert-info ("Dummy's QUIT notice in query contains metadata props") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "dummy")) + (funcall expect 10 " hi") + (funcall expect 10 "*** dummy (~u@rdjcgiwfuwqmc.irc) has quit") + (should (eq 'QUIT (get-text-property (match-beginning 0) 'erc-msg))))) + + (ert-info ("Dummy's QUIT notice in #chan contains metadata props") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 10 "*** dummy (~u@rdjcgiwfuwqmc.irc) has quit") + (should (eq 'QUIT (get-text-property (match-beginning 0) 'erc-msg))))) + + (erc-cmd-QUIT ""))) + +(eval-when-compile (require 'erc-join)) + +;;; erc-scenarios-display-message.el ends here diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index 4f4662f5075..b35afaa552f 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -1938,6 +1938,68 @@ erc-format-privmessage 2 5 (erc-speaker "Bob" font-lock-face erc-nick-default-face) 5 12 (font-lock-face erc-default-face)))))) +(ert-deftest erc-display-line () + (erc-tests--send-prep) + (erc-tests--set-fake-server-process "sleep" "1") + (setq erc-networks--id (erc-networks--id-create 'foonet)) + + (let ((server-buffer (current-buffer)) + (spam-buffer (save-excursion (erc--open-target "#spam"))) + (chan-buffer (save-excursion (erc--open-target "#chan"))) + calls) + (cl-letf (((symbol-function 'erc-display-line-1) + (lambda (&rest r) (push (cons 'line-1 r) calls)))) + + (with-current-buffer chan-buffer + + (ert-info ("Null `buffer' routes to live server-buffer") + (erc-display-line "null" nil) + (should (equal (pop calls) `(line-1 "null" ,server-buffer))) + (should-not calls)) + + (ert-info ("Cons `buffer' routes to live members") + ;; Copies a let-bound `erc--msg-props' before mutating. + (let* ((table (map-into '(erc-msg msg) 'hash-table)) + (erc--msg-props table)) + (erc-display-line "cons" (list server-buffer spam-buffer)) + (should-not (eq table erc--msg-props))) + (should (equal (pop calls) `(line-1 "cons" ,spam-buffer))) + (should (equal (pop calls) `(line-1 "cons" ,server-buffer))) + (should-not calls)) + + (ert-info ("Variant `all' inserts in all session buffers") + (erc-display-line "all" 'all) + (should (equal (pop calls) `(line-1 "all" ,chan-buffer))) + (should (equal (pop calls) `(line-1 "all" ,spam-buffer))) + (should (equal (pop calls) `(line-1 "all" ,server-buffer))) + (should-not calls)) + + (ert-info ("Variant `active' routes to active buffer if alive") + (should (eq chan-buffer (erc-with-server-buffer erc-active-buffer))) + (erc-set-active-buffer spam-buffer) + (erc-display-line "act" 'active) + (should (equal (pop calls) `(line-1 "act" ,spam-buffer))) + (should (eq (erc-active-buffer) spam-buffer)) + (should-not calls)) + + (ert-info ("Variant `active' falls back to current buffer") + (should (eq spam-buffer (erc-active-buffer))) + (kill-buffer "#spam") + (erc-display-line "nact" 'active) + (should (equal (pop calls) `(line-1 "nact" ,server-buffer))) + (should (eq (erc-with-server-buffer erc-active-buffer) + server-buffer)) + (should-not calls)) + + (ert-info ("Dead single buffer defaults to live server-buffer") + (should-not (get-buffer "#spam")) + (erc-display-line "dead" 'spam-buffer) + (should (equal (pop calls) `(line-1 "dead" ,server-buffer))) + (should-not calls)))) + + (should-not (buffer-live-p spam-buffer)) + (kill-buffer chan-buffer))) + (defvar erc-tests--ipv6-examples '("1:2:3:4:5:6:7:8" "::ffff:10.0.0.1" "::ffff:1.2.3.4" "::ffff:0.0.0.0" diff --git a/test/lisp/erc/resources/base/display-message/multibuf.eld b/test/lisp/erc/resources/base/display-message/multibuf.eld new file mode 100644 index 00000000000..e49a654cd06 --- /dev/null +++ b/test/lisp/erc/resources/base/display-message/multibuf.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Sat, 14 Oct 2023 16:08:20 UTC") + (0.02 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.00 ":irc.foonet.org 251 tester :There are 0 users and 5 invisible on 1 server(s)") + (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.00 ":irc.foonet.org 254 tester 2 :channels formed") + (0.00 ":irc.foonet.org 255 tester :I have 5 clients and 0 servers") + (0.00 ":irc.foonet.org 265 tester 5 5 :Current local users 5, max 5") + (0.02 ":irc.foonet.org 266 tester 5 5 :Current global users 5, max 5") + (0.01 ":irc.foonet.org 422 tester :MOTD File is missing") + (0.00 ":irc.foonet.org 221 tester +i") + (0.01 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((mode 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i")) + +((join 10 "JOIN #chan") + (0.03 ":tester!~u@rdjcgiwfuwqmc.irc JOIN #chan") + (0.03 ":irc.foonet.org 353 tester = #chan :@fsbot bob alice dummy tester") + (0.01 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.00 ":bob!~u@uee7kge7ua5sy.irc PRIVMSG #chan :tester, welcome!") + (0.01 ":alice!~u@uee7kge7ua5sy.irc PRIVMSG #chan :tester, welcome!")) + +((mode 10 "MODE #chan") + (0.01 ":bob!~u@uee7kge7ua5sy.irc PRIVMSG #chan :alice: Persuade this rude wretch willingly to die.") + (0.01 ":irc.foonet.org 324 tester #chan +Cnt") + (0.01 ":irc.foonet.org 329 tester #chan 1697299707") + (0.03 ":alice!~u@uee7kge7ua5sy.irc PRIVMSG #chan :bob: It might be yours or hers, for aught I know.") + (0.07 ":bob!~u@uee7kge7ua5sy.irc PRIVMSG #chan :Would all themselves laugh mortal.") + (0.04 ":dummy!~u@rdjcgiwfuwqmc.irc PRIVMSG tester :hi") + (0.06 ":bob!~u@uee7kge7ua5sy.irc PRIVMSG #chan :alice: It hath pleased the devil drunkenness to give place to the devil wrath; one unperfectness shows me another, to make me frankly despise myself.") + (0.05 ":dummy!~u@rdjcgiwfuwqmc.irc QUIT :Quit: \2ERC\2 5.6-git (IRC client for GNU Emacs 30.0.50)") + (0.08 ":alice!~u@uee7kge7ua5sy.irc PRIVMSG #chan :You speak of him when he was less furnished than now he is with that which makes him both without and within.")) + +((quit 10 "QUIT :\2ERC\2") + (0.04 ":tester!~u@rdjcgiwfuwqmc.irc QUIT :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)") + (0.02 "ERROR :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)")) -- 2.41.0