mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-10 16:20:17 -08:00
* etc/ERC-NEWS: Mention that line endings have moved from the end to the beginning of hidden messages. * lisp/erc/erc-fill.el (erc-fill--wrap-ensure-dependencies): Warn when users have `erc-legacy-invisible-bounds-p' enabled, and force it to its default value of nil in the current buffer. (erc-fill-wrap-mode, erc-fill-wrap-enable): Move business involving compat variable for enabling legacy hidden-message behavior to helper. * lisp/erc/erc-match.el (erc-match--hide-fools-offset-bounds): Move internal variable from to main library file and rename to `erc-legacy-invisible-bounds-p'. Also make obsolete and flip semantics so a non-nil value enables the traditional behavior. (erc-match--hide-message): Move to main library file and rename to `erc--hide-message'. Add a property-value parameter instead of hard-coding to `erc-match'. Also, condition behavior on renamed compatibility flag `erc-legacy-invisible-bounds-p'. (erc-hide-fools): Call `erc--hide-message' with own value for `invisible' property specifically for fools. That is, use `match-fools' rather than `erc-match' or `erc-match-fools' to save room when visually inspecting. This retains the module name as a prefix to hopefully minimize collisions with invisibility spec members owned by non-ERC minor modes. The `timestamp' spec member owned by erc-stamp likewise lacks a namespace prefix, but its feature/group affiliation is self-evident. (erc-match--modify-invisibility-spec): Use toggle command non-interactively for adding and removing invisibility spec member. (erc-match-toggle-hidden-fools): Add explicit override argument and defer to general helper for actually modifying spec. (erc-match--toggle-hidden): New helper for toggling invisibility spec. * lisp/erc/erc.el (erc--merge-prop): If new value is a list, prepend onto existing. Add note about possible space optimization. (erc-legacy-invisible-bounds-p): New obsolete compat variable to enable traditional pre-5.6 invisibility interval on hidden messages. Replaces `erc-match--hide-fools-offset-bounds-p' but has an inverted meaning. The new default value of nil means invisibility covers a shifted interval consisting of the message body plus the line ending immediately preceding it. (erc--hide-message): New function, formerly `erc-match--hide-message' from erc-match.el introduced in ERC 5.6. * test/lisp/erc/erc-scenarios-match.el: (erc-scenarios-match--invisible-stamp): Fix comment and use API function in interactive convenience setup. (erc-scenarios-match--find-bol): New test helper. (erc-scenarios-match--find-eol): Fix bug affecting interactive use. (erc-scenarios-match--stamp-left-fools-invisible, erc-scenarios-match--stamp-right-fools-invisible, erc-scenarios-match--stamp-right-invisible-fill-wrap, erc-scenarios-match--stamp-both-invisible-fill-static): Update `invisible' property from `erc-match' to `match-fools'. (erc-scenarios-match--stamp-right-fools-invisible, erc-scenarios-match--stamp-both-invisible-fill-static): Move test body to function of same name for use in multiple cases. (erc-scenarios-match--stamp-right-fools-invisible--nooffset, erc-scenarios-match--stamp-both-invisible-fill-static--nooffset): New test variants asserting proper hiding with old pre-5.6 invisibility interval. * test/lisp/erc/erc-tests.el (erc-tests--equal-including-properties): Relocate macro higher in same file. (erc--merge-prop): New test. (Bug#64301)
377 lines
16 KiB
EmacsLisp
377 lines
16 KiB
EmacsLisp
;;; 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 <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Code:
|
|
|
|
(require 'ert-x)
|
|
(eval-and-compile
|
|
(let ((load-path (cons (ert-resource-directory) load-path)))
|
|
(require 'erc-scenarios-common)))
|
|
|
|
(eval-when-compile
|
|
(require 'erc-join)
|
|
(require 'erc-match))
|
|
|
|
(require 'erc-stamp)
|
|
(require 'erc-fill)
|
|
|
|
;; 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")
|
|
;; Module `timestamp' follows `match' in insertion hooks.
|
|
(should (memq 'erc-add-timestamp
|
|
(memq 'erc-match-message 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))))))
|
|
|
|
;; When hacking on tests that use this fixture, it's best to run it
|
|
;; interactively, and visually inspect the output with various
|
|
;; combinations of:
|
|
;;
|
|
;; M-x erc-match-toggle-hidden-fools RET
|
|
;; M-x erc-toggle-timestamps RET
|
|
;;
|
|
(defun erc-scenarios-match--invisible-stamp (hiddenp visiblep)
|
|
(unless noninteractive
|
|
(kill-new "erc-match-toggle-hidden-fools"))
|
|
|
|
(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-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)))
|
|
|
|
(ert-info ("Connect")
|
|
(with-current-buffer (erc :server "127.0.0.1"
|
|
:port port
|
|
:full-name "tester"
|
|
:password "changeme"
|
|
:nick "tester")
|
|
;; Module `timestamp' follows `match' in insertion hooks.
|
|
(should (memq 'erc-add-timestamp
|
|
(memq 'erc-match-message 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 "<bob> tester, welcome!"))
|
|
(ert-info ("<bob> tester, welcome!") (funcall hiddenp))
|
|
|
|
;; Alice's is the only one visible.
|
|
(should (funcall expect 10 "<alice> tester, welcome!"))
|
|
(ert-info ("<alice> tester, welcome!") (funcall visiblep))
|
|
|
|
(should (funcall expect 10 "<bob> alice: But, as it seems"))
|
|
(ert-info ("<bob> alice: But, as it seems") (funcall hiddenp))
|
|
|
|
(should (funcall expect 10 "<alice> bob: Well, this is the forest"))
|
|
(ert-info ("<alice> bob: Well, this is the forest") (funcall hiddenp))
|
|
|
|
(should (funcall expect 10 "<alice> bob: And will you"))
|
|
(ert-info ("<alice> bob: And will you") (funcall hiddenp))
|
|
|
|
(should (funcall expect 10 "<bob> alice: Live, and be prosperous"))
|
|
(ert-info ("<bob> alice: Live, and be prosperous") (funcall hiddenp))
|
|
|
|
(should (funcall expect 10 "ERC>"))
|
|
(should-not (get-text-property (pos-bol) 'invisible))
|
|
(should-not (get-text-property (point) 'invisible))))))
|
|
|
|
;; This asserts that when stamps appear before a message, registered
|
|
;; invisibility properties owned by modules span the entire message.
|
|
(ert-deftest erc-scenarios-match--stamp-left-fools-invisible ()
|
|
:tags '(:expensive-test)
|
|
(let ((erc-insert-timestamp-function #'erc-insert-timestamp-left))
|
|
(erc-scenarios-match--invisible-stamp
|
|
|
|
(lambda ()
|
|
;; This is a time-stamped message.
|
|
(should (eq (field-at-pos (pos-bol)) 'erc-timestamp))
|
|
|
|
;; Leading stamp has combined `invisible' property value.
|
|
(should (equal (get-text-property (pos-bol) 'invisible)
|
|
'(timestamp match-fools)))
|
|
|
|
;; Message proper has the `invisible' property `match-fools'.
|
|
(let ((msg-beg (next-single-property-change (pos-bol) 'invisible)))
|
|
(should (eq (get-text-property msg-beg 'invisible) 'match-fools))
|
|
(should (>= (next-single-property-change msg-beg 'invisible nil)
|
|
(pos-eol)))))
|
|
|
|
(lambda ()
|
|
;; This is a time-stamped message.
|
|
(should (eq (field-at-pos (pos-bol)) 'erc-timestamp))
|
|
(should (get-text-property (pos-bol) 'invisible))
|
|
|
|
;; The entire message proper is visible.
|
|
(let ((msg-beg (next-single-property-change (pos-bol) 'invisible)))
|
|
(should
|
|
(= (next-single-property-change msg-beg 'invisible nil (pos-eol))
|
|
(pos-eol))))))))
|
|
|
|
(defun erc-scenarios-match--find-bol ()
|
|
(save-excursion
|
|
(should (get-text-property (1- (point)) 'erc-command))
|
|
(goto-char (should (previous-single-property-change (point) 'erc-command)))
|
|
(pos-bol)))
|
|
|
|
(defun erc-scenarios-match--find-eol ()
|
|
(save-excursion
|
|
(if-let ((next (next-single-property-change (point) 'erc-command)))
|
|
(goto-char next)
|
|
;; We're already at the end of the message.
|
|
(should (get-text-property (1- (point)) 'erc-command)))
|
|
(pos-eol)))
|
|
|
|
;; In most cases, `erc-hide-fools' makes line endings invisible.
|
|
(defun erc-scenarios-match--stamp-right-fools-invisible ()
|
|
:tags '(:expensive-test)
|
|
(let ((erc-insert-timestamp-function #'erc-insert-timestamp-right))
|
|
(erc-scenarios-match--invisible-stamp
|
|
|
|
(lambda ()
|
|
(let ((beg (erc-scenarios-match--find-bol))
|
|
(end (erc-scenarios-match--find-eol)))
|
|
;; The end of the message is a newline.
|
|
(should (= ?\n (char-after end)))
|
|
|
|
;; Every message has a trailing time stamp.
|
|
(should (eq (field-at-pos (1- end)) 'erc-timestamp))
|
|
|
|
;; Stamps have a combined `invisible' property value.
|
|
(should (equal (get-text-property (1- end) 'invisible)
|
|
'(timestamp match-fools)))
|
|
|
|
;; The final newline is hidden by `match', not `stamps'
|
|
(with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p))
|
|
(if erc-legacy-invisible-bounds-p
|
|
(should (eq (get-text-property end 'invisible) 'match-fools))
|
|
(should (eq (get-text-property beg 'invisible) 'match-fools))
|
|
(should-not (get-text-property end 'invisible))))
|
|
|
|
;; The message proper has the `invisible' property `match-fools',
|
|
;; and it starts after the preceding newline.
|
|
(should (eq (get-text-property (pos-bol) 'invisible) 'match-fools))
|
|
|
|
;; It ends just before the timestamp.
|
|
(let ((msg-end (next-single-property-change (pos-bol) 'invisible)))
|
|
(should (equal (get-text-property msg-end 'invisible)
|
|
'(timestamp match-fools)))
|
|
|
|
;; Stamp's `invisible' property extends throughout the stamp
|
|
;; and ends before the trailing newline.
|
|
(should (= (next-single-property-change msg-end 'invisible) end)))))
|
|
|
|
(lambda ()
|
|
(let ((end (erc-scenarios-match--find-eol)))
|
|
;; This message has a time stamp like all the others.
|
|
(should (eq (field-at-pos (1- end)) 'erc-timestamp))
|
|
|
|
;; The entire message proper is visible.
|
|
(should-not (get-text-property (pos-bol) 'invisible))
|
|
(let ((inv-beg (next-single-property-change (pos-bol) 'invisible)))
|
|
(should (eq (get-text-property inv-beg 'invisible)
|
|
'timestamp))))))))
|
|
|
|
(ert-deftest erc-scenarios-match--stamp-right-fools-invisible ()
|
|
:tags '(:expensive-test)
|
|
(erc-scenarios-match--stamp-right-fools-invisible))
|
|
|
|
(ert-deftest erc-scenarios-match--stamp-right-fools-invisible--nooffset ()
|
|
:tags '(:expensive-test)
|
|
(with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p))
|
|
(should-not erc-legacy-invisible-bounds-p)
|
|
(let ((erc-legacy-invisible-bounds-p t))
|
|
(erc-scenarios-match--stamp-right-fools-invisible))))
|
|
|
|
;; This asserts that when `erc-fill-wrap-mode' is enabled, ERC hides
|
|
;; the preceding message's line ending.
|
|
(ert-deftest erc-scenarios-match--stamp-right-invisible-fill-wrap ()
|
|
:tags '(:expensive-test)
|
|
(let ((erc-insert-timestamp-function #'erc-insert-timestamp-right)
|
|
(erc-fill-function #'erc-fill-wrap))
|
|
(erc-scenarios-match--invisible-stamp
|
|
|
|
(lambda ()
|
|
;; Every message has a trailing time stamp.
|
|
(should (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp))
|
|
|
|
;; Stamps appear in the right margin.
|
|
(should (equal (car (get-text-property (1- (pos-eol)) 'display))
|
|
'(margin right-margin)))
|
|
|
|
;; Stamps have a combined `invisible' property value.
|
|
(should (equal (get-text-property (1- (pos-eol)) 'invisible)
|
|
'(timestamp match-fools)))
|
|
|
|
;; The message proper has the `invisible' property `match-fools',
|
|
;; which starts at the preceding newline...
|
|
(should (eq (get-text-property (1- (pos-bol)) 'invisible) 'match-fools))
|
|
|
|
;; ... and ends just before the timestamp.
|
|
(let ((msgend (next-single-property-change (1- (pos-bol)) 'invisible)))
|
|
(should (equal (get-text-property msgend 'invisible)
|
|
'(timestamp match-fools)))
|
|
|
|
;; The newline before `erc-insert-marker' is still visible.
|
|
(should-not (get-text-property (pos-eol) 'invisible))
|
|
(should (= (next-single-property-change msgend 'invisible)
|
|
(pos-eol)))))
|
|
|
|
(lambda ()
|
|
;; This message has a time stamp like all the others.
|
|
(should (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp))
|
|
|
|
;; Unlike hidden messages, the preceding newline is visible.
|
|
(should-not (get-text-property (1- (pos-bol)) 'invisible))
|
|
|
|
;; The entire message proper is visible.
|
|
(let ((inv-beg (next-single-property-change (1- (pos-bol)) 'invisible)))
|
|
(should (eq (get-text-property inv-beg 'invisible) 'timestamp)))))))
|
|
|
|
(defun erc-scenarios-match--stamp-both-invisible-fill-static ()
|
|
(should (eq erc-insert-timestamp-function
|
|
#'erc-insert-timestamp-left-and-right))
|
|
|
|
;; Rewind the clock to known date artificially.
|
|
(let ((erc-stamp--current-time 704591940)
|
|
(erc-stamp--tz t)
|
|
(erc-fill-function #'erc-fill-static)
|
|
(bob-utterance-counter 0))
|
|
|
|
(erc-scenarios-match--invisible-stamp
|
|
|
|
(lambda ()
|
|
(ert-info ("Baseline check")
|
|
;; False date printed initially before anyone speaks.
|
|
(when (zerop bob-utterance-counter)
|
|
(save-excursion
|
|
(goto-char (point-min))
|
|
(search-forward "[Wed Apr 29 1992]")
|
|
(search-forward "[23:59]"))))
|
|
|
|
(ert-info ("Line endings in Bob's messages are invisible")
|
|
;; The message proper has the `invisible' property `match-fools'.
|
|
(should (eq (get-text-property (pos-bol) 'invisible) 'match-fools))
|
|
(let* ((mbeg (next-single-property-change (pos-bol) 'erc-command))
|
|
(mend (next-single-property-change mbeg 'erc-command)))
|
|
|
|
(if (/= 1 bob-utterance-counter)
|
|
(should-not (field-at-pos mend))
|
|
;; For Bob's stamped message, check newline after stamp.
|
|
(should (eq (field-at-pos mend) 'erc-timestamp))
|
|
(setq mend (field-end mend)))
|
|
|
|
;; The `erc-timestamp' property spans entire messages,
|
|
;; including stamps and filled text, which makes for
|
|
;; convenient traversal when `erc-stamp-mode' is enabled.
|
|
(should (get-text-property (pos-bol) 'erc-timestamp))
|
|
(should (= (next-single-property-change (pos-bol) 'erc-timestamp)
|
|
mend))
|
|
|
|
;; Line ending has the `invisible' property `match-fools'.
|
|
(should (= (char-after mend) ?\n))
|
|
(with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p))
|
|
(if erc-legacy-invisible-bounds-p
|
|
(should (eq (get-text-property mend 'invisible) 'match-fools))
|
|
(should (eq (get-text-property mbeg 'invisible) 'match-fools))
|
|
(should-not (get-text-property mend 'invisible))))))
|
|
|
|
;; Only the message right after Alice speaks contains stamps.
|
|
(when (= 1 bob-utterance-counter)
|
|
|
|
(ert-info ("Date stamp occupying previous line is invisible")
|
|
(save-excursion
|
|
(forward-line -1)
|
|
(goto-char (pos-bol))
|
|
(should (looking-at (rx "[Mon May 4 1992]")))
|
|
;; Date stamp has a combined `invisible' property value
|
|
;; that extends until the start of the message proper.
|
|
(should (equal (get-text-property (point) 'invisible)
|
|
'(timestamp match-fools)))
|
|
(should (= (next-single-property-change (point) 'invisible)
|
|
(1+ (pos-eol))))))
|
|
|
|
(ert-info ("Folding preserved despite invisibility")
|
|
;; Message has a trailing time stamp, but it's been folded
|
|
;; over to the next line.
|
|
(should-not (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp))
|
|
(save-excursion
|
|
(forward-line)
|
|
(should (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp)))
|
|
|
|
;; Stamp invisibility starts where message's ends.
|
|
(let ((msgend (next-single-property-change (pos-bol) 'invisible)))
|
|
;; Stamp has a combined `invisible' property value.
|
|
(should (equal (get-text-property msgend 'invisible)
|
|
'(timestamp match-fools)))
|
|
|
|
;; Combined `invisible' property spans entire timestamp.
|
|
(should (= (next-single-property-change msgend 'invisible)
|
|
(save-excursion (forward-line) (pos-eol)))))))
|
|
|
|
(cl-incf bob-utterance-counter))
|
|
|
|
;; Alice.
|
|
(lambda ()
|
|
;; Set clock ahead a week or so.
|
|
(setq erc-stamp--current-time 704962800)
|
|
|
|
;; This message has no time stamp and is completely visible.
|
|
(should-not (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp))
|
|
(should-not (next-single-property-change (pos-bol) 'invisible))))))
|
|
|
|
(ert-deftest erc-scenarios-match--stamp-both-invisible-fill-static ()
|
|
:tags '(:expensive-test)
|
|
(erc-scenarios-match--stamp-both-invisible-fill-static))
|
|
|
|
(ert-deftest erc-scenarios-match--stamp-both-invisible-fill-static--nooffset ()
|
|
:tags '(:expensive-test)
|
|
(with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p))
|
|
(should-not erc-legacy-invisible-bounds-p)
|
|
(let ((erc-legacy-invisible-bounds-p t))
|
|
(erc-scenarios-match--stamp-both-invisible-fill-static))))
|
|
|
|
;;; erc-scenarios-match.el ends here
|