From: F. Jason Park Date: Wed, 28 Jun 2023 03:47:26 +0000 (-0700) Subject: Account for leading timestamps in erc-match X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=99d74dcd45938e2686d93eb5649800e14a88cd84;p=emacs.git Account for leading timestamps in erc-match * lisp/erc/erc-match.el (erc-text-matched-hook): Mention that stamps may be present in the narrowed buffer but absent from the message parameter. (erc-match--message): New function containing what was the body of `erc-match-message' as if the latter were simply renamed. (erc-match-message): Move body to `erc-match--message' and call it with more aggressive narrowing. This fixes a regression stemming from d880a08f "Cement ordering of essential hook members in ERC". Special thanks to Libera.Chat user jrm for reporting this bug. (Bug#60936) * test/lisp/erc/erc-scenarios-match.el: New test file. --- diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el index 6ba524ef9a8..204bf14a1cf 100644 --- a/lisp/erc/erc-match.el +++ b/lisp/erc/erc-match.el @@ -233,10 +233,14 @@ for beeping to work." (const :tag "Don't beep" nil))) (defcustom erc-text-matched-hook '(erc-log-matches) - "Hook run when text matches a given match-type. -Functions in this hook are passed as arguments: -\(match-type nick!user@host message) where MATCH-TYPE is a symbol of: -current-nick, keyword, pal, dangerous-host, fool." + "Abnormal hook for visiting text matching a predefined \"type\". +ERC calls members with the arguments (MATCH-TYPE NUH MESSAGE), +where MATCH-TYPE is one of the symbols `current-nick', `keyword', +`pal', `dangerous-host', `fool', and NUH is an `erc-response' +sender, like bob!~bob@example.org. Users should keep in mind +that MESSAGE may not include decorations, such as white space or +time stamps, preceding the same text as inserted in the narrowed +buffer." :options '(erc-log-matches erc-hide-fools erc-beep-on-match) :type 'hook) @@ -458,8 +462,19 @@ In any of the following situations, MSG is directed at an entry FOOL: (erc-list-match fools-end msg)))) (defun erc-match-message () - "Mark certain keywords in a region. -Use this defun with `erc-insert-modify-hook'." + "Add faces to matching text in inserted message." + ;; Exclude leading whitespace, stamps, etc. + (let ((omin (point-min)) + (beg (or (and (not (get-text-property (point-min) 'erc-command)) + (next-single-property-change (point-min) 'erc-command)) + (point-min)))) + ;; FIXME when ERC no longer supports 28, use `with-restriction' + ;; with `:label' here instead of passing `omin'. + (save-restriction + (narrow-to-region beg (point-max)) + (erc-match--message omin)))) + +(defun erc-match--message (unrestricted-point-min) ;; This needs some refactoring. (goto-char (point-min)) (let* ((to-match-nick-dep '("pal" "fool" "dangerous-host")) @@ -561,12 +576,14 @@ Use this defun with `erc-insert-modify-hook'." 'font-lock-face match-face))) ;; Else twiddle your thumbs. (t nil)) - (run-hook-with-args - 'erc-text-matched-hook - (intern match-type) - (or nickuserhost - (concat "Server:" (erc-get-parsed-vector-type vector))) - message)))) + ;; FIXME use `without-restriction' after dropping 28. + (save-restriction + (narrow-to-region unrestricted-point-min (point-max)) + (run-hook-with-args + 'erc-text-matched-hook (intern match-type) + (or nickuserhost + (concat "Server:" (erc-get-parsed-vector-type vector))) + message))))) (if nickuserhost (append to-match-nick-dep to-match-nick-indep) to-match-nick-indep))))) diff --git a/test/lisp/erc/erc-scenarios-match.el b/test/lisp/erc/erc-scenarios-match.el new file mode 100644 index 00000000000..49e6a3370fc --- /dev/null +++ b/test/lisp/erc/erc-scenarios-match.el @@ -0,0 +1,120 @@ +;;; erc-scenarios-match.el --- Misc `erc-match' scenarios -*- 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))) + +(require 'erc-stamp) +(require 'erc-match) + +;; This defends against a regression in which all matching by the +;; `erc-match-message' fails when `erc-add-timestamp' precedes it in +;; `erc-insert-modify-hook'. Basically, `erc-match-message' used to +;; expect an `erc-parsed' text property on the first character in a +;; message, which doesn't exist, when the message content is prefixed +;; by a leading timestamp. + +(ert-deftest erc-scenarios-match--stamp-left-current-nick () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (erc-insert-timestamp-function 'erc-insert-timestamp-left) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + (should (memq 'erc-match-message + (memq 'erc-add-timestamp erc-insert-modify-hook))) + ;; The "match type" is `current-nick'. + (funcall expect 5 "tester") + (should (eq (get-text-property (1- (point)) 'font-lock-face) + 'erc-current-nick-face)))))) + +;; This asserts that when stamps appear before a message, +;; some non-nil invisibility property spans the entire message. +(ert-deftest erc-scenarios-match--stamp-left-fools-invisible () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "join/legacy") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (erc-insert-timestamp-function 'erc-insert-timestamp-left) + (erc-timestamp-only-if-changed-flag nil) + (erc-fools '("bob")) + (erc-text-matched-hook '(erc-hide-fools)) + (erc-autojoin-channels-alist '((FooNet "#chan"))) + (expect (erc-d-t-make-expecter)) + (hiddenp (lambda () + (and (eq (field-at-pos (pos-bol)) 'erc-timestamp) + (get-text-property (pos-bol) 'invisible) + (>= (next-single-property-change (pos-bol) + 'invisible nil) + (pos-eol)))))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :password "changeme" + :nick "tester") + (should (memq 'erc-match-message + (memq 'erc-add-timestamp erc-insert-modify-hook))) + (funcall expect 5 "This server is in debug mode"))) + + (ert-info ("Ensure lines featuring \"bob\" are invisible") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (should (funcall expect 10 " tester, welcome!")) + (should (funcall hiddenp)) + + ;; Alice's is the only one visible. + (should (funcall expect 10 " tester, welcome!")) + (should (eq (field-at-pos (pos-bol)) 'erc-timestamp)) + (should (get-text-property (pos-bol) 'invisible)) + (should-not (get-text-property (point) 'invisible)) + + (should (funcall expect 10 " alice: But, as it seems")) + (should (funcall hiddenp)) + + (should (funcall expect 10 " bob: Well, this is the forest")) + (should (funcall hiddenp)) + + (should (funcall expect 10 " bob: And will you")) + (should (funcall hiddenp)) + + (should (funcall expect 10 " alice: Live, and be prosperous")) + (should (funcall hiddenp)) + + (should (funcall expect 10 "ERC>")) + (should-not (get-text-property (pos-bol) 'invisible)) + (should-not (get-text-property (point) 'invisible)))))) + +(eval-when-compile (require 'erc-join)) + +;;; erc-scenarios-match.el ends here