From: "J.P." <jp@neverwas.me>
To: 64301-done@debbugs.gnu.org
Cc: emacs-erc@gnu.org
Subject: bug#64301: 30.0.50; ERC 5.6: Make speaker labels easier to work with
Date: Thu, 20 Jul 2023 06:29:13 -0700 [thread overview]
Message-ID: <871qh2iudy.fsf__32737.8316478402$1689859820$gmane$org@neverwas.me> (raw)
In-Reply-To: <87cz0tnubk.fsf@neverwas.me> (J. P.'s message of "Sat, 15 Jul 2023 07:05:51 -0700")
[-- Attachment #1: Type: text/plain, Size: 157 bytes --]
v2 (invisibility API). Move primary message-hiding function to erc.el.
Invert meaning of invisible-bounds switch and make old non-nil behavior
new default.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v1-v2.diff --]
[-- Type: text/x-patch, Size: 11485 bytes --]
From 8f0f4c1d424e0a70c6e46b97ca5b75699f80c892 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
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.
"<remap> <erc-bol>" #'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
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-5.6-Improve-ERC-s-internal-invisibility-API.patch --]
[-- Type: text/x-patch, Size: 24866 bytes --]
From 8f0f4c1d424e0a70c6e46b97ca5b75699f80c892 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Fri, 14 Jul 2023 21:08:31 -0700
Subject: [PATCH 1/1] [5.6] Improve ERC's internal invisibility API
* 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 non-nil 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 inverted and
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
is already inherent.
(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 in same file.
(erc--merge-prop): New test. (Bug#64301)
---
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(-)
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 a65c95f1d85..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.
"<remap> <erc-bol>" #'erc-fill--wrap-beginning-of-line)
-(defvar erc-match-mode)
(defvar erc-button-mode)
-(defvar erc-match--hide-fools-offset-bounds)
+(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--hide-fools-offset-bounds 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 a5b0af41b2a..50db8a132ec 100644
--- a/lisp/erc/erc-match.el
+++ b/lisp/erc/erc-match.el
@@ -655,24 +655,10 @@ erc-go-to-log-matches-buffer
(get-buffer (car buffer-cons))))))
(switch-to-buffer buffer-name)))
-(defvar-local erc-match--hide-fools-offset-bounds nil)
-
(defun erc-hide-fools (match-type _nickuserhost _message)
"Hide comments from designated fools."
(when (eq match-type 'fool)
- (erc-match--hide-message)))
-
-(defun erc-match--hide-message ()
- (progn ; FIXME raise sexp
- (if erc-match--hide-fools-offset-bounds
- (let ((beg (point-min))
- (end (point-max)))
- (save-restriction
- (widen)
- (erc--merge-prop (1- beg) (1- end) 'invisible 'erc-match)))
- ;; 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 'erc-match))))
+ (erc--hide-message 'match-fools)))
(defun erc-beep-on-match (match-type _nickuserhost _message)
"Beep when text matches.
@@ -682,19 +668,31 @@ erc-beep-on-match
(defun erc-match--modify-invisibility-spec ()
"Add an `erc-match' property to the local spec."
+ ;; Hopefully, this will be extended to do the same for other
+ ;; invisible properties managed by this module.
(if erc-match-mode
- (add-to-invisibility-spec 'erc-match)
+ (erc-match-toggle-hidden-fools +1)
(erc-with-all-buffers-of-server nil nil
- (remove-from-invisibility-spec 'erc-match))))
+ (erc-match-toggle-hidden-fools -1))))
-(defun erc-match-toggle-hidden-fools ()
+(defun erc-match-toggle-hidden-fools (arg)
"Toggle fool visibility.
-Expect `erc-hide-fools' or a function that does something similar
-to be in `erc-text-matched-hook'."
- (interactive)
- (if (memq 'erc-match (ensure-list buffer-invisibility-spec))
- (remove-from-invisibility-spec 'erc-match)
- (add-to-invisibility-spec 'erc-match)))
+Expect the function `erc-hide-fools' or similar to be present in
+`erc-text-matched-hook'."
+ (interactive "P")
+ (erc-match--toggle-hidden 'match-fools arg))
+
+(defun erc-match--toggle-hidden (prop arg)
+ "Toggle invisibility for spec member PROP.
+Treat ARG in a manner similar to mode toggles defined by
+`define-minor-mode'."
+ (when arg
+ (setq arg (prefix-numeric-value arg)))
+ (if (memq prop (ensure-list buffer-invisibility-spec))
+ (unless (natnump arg)
+ (remove-from-invisibility-spec prop))
+ (when (or (not arg) (natnump arg))
+ (add-to-invisibility-spec prop))))
(provide 'erc-match)
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index eca6a90d706..972cf8d63f3 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -3007,22 +3007,50 @@ 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
value, ensuring a proper list. Otherwise, just set PROP to VAL.
-See also `erc-button-add-face'."
+When VAL is itself a list, prepend its members onto an existing
+value. See also `erc-button-add-face'."
(let ((old (get-text-property from prop object))
(pos from)
(end (next-single-property-change from prop object to))
new)
(while (< pos to)
- (setq new (if old (cons val (ensure-list old)) val))
+ (setq new (if old
+ (if (listp val)
+ (append val (ensure-list old))
+ (cons val (ensure-list old)))
+ val))
(put-text-property pos end prop new object)
(setq pos end
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 8a718962c55..cd899fddb98 100644
--- a/test/lisp/erc/erc-scenarios-match.el
+++ b/test/lisp/erc/erc-scenarios-match.el
@@ -62,11 +62,15 @@ erc-scenarios-match--stamp-left-current-nick
'erc-current-nick-face))))))
;; When hacking on tests that use this fixture, it's best to run it
-;; interactively, and check for wierdness before and after doing
-;; M-: (remove-from-invisibility-spec 'erc-match) RET.
+;; 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 "(remove-from-invisibility-spec 'erc-match)"))
+ (kill-new "erc-match-toggle-hidden-fools"))
(erc-scenarios-common-with-cleanup
((erc-scenarios-common-dialog "join/legacy")
@@ -128,11 +132,11 @@ erc-scenarios-match--stamp-left-fools-invisible
;; Leading stamp has combined `invisible' property value.
(should (equal (get-text-property (pos-bol) 'invisible)
- '(timestamp erc-match)))
+ '(timestamp match-fools)))
- ;; Message proper has the `invisible' property `erc-match'.
+ ;; 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) 'erc-match))
+ (should (eq (get-text-property msg-beg 'invisible) 'match-fools))
(should (>= (next-single-property-change msg-beg 'invisible nil)
(pos-eol)))))
@@ -147,19 +151,29 @@ 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
- (goto-char (next-single-property-change (point) 'erc-command))
+ (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.
-(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)))
@@ -168,19 +182,23 @@ erc-scenarios-match--stamp-right-fools-invisible
;; Stamps have a combined `invisible' property value.
(should (equal (get-text-property (1- end) 'invisible)
- '(timestamp erc-match)))
+ '(timestamp match-fools)))
;; The final newline is hidden by `match', not `stamps'
- (should (equal (get-text-property end 'invisible) 'erc-match))
+ (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 `erc-match',
+ ;; The message proper has the `invisible' property `match-fools',
;; and it starts after the preceding newline.
- (should (eq (get-text-property (pos-bol) 'invisible) 'erc-match))
+ (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 erc-match)))
+ '(timestamp match-fools)))
;; Stamp's `invisible' property extends throughout the stamp
;; and ends before the trailing newline.
@@ -197,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 ()
@@ -215,16 +244,16 @@ erc-scenarios-match--stamp-right-invisible-fill-wrap
;; Stamps have a combined `invisible' property value.
(should (equal (get-text-property (1- (pos-eol)) 'invisible)
- '(timestamp erc-match)))
+ '(timestamp match-fools)))
- ;; The message proper has the `invisible' property `erc-match',
+ ;; The message proper has the `invisible' property `match-fools',
;; which starts at the preceding newline...
- (should (eq (get-text-property (1- (pos-bol)) 'invisible) 'erc-match))
+ (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 erc-match)))
+ '(timestamp match-fools)))
;; The newline before `erc-insert-marker' is still visible.
(should-not (get-text-property (pos-eol) 'invisible))
@@ -242,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))
@@ -265,8 +293,8 @@ erc-scenarios-match--stamp-both-invisible-fill-static
(search-forward "[23:59]"))))
(ert-info ("Line endings in Bob's messages are invisible")
- ;; The message proper has the `invisible' property `erc-match'.
- (should (eq (get-text-property (pos-bol) 'invisible) 'erc-match))
+ ;; 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)))
@@ -283,9 +311,13 @@ erc-scenarios-match--stamp-both-invisible-fill-static
(should (= (next-single-property-change (pos-bol) 'erc-timestamp)
mend))
- ;; Line ending has the `invisible' property `erc-match'.
+ ;; Line ending has the `invisible' property `match-fools'.
(should (= (char-after mend) ?\n))
- (should (eq (get-text-property mend'invisible) 'erc-match))))
+ (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)
@@ -298,7 +330,7 @@ erc-scenarios-match--stamp-both-invisible-fill-static
;; 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 erc-match)))
+ '(timestamp match-fools)))
(should (= (next-single-property-change (point) 'invisible)
(1+ (pos-eol))))))
@@ -314,7 +346,7 @@ erc-scenarios-match--stamp-both-invisible-fill-static
(let ((msgend (next-single-property-change (pos-bol) 'invisible)))
;; Stamp has a combined `invisible' property value.
(should (equal (get-text-property msgend 'invisible)
- '(timestamp erc-match)))
+ '(timestamp match-fools)))
;; Combined `invisible' property spans entire timestamp.
(should (= (next-single-property-change msgend 'invisible)
@@ -331,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
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index b5db5fe8764..610fb149a94 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -1272,6 +1272,50 @@ erc-process-input-line
(should-not calls))))))
+(defmacro erc-tests--equal-including-properties (a b)
+ (list (if (< emacs-major-version 29)
+ 'ert-equal-including-properties
+ 'equal-including-properties)
+ a b))
+
+(ert-deftest erc--merge-prop ()
+ (with-current-buffer (get-buffer-create "*erc-test*")
+ ;; Baseline.
+ (insert "abc\n")
+ (erc--merge-prop 1 3 'erc-test 'x)
+ (should (erc-tests--equal-including-properties
+ (buffer-substring 1 4) #("abc" 0 2 (erc-test x))))
+ (erc--merge-prop 1 3 'erc-test 'y)
+ (should (erc-tests--equal-including-properties
+ (buffer-substring 1 4) #("abc" 0 2 (erc-test (y x)))))
+
+ ;; Multiple intervals.
+ (goto-char (point-min))
+ (insert "def\n")
+ (erc--merge-prop 1 2 'erc-test 'x)
+ (erc--merge-prop 2 3 'erc-test 'y)
+ (should (erc-tests--equal-including-properties
+ (buffer-substring 1 4)
+ #("def" 0 1 (erc-test x) 1 2 (erc-test y))))
+ (erc--merge-prop 1 3 'erc-test 'z)
+ (should (erc-tests--equal-including-properties
+ (buffer-substring 1 4)
+ #("def" 0 1 (erc-test (z x)) 1 2 (erc-test (z y)))))
+
+ ;; New val as list.
+ (goto-char (point-min))
+ (insert "ghi\n")
+ (erc--merge-prop 2 3 'erc-test '(y z))
+ (should (erc-tests--equal-including-properties
+ (buffer-substring 1 4) #("ghi" 1 2 (erc-test (y z)))))
+ (erc--merge-prop 1 3 'erc-test '(w x))
+ (should (erc-tests--equal-including-properties
+ (buffer-substring 1 4)
+ #("ghi" 0 1 (erc-test (w x)) 1 2 (erc-test (w x y z)))))
+
+ (when noninteractive
+ (kill-buffer))))
+
(ert-deftest erc--split-string-shell-cmd ()
;; Leading and trailing space
@@ -1488,12 +1532,6 @@ erc-message
(kill-buffer "ExampleNet")
(kill-buffer "#chan")))
-(defmacro erc-tests--equal-including-properties (a b)
- (list (if (< emacs-major-version 29)
- 'ert-equal-including-properties
- 'equal-including-properties)
- a b))
-
(ert-deftest erc-format-privmessage ()
;; Basic PRIVMSG
(should (erc-tests--equal-including-properties
--
2.41.0
next prev parent reply other threads:[~2023-07-20 13:29 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <87bkh21gfa.fsf@neverwas.me>
2023-07-05 14:03 ` bug#64301: 30.0.50; ERC 5.6: Make speaker labels easier to work with J.P.
2023-07-08 14:19 ` J.P.
[not found] ` <87sf9y32q9.fsf@neverwas.me>
2023-07-14 2:20 ` J.P.
[not found] ` <87zg3zqlnr.fsf@neverwas.me>
2023-07-15 14:05 ` J.P.
[not found] ` <87cz0tnubk.fsf@neverwas.me>
2023-07-20 13:29 ` J.P. [this message]
[not found] ` <871qh2iudy.fsf@neverwas.me>
2023-07-23 14:00 ` J.P.
2023-06-26 13:50 J.P.
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='871qh2iudy.fsf__32737.8316478402$1689859820$gmane$org@neverwas.me' \
--to=jp@neverwas.me \
--cc=64301-done@debbugs.gnu.org \
--cc=emacs-erc@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).