>From 8f0f4c1d424e0a70c6e46b97ca5b75699f80c892 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Thu, 20 Jul 2023 05:38:35 -0700 Subject: [PATCH 0/1] *** NOT A PATCH *** *** BLURB HERE *** F. Jason Park (1): [5.6] Improve ERC's internal invisibility API etc/ERC-NEWS | 9 +++ lisp/erc/erc-fill.el | 16 +++-- lisp/erc/erc-match.el | 46 +++++++------- lisp/erc/erc.el | 32 +++++++++- test/lisp/erc/erc-scenarios-match.el | 95 ++++++++++++++++++++-------- test/lisp/erc/erc-tests.el | 50 +++++++++++++-- 6 files changed, 183 insertions(+), 65 deletions(-) Interdiff: diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS index 4c881e32ab4..16577c73b2e 100644 --- a/etc/ERC-NEWS +++ b/etc/ERC-NEWS @@ -287,6 +287,15 @@ The 'fill' module is now defined by 'define-erc-module'. The same goes for ERC's imenu integration, which has 'imenu' now appearing in the default value of 'erc-modules'. +*** Hidden messages contain a preceding rather than trailing newline. +ERC has traditionally only offered to hide messages involving fools, +but plans are to make hiding more powerful. Anyone depending on the +existing behavior should be aware that hidden messages now start and +end one character earlier, so that hidden line endings precede rather +than follow accompanying text. However, an escape hatch is available +in the variable 'erc-legacy-invisible-bounds-p'. It reinstates the +old behavior, which is unsupported by newer modules and features. + *** 'erc-display-message' optionally combines faces. Users may notice that ERC now inserts some important error messages in a combination of 'erc-error-face' and 'erc-notice-face'. This is diff --git a/lisp/erc/erc-fill.el b/lisp/erc/erc-fill.el index 2dfae35b871..bb11b03e791 100644 --- a/lisp/erc/erc-fill.el +++ b/lisp/erc/erc-fill.el @@ -288,11 +288,17 @@ erc-fill-wrap-mode-map ;; Not sure if this is problematic because `erc-bol' takes no args. " " #'erc-fill--wrap-beginning-of-line) -(defvar erc-match-mode) (defvar erc-button-mode) -(defvar erc-match--offset-invisible-bounds-p) +(defvar erc-legacy-invisible-bounds-p) (defun erc-fill--wrap-ensure-dependencies () + (with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p)) + (when erc-legacy-invisible-bounds-p + (erc--warn-once-before-connect 'erc-fill-wrap-mode + "Module `fill-wrap' is incompatible with the obsolete compatibility" + " flag `erc-legacy-invisible-bounds-p'. Disabling locally in %s." + (current-buffer)) + (setq-local erc-legacy-invisible-bounds-p nil))) (let (missing-deps) (unless erc-fill-mode (push 'fill missing-deps) @@ -336,11 +342,7 @@ fill-wrap ;; Internal integrations. (add-function :after (local 'erc-stamp--insert-date-function) #'erc-fill--wrap-stamp-insert-prefixed-date) - (when (or erc-stamp-mode (memq 'stamp erc-modules)) - (erc-stamp--display-margin-mode +1)) - (when (or (bound-and-true-p erc-match-mode) (memq 'match erc-modules)) - (require 'erc-match) - (setq erc-match--offset-invisible-bounds-p t)) + (erc-stamp--display-margin-mode +1) (when erc-fill-wrap-merge (add-hook 'erc-button--prev-next-predicate-functions #'erc-fill--wrap-merged-button-p nil t)) diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el index 3fba35536b3..50db8a132ec 100644 --- a/lisp/erc/erc-match.el +++ b/lisp/erc/erc-match.el @@ -655,25 +655,10 @@ erc-go-to-log-matches-buffer (get-buffer (car buffer-cons)))))) (switch-to-buffer buffer-name))) -(defvar-local erc-match--offset-invisible-bounds-p nil - "Whether to hide a message from its preceding newline.") - (defun erc-hide-fools (match-type _nickuserhost _message) "Hide comments from designated fools." (when (eq match-type 'fool) - (erc-match--hide-message 'match-fools))) - -(defun erc-match--hide-message (prop) - (progn ; FIXME raise sexp - (if erc-match--offset-invisible-bounds-p - (let ((beg (point-min)) - (end (point-max))) - (save-restriction - (widen) - (erc--merge-prop (1- beg) (1- end) 'invisible prop))) - ;; Before ERC 5.6, this also used to add an `intangible' - ;; property, but the docs say it's now obsolete. - (erc--merge-prop (point-min) (point-max) 'invisible prop)))) + (erc--hide-message 'match-fools))) (defun erc-beep-on-match (match-type _nickuserhost _message) "Beep when text matches. diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 4192ea9a9e2..972cf8d63f3 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -3007,6 +3007,9 @@ erc-display-line (defvar erc--compose-text-properties nil "Non-nil when `erc-put-text-property' defers to `erc--merge-prop'.") +;; We could maintain a cache of all combined values and dispense +;; archetype singleton objects so all occurrences of some list (a b) +;; across all ERC buffers are actually the same object. (defun erc--merge-prop (from to prop val &optional object) "Compose existing PROP values with VAL between FROM and TO in OBJECT. For spans where PROP is non-nil, cons VAL onto the existing @@ -3028,6 +3031,26 @@ erc--merge-prop old (get-text-property pos prop object) end (next-single-property-change pos prop object to))))) +(defvar erc-legacy-invisible-bounds-p nil + "Whether to hide trailing rather than preceding newlines. +Beginning in ERC 5.6, invisibility extends from a message's +preceding newline to its last non-newline character.") +(make-obsolete-variable 'erc-legacy-invisible-bounds-p + "decremented interval now permanent" "30.1") + +(defun erc--hide-message (value) + "Apply `invisible' text-property with VALUE to current message. +Expect to run in a narrowed buffer during message insertion." + (if erc-legacy-invisible-bounds-p + ;; Before ERC 5.6, this also used to add an `intangible' + ;; property, but the docs say it's now obsolete. + (erc--merge-prop (point-min) (point-max) 'invisible value) + (let ((beg (point-min)) + (end (point-max))) + (save-restriction + (widen) + (erc--merge-prop (1- beg) (1- end) 'invisible value))))) + (defun erc-display-message-highlight (type string) "Highlight STRING according to TYPE, where erc-TYPE-face is an ERC face. diff --git a/test/lisp/erc/erc-scenarios-match.el b/test/lisp/erc/erc-scenarios-match.el index a438635960e..cd899fddb98 100644 --- a/test/lisp/erc/erc-scenarios-match.el +++ b/test/lisp/erc/erc-scenarios-match.el @@ -151,6 +151,12 @@ erc-scenarios-match--stamp-left-fools-invisible (= (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))) @@ -160,13 +166,14 @@ erc-scenarios-match--find-eol (pos-eol))) ;; In most cases, `erc-hide-fools' makes line endings invisible. -(ert-deftest erc-scenarios-match--stamp-right-fools-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 ((end (erc-scenarios-match--find-eol))) + (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))) @@ -178,7 +185,11 @@ erc-scenarios-match--stamp-right-fools-invisible '(timestamp match-fools))) ;; The final newline is hidden by `match', not `stamps' - (should (equal (get-text-property end 'invisible) 'match-fools)) + (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. @@ -204,6 +215,17 @@ erc-scenarios-match--stamp-right-fools-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 () @@ -249,8 +271,7 @@ erc-scenarios-match--stamp-right-invisible-fill-wrap (let ((inv-beg (next-single-property-change (1- (pos-bol)) 'invisible))) (should (eq (get-text-property inv-beg 'invisible) 'timestamp))))))) -(ert-deftest erc-scenarios-match--stamp-both-invisible-fill-static () - :tags '(:expensive-test) +(defun erc-scenarios-match--stamp-both-invisible-fill-static () (should (eq erc-insert-timestamp-function #'erc-insert-timestamp-left-and-right)) @@ -292,7 +313,11 @@ erc-scenarios-match--stamp-both-invisible-fill-static ;; Line ending has the `invisible' property `match-fools'. (should (= (char-after mend) ?\n)) - (should (eq (get-text-property mend'invisible) 'match-fools)))) + (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) @@ -338,4 +363,15 @@ erc-scenarios-match--stamp-both-invisible-fill-static (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 -- 2.41.0