mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-15 10:30:25 -08:00
Don't nest date stamp insertions in erc-stamp
* etc/ERC-NEWS: Don't mention certain insertion-adjacent hooks being suppressed for date stamps, which is no longer true. * lisp/erc/erc-common.el (erc--solo): New utility function. * lisp/erc/erc-fill.el (erc-fill-wrap): Don't move last-message marker when encountering a date stamp. * lisp/erc/erc-stamp.el (erc-stamp--recover-on-reconnect): Restore `erc-stamp--date-stamps' on reconnect and rejoin. (erc-stamp--insert-date-hook): Fix erroneous doc string. (erc-stamp--date): New struct type. (erc-stamp--deferred-date-stamp): New internal variable to pass state between hook members. (erc-stamp--date-stamps): New internal variable to store a reference to all inserted timestamps. (erc-stamp--propertize-left-date-stamp): Don't hide messages because this function runs on `erc-insert-modify-hook'. Prefer doing so later, in `erc-insert-post-hook'. (erc-stamp--find-insertion-point): New helper function. (erc-stamp--insert-date-stamp-as-phony-message): Remove. (erc-stamp--lr-date-on-pre-modify): Remove function. Portions of body now appear in `erc-stamp--defer-date-insertion-on-post-modify'. (erc-stamp--defer-date-insertion-on-post-modify) (erc-stamp--defer-date-insertion-on-post-insert) (erc-stamp--defer-date-insertion-on-post-send): New functions, although the first incorporates parts of the now defunct `erc-stamp--lr-date-on-pre-modify'. (erc-stamp--date-mode): Update hook-member functions. (erc-stamp-prepend-date-stamps-p): Revise doc. (erc-insert-timestamp-left-and-right): Remove code to initialize a date stamp in place through a nested call to `erc-display-message'. Instead, "pre-render" date stamp and stash it for retrieval by the function `erc-stamp--defer-date-insertion-on-post-modify'. (erc-stamp--setup): Kill variables `erc-stamp--deferred-date-stamp' and `erc-stamp--date-stamps'. (erc-stamp--reset-on-clear): Remove trimmed stamps from `erc-stamp--date-stamps'. * lisp/erc/erc.el (erc--msg-props): Document `erc--hide' in doc string. (erc--with-inserted-msg): Remove unused macro. (erc--insert-line-splice-function): New variable. (erc--with-spliced-insertion): New macro. (erc--insert-line-function): Expand doc string. (erc--remove-from-prop-value-list): Tweak doc string. (erc--insert-before-markers-transplanting-hidden): New function. (erc--hide-message): Remember managed `invisible' prop value. Do so by recording them in the `erc--hide' "msg prop". (erc--delete-inserted-message, erc--delete-inserted-message-naively): Rename former to latter to emphasize that it's largely impractical for general use. (erc--ranked-properties): Add `erc--hide'. * test/lisp/erc/erc-button-tests.el (erc-button-tests--erc-button-alist--function-as-form): Use `erc-display-message' helper. * test/lisp/erc/erc-fill-tests.el (erc-fill-tests--insert-privmsg) (erc-fill-tests--wrap-populate, erc-fill-wrap-tests--merge-action) (erc-fill-line-spacing): Use `erc-display-message' wrappers to intercept `erc-timer-hook' modifications. * test/lisp/erc/erc-scenarios-match.el (erc-scenarios-match--invisible-stamp): Add convenience commands to `extended-command-history' when running interactively. * test/lisp/erc/erc-tests.el (erc--insert-before-markers-transplanting-hidden): New test. (erc--delete-inserted-message, erc--delete-inserted-message-naively): Update test name as well as namesake function in body. * test/lisp/erc/resources/erc-scenarios-common.el (erc-scenarios-common-with-cleanup): Validate `erc-stamp--date-stamps' members after every scenario test. (erc-scenarios-common--assert-date-stamps): New function. * test/lisp/erc/resources/erc-tests-common.el: Require `erc-stamp' atop file when compiling. (erc-tests--common-display-message) (erc-tests-common-display-message) (erc-tests-common-with-date-aware-display-message): New functions and macro for running `erc-display-message' while intercepting additions to `erc-timer-hook' made by date-stamp-related post-insertion hooks. (erc-tests-common-snapshot-compare): Insert expected output into its own buffer for easier review during interactive sessions. This change is unrelated to the rest of this commit. (Bug#60936)
This commit is contained in:
parent
21b372a57b
commit
86184cba21
11 changed files with 328 additions and 147 deletions
|
|
@ -617,6 +617,15 @@ the resulting variables will end up with more useful doc strings."
|
|||
"Return position of CHAR in STRING or nil if not found."
|
||||
(inline-quote (string-search (string ,char) ,string)))
|
||||
|
||||
(define-inline erc--solo (list-or-atom)
|
||||
"If LIST-OR-ATOM is a list of one element, return that element.
|
||||
Otherwise, return LIST-OR-ATOM."
|
||||
(inline-letevals (list-or-atom)
|
||||
(inline-quote
|
||||
(if (and (consp ,list-or-atom) (null (cdr ,list-or-atom)))
|
||||
(car ,list-or-atom)
|
||||
,list-or-atom))))
|
||||
|
||||
(defmacro erc--doarray (spec &rest body)
|
||||
"Map over ARRAY, running BODY with VAR bound to iteration element.
|
||||
Behave more or less like `seq-doseq', but tailor operations for
|
||||
|
|
|
|||
|
|
@ -674,8 +674,6 @@ See `erc-fill-wrap-mode' for details."
|
|||
(skip-syntax-forward "^-")
|
||||
(forward-char)
|
||||
(cond ((eq msg-prop 'datestamp)
|
||||
(when erc-fill--wrap-last-msg
|
||||
(set-marker erc-fill--wrap-last-msg (point-min)))
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(skip-chars-backward "\n")
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@ from entering them and instead jump over them."
|
|||
(when-let ((priors (or erc--server-reconnecting erc--target-priors)))
|
||||
(dolist (var '(erc-timestamp-last-inserted
|
||||
erc-timestamp-last-inserted-left
|
||||
erc-timestamp-last-inserted-right))
|
||||
erc-timestamp-last-inserted-right
|
||||
erc-stamp--date-stamps))
|
||||
(when-let (existing (alist-get var priors))
|
||||
(set var existing)))))
|
||||
|
||||
|
|
@ -652,7 +653,7 @@ printed just after each line's text (no alignment)."
|
|||
(erc-put-text-property from (1+ (point)) 'cursor-intangible t)))))
|
||||
|
||||
(defvar erc-stamp--insert-date-hook nil
|
||||
"Functions appended to send and modify hooks when inserting date stamp.")
|
||||
"Hook run when inserting a date stamp.")
|
||||
|
||||
(defvar-local erc-stamp--date-format-end nil
|
||||
"Tristate value indicating how and whether date stamps have been set up.
|
||||
|
|
@ -661,9 +662,27 @@ stamps. An integer marks the `substring' TO parameter for
|
|||
truncating `erc-timestamp-format-left' prior to rendering. A
|
||||
value of t means the option's value doesn't require trimming.")
|
||||
|
||||
(defun erc-stamp--propertize-left-date-stamp ()
|
||||
;; This struct and its namesake variable exist to assist in testing.
|
||||
(cl-defstruct erc-stamp--date
|
||||
"Data relevant to life cycle of date-stamp insertion."
|
||||
( ts (error "Missing `ts' field") :type (or cons integer)
|
||||
:documentation "Time recorded by `erc-insert-timestamp-left-and-right'.")
|
||||
( str (error "Missing `str' field") :type string
|
||||
:documentation "Stamp rendered by `erc-insert-timestamp-left-and-right'.")
|
||||
( fn nil :type (or null function)
|
||||
:documentation "Deferred insertion function created by post-modify hook.")
|
||||
( marker (make-marker) :type marker
|
||||
:documentation "Insertion marker."))
|
||||
|
||||
(defvar-local erc-stamp--deferred-date-stamp nil
|
||||
"Active `erc-stamp--date' instance.
|
||||
Non-nil between insertion-modification and \"done\" (or timer) hook.")
|
||||
|
||||
(defvar-local erc-stamp--date-stamps nil
|
||||
"List of stamps in the current buffer.")
|
||||
|
||||
(defun erc-stamp--propertize-left-date-stamp (&rest _)
|
||||
(add-text-properties (point-min) (1- (point-max)) '(field erc-timestamp))
|
||||
(erc--hide-message 'timestamp)
|
||||
(run-hooks 'erc-stamp--insert-date-hook))
|
||||
|
||||
(defun erc-stamp--format-date-stamp (ct)
|
||||
|
|
@ -680,6 +699,16 @@ value of t means the option's value doesn't require trimming.")
|
|||
0 erc-stamp--date-format-end)
|
||||
erc-timestamp-format-left))))
|
||||
|
||||
(defun erc-stamp--find-insertion-point (p target-time)
|
||||
"Scan buffer backwards from P looking for TARGET-TIME.
|
||||
Return P or, if found, a position less than P."
|
||||
(while-let ((q (previous-single-property-change (1- p) 'erc--ts))
|
||||
(qq (erc--get-inserted-msg-beg q))
|
||||
(ts (get-text-property qq 'erc--ts))
|
||||
((not (time-less-p ts target-time))))
|
||||
(setq p qq))
|
||||
p)
|
||||
|
||||
(defun erc-stamp-inserting-date-stamp-p ()
|
||||
"Return non-nil if the narrowed buffer contains a date stamp.
|
||||
Expect to be called by members of `erc-insert-modify-hook' and
|
||||
|
|
@ -687,75 +716,77 @@ Expect to be called by members of `erc-insert-modify-hook' and
|
|||
inserted is a date stamp."
|
||||
(erc--check-msg-prop 'erc--msg 'datestamp))
|
||||
|
||||
;; Calling `erc-display-message' from within a hook it's currently
|
||||
;; running is roundabout, but it's a definite means of ensuring hooks
|
||||
;; can act on the date stamp as a standalone message to do things like
|
||||
;; adjust invisibility props.
|
||||
(defun erc-stamp--insert-date-stamp-as-phony-message (string)
|
||||
(cl-assert (string-empty-p string))
|
||||
(setq string erc-timestamp-last-inserted-left)
|
||||
(let ((erc-stamp--skip t)
|
||||
(erc-insert-modify-hook `(,@erc-insert-modify-hook
|
||||
erc-stamp--propertize-left-date-stamp))
|
||||
(erc--insert-line-function #'insert-before-markers)
|
||||
;; Don't run hooks that aren't expecting a narrowed buffer.
|
||||
(erc-insert-pre-hook nil)
|
||||
(erc-insert-done-hook nil))
|
||||
(erc-display-message nil nil (current-buffer) string)))
|
||||
(defun erc-stamp--defer-date-insertion-on-post-modify (hook-var)
|
||||
"Schedule a date stamp to be inserted via HOOK-VAR.
|
||||
Do so when `erc-stamp--deferred-date-stamp' and its `fn' slot are
|
||||
non-nil."
|
||||
(when-let ((data erc-stamp--deferred-date-stamp)
|
||||
((null (erc-stamp--date-fn data)))
|
||||
(ct (erc-stamp--date-ts data))
|
||||
(rendered (erc-stamp--date-str data))
|
||||
(buffer (current-buffer))
|
||||
(symbol (make-symbol "erc-stamp--insert-date"))
|
||||
(marker (setf (erc-stamp--date-marker data) (point-min-marker))))
|
||||
(setf (erc-stamp--date-fn data) symbol)
|
||||
(fset symbol
|
||||
(lambda (&rest _)
|
||||
(remove-hook hook-var symbol)
|
||||
(when (buffer-live-p buffer)
|
||||
(with-current-buffer buffer
|
||||
(setq erc-stamp--date-stamps
|
||||
(cl-sort (cons data erc-stamp--date-stamps) #'time-less-p
|
||||
:key #'erc-stamp--date-ts))
|
||||
(setq erc-stamp--deferred-date-stamp nil)
|
||||
(let* ((aligned (erc-stamp--time-as-day ct))
|
||||
(erc-stamp--current-time aligned)
|
||||
(erc--msg-props (map-into '((erc--msg . datestamp))
|
||||
'hash-table))
|
||||
(erc-insert-post-hook
|
||||
`(,(lambda ()
|
||||
(set-marker marker (point-min))
|
||||
(set-marker-insertion-type marker t)
|
||||
(erc--hide-message 'timestamp))
|
||||
,@erc-insert-post-hook))
|
||||
(erc-insert-timestamp-function
|
||||
#'erc-stamp--propertize-left-date-stamp)
|
||||
(pos (erc-stamp--find-insertion-point marker aligned))
|
||||
;;
|
||||
erc-timestamp-format erc-away-timestamp-format)
|
||||
(erc--with-spliced-insertion pos
|
||||
(erc-display-message nil nil (current-buffer) rendered))
|
||||
(setf (erc-stamp--date-ts data) aligned))
|
||||
(setq erc-timestamp-last-inserted-left rendered)))))
|
||||
(add-hook hook-var symbol -90)))
|
||||
|
||||
(defun erc-stamp--lr-date-on-pre-modify (_)
|
||||
(when-let (((not erc-stamp--skip))
|
||||
(ct (erc-stamp--current-time))
|
||||
(rendered (erc-stamp--format-date-stamp ct))
|
||||
((not (string-equal rendered erc-timestamp-last-inserted-left)))
|
||||
(erc-insert-timestamp-function
|
||||
#'erc-stamp--insert-date-stamp-as-phony-message))
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(narrow-to-region (or erc--insert-marker erc-insert-marker)
|
||||
(or erc--insert-marker erc-insert-marker))
|
||||
;; Ensure all hooks, like `erc-stamp--insert-date-hook', only
|
||||
;; see the let-bound value below during `erc-add-timestamp'.
|
||||
(setq erc-timestamp-last-inserted-left nil)
|
||||
(let* ((aligned (erc-stamp--time-as-day ct))
|
||||
(erc-stamp--current-time aligned)
|
||||
;; Forget current `erc--cmd', etc.
|
||||
(erc--msg-props (map-into `((erc--msg . datestamp))
|
||||
'hash-table))
|
||||
(erc-timestamp-last-inserted-left rendered)
|
||||
erc-timestamp-format erc-away-timestamp-format)
|
||||
(erc-add-timestamp))
|
||||
(setq erc-timestamp-last-inserted-left rendered)))))
|
||||
(defun erc-stamp--defer-date-insertion-on-post-insert ()
|
||||
(erc-stamp--defer-date-insertion-on-post-modify 'erc-timer-hook))
|
||||
|
||||
;; This minor mode is just a placeholder and currently unhelpful for
|
||||
;; managing complexity. A useful version would leave a marker during
|
||||
;; post-modify hooks and then perform insertions (before markers)
|
||||
;; during "done" hooks. This would enable completely decoupling from
|
||||
;; and possibly deprecating `erc-insert-timestamp-left-and-right'.
|
||||
;; However, doing this would require expanding the internal API to
|
||||
;; include insertion and deletion handlers for twiddling and massaging
|
||||
;; text properties based on context immediately after modifying text
|
||||
;; earlier in a buffer (away from `erc-insert-marker'). Without such
|
||||
;; handlers, things like "merged" `fill-wrap' speakers and invisible
|
||||
;; messages may be damaged by buffer modifications.
|
||||
(defun erc-stamp--defer-date-insertion-on-post-send ()
|
||||
(erc-stamp--defer-date-insertion-on-post-modify 'erc-send-completed-hook))
|
||||
|
||||
;; This minor mode is hopefully just a placeholder because it's quite
|
||||
;; unhelpful for managing complexity. A useful version would exist as
|
||||
;; a standalone module to allow completely decoupling from and
|
||||
;; possibly deprecating `erc-insert-timestamp-left-and-right'.
|
||||
(define-minor-mode erc-stamp--date-mode
|
||||
"Insert date stamps as standalone messages."
|
||||
:interactive nil
|
||||
(if erc-stamp--date-mode
|
||||
(progn (add-hook 'erc-insert-pre-hook
|
||||
#'erc-stamp--lr-date-on-pre-modify 10 t)
|
||||
(add-hook 'erc-pre-send-functions
|
||||
#'erc-stamp--lr-date-on-pre-modify 10 t))
|
||||
(progn
|
||||
(add-hook 'erc-insert-post-hook
|
||||
#'erc-stamp--defer-date-insertion-on-post-insert 0 t)
|
||||
(add-hook 'erc-send-post-hook
|
||||
#'erc-stamp--defer-date-insertion-on-post-send 0 t))
|
||||
(kill-local-variable 'erc-timestamp-last-inserted-left)
|
||||
(remove-hook 'erc-insert-pre-hook
|
||||
#'erc-stamp--lr-date-on-pre-modify t)
|
||||
(remove-hook 'erc-pre-send-functions
|
||||
#'erc-stamp--lr-date-on-pre-modify t)))
|
||||
(remove-hook 'erc-insert-post-hook
|
||||
#'erc-stamp--defer-date-insertion-on-post-insert t)
|
||||
(remove-hook 'erc-send-post-hook
|
||||
#'erc-stamp--defer-date-insertion-on-post-send t)))
|
||||
|
||||
(defvar erc-stamp-prepend-date-stamps-p nil
|
||||
"When non-nil, date stamps are not independent messages.
|
||||
This flag restores pre-5.6 behavior in which date stamps formed
|
||||
the leading portion of affected messages. Beware that enabling
|
||||
This flag restores pre-5.6 behavior in which date stamps were
|
||||
prepended to normal chat messages. Beware that enabling
|
||||
this degrades the user experience by causing 5.6+ features, like
|
||||
`fill-wrap', dynamic invisibility, etc., to malfunction. When
|
||||
non-nil, none of the newline twiddling mentioned in the doc
|
||||
|
|
@ -775,26 +806,17 @@ in the latter (if any) as part of the `erc-timestamp' field.
|
|||
Allow the stamp's `invisible' property to span that same interval
|
||||
but also cover the previous newline, in order to satisfy folding
|
||||
requirements related to `erc-legacy-invisible-bounds-p'.
|
||||
Additionally, ensure every date stamp is identifiable as such so
|
||||
that internal modules can easily distinguish between other
|
||||
left-sided stamps and date stamps inserted by this function."
|
||||
Additionally, ensure every date stamp is identifiable as such via
|
||||
the function `erc-stamp-inserting-date-stamp-p' so that internal
|
||||
modules can easily distinguish between other left-sided stamps
|
||||
and date stamps inserted by this function."
|
||||
(unless (or erc-stamp--date-format-end erc-stamp-prepend-date-stamps-p
|
||||
(and (or (null erc-timestamp-format-left)
|
||||
(string-empty-p ; compat
|
||||
(string-trim erc-timestamp-format-left "\n")))
|
||||
(always (erc-stamp--date-mode -1))
|
||||
(setq erc-stamp-prepend-date-stamps-p t)))
|
||||
(erc-stamp--date-mode +1)
|
||||
;; Hooks used by ^ are the preferred means of inserting date
|
||||
;; stamps. But they'll never see this inaugural message, so it
|
||||
;; must be handled specially.
|
||||
(let ((erc--insert-marker (point-min-marker))
|
||||
(end-marker (point-max-marker)))
|
||||
(set-marker-insertion-type erc--insert-marker t)
|
||||
(erc-stamp--lr-date-on-pre-modify nil)
|
||||
(narrow-to-region erc--insert-marker end-marker)
|
||||
(set-marker end-marker nil)
|
||||
(set-marker erc--insert-marker nil)))
|
||||
(erc-stamp--date-mode +1))
|
||||
(let* ((ct (erc-stamp--current-time))
|
||||
(ts-right (with-suppressed-warnings
|
||||
((obsolete erc-timestamp-format-right))
|
||||
|
|
@ -805,12 +827,22 @@ left-sided stamps and date stamps inserted by this function."
|
|||
;; "prepended" date stamps as well. However, since this is a
|
||||
;; compatibility oriented code path, and pre-5.6 did no such
|
||||
;; thing, better to punt.
|
||||
(when-let ((erc-stamp-prepend-date-stamps-p)
|
||||
(ts-left (erc-format-timestamp ct erc-timestamp-format-left))
|
||||
((not (string= ts-left erc-timestamp-last-inserted-left))))
|
||||
(goto-char (point-min))
|
||||
(erc-put-text-property 0 (length ts-left) 'field 'erc-timestamp ts-left)
|
||||
(insert (setq erc-timestamp-last-inserted-left ts-left)))
|
||||
(if-let ((erc-stamp-prepend-date-stamps-p)
|
||||
(ts-left (erc-format-timestamp ct erc-timestamp-format-left))
|
||||
((not (string= ts-left erc-timestamp-last-inserted-left))))
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(erc-put-text-property 0 (length ts-left) 'field 'erc-timestamp
|
||||
ts-left)
|
||||
(insert (setq erc-timestamp-last-inserted-left ts-left)))
|
||||
(when-let
|
||||
(((null erc-stamp--deferred-date-stamp))
|
||||
(rendered (erc-stamp--format-date-stamp ct))
|
||||
((not (string-equal rendered erc-timestamp-last-inserted-left)))
|
||||
((null (cl-find rendered erc-stamp--date-stamps
|
||||
:test #'string= :key #'erc-stamp--date-str))))
|
||||
(setq erc-stamp--deferred-date-stamp
|
||||
(make-erc-stamp--date :ts ct :str rendered))))
|
||||
;; insert right timestamp
|
||||
(let ((erc-timestamp-only-if-changed-flag t)
|
||||
(erc-timestamp-last-inserted erc-timestamp-last-inserted-right))
|
||||
|
|
@ -924,6 +956,8 @@ For `erc-hide-timestamps, modify `buffer-invisibility-spec'."
|
|||
(kill-local-variable 'erc-stamp--last-stamp)
|
||||
(kill-local-variable 'erc-timestamp-last-inserted)
|
||||
(kill-local-variable 'erc-timestamp-last-inserted-right)
|
||||
(kill-local-variable 'erc-stamp--deferred-date-stamp)
|
||||
(kill-local-variable 'erc-stamp--date-stamps)
|
||||
(kill-local-variable 'erc-stamp--date-format-end)))
|
||||
|
||||
(defun erc-hide-timestamps ()
|
||||
|
|
@ -992,7 +1026,12 @@ with the option `erc-echo-timestamps', see the companion option
|
|||
(move-marker erc-last-saved-position (1- (point-max))))
|
||||
|
||||
(defun erc-stamp--reset-on-clear (pos)
|
||||
"Forget last-inserted stamps when POS is at insert marker."
|
||||
"Forget last-inserted stamps when POS is at insert marker.
|
||||
And discard stale references in `erc-stamp--date-stamps'."
|
||||
(when erc-stamp--date-stamps
|
||||
(setq erc-stamp--date-stamps
|
||||
(seq-filter (lambda (o) (> (erc-stamp--date-marker o) pos))
|
||||
erc-stamp--date-stamps)))
|
||||
(when (= pos (1- erc-insert-marker))
|
||||
(when erc-stamp--date-mode
|
||||
(add-hook 'erc-stamp--insert-date-hook
|
||||
|
|
|
|||
|
|
@ -186,6 +186,10 @@ as of ERC 5.6:
|
|||
hooks that the current message should not affect stateful
|
||||
operations, such as recording a channel's most recent speaker
|
||||
|
||||
- `erc--hide': a symbol or list of symbols added as an `invisible'
|
||||
prop value to the entire message, starting *before* the preceding
|
||||
newline and ending before the trailing newline
|
||||
|
||||
This is an internal API, and the selection of related helper
|
||||
utilities is fluid and provisional. As of ERC 5.6, see the
|
||||
functions `erc--check-msg-prop' and `erc--get-inserted-msg-prop'.")
|
||||
|
|
@ -3278,14 +3282,36 @@ if not found."
|
|||
(and-let* ((stack-pos (erc--get-inserted-msg-beg (point))))
|
||||
(get-text-property stack-pos prop)))
|
||||
|
||||
(defmacro erc--with-inserted-msg (&rest body)
|
||||
"Simulate narrowing performed for send and insert hooks, and run BODY.
|
||||
Expect callers to know that this doesn't wrap BODY in
|
||||
`with-silent-modifications' or bind a temporary `erc--msg-props'."
|
||||
`(when-let ((bounds (erc--get-inserted-msg-bounds)))
|
||||
(save-restriction
|
||||
(narrow-to-region (car bounds) (1+ (cdr bounds)))
|
||||
,@body)))
|
||||
;; FIXME improve this nascent "message splicing" facility to include a
|
||||
;; means for modules to adjust inserted messages on either side of the
|
||||
;; splice position as well as to modify the spliced-in message itself
|
||||
;; before and after each insertion-related hook runs. Also add a
|
||||
;; counterpart to `erc--with-spliced-insertion' for deletions.
|
||||
(defvar erc--insert-line-splice-function
|
||||
#'erc--insert-before-markers-transplanting-hidden
|
||||
"Function to handle in-place insertions away from prompt.
|
||||
Modules that display \"stateful\" messages, where one message's content
|
||||
depends on prior messages, should advise this locally as needed.")
|
||||
|
||||
(defmacro erc--with-spliced-insertion (marker-or-pos &rest body)
|
||||
"In BODY, ensure `erc-insert-line' inserts messages at MARKER-OR-POS.
|
||||
If MARKER-OR-POS is a marker, let it advance normally (and permanently)
|
||||
with each insertion. Allow modules to influence insertion by binding
|
||||
`erc--insert-line-function' to `erc--insert-line-splice-function' around
|
||||
BODY. Note that as of ERC 5.6, this macro cannot handle multiple
|
||||
successive calls to `erc-insert-line' in BODY, such as when replaying
|
||||
a history backlog."
|
||||
(declare (indent 1))
|
||||
(let ((marker (make-symbol "marker")))
|
||||
`(progn
|
||||
(cl-assert (= ?\n (char-before ,marker-or-pos)))
|
||||
(cl-assert (null erc--insert-line-function))
|
||||
(let* ((,marker (and (not (markerp ,marker-or-pos))
|
||||
(copy-marker ,marker-or-pos)))
|
||||
(erc--insert-marker (or ,marker ,marker-or-pos))
|
||||
(erc--insert-line-function erc--insert-line-splice-function))
|
||||
(prog1 (progn ,@body)
|
||||
(when ,marker (set-marker ,marker nil)))))))
|
||||
|
||||
(defun erc--traverse-inserted (beg end fn)
|
||||
"Visit messages between BEG and END and run FN in narrowed buffer.
|
||||
|
|
@ -3325,7 +3351,11 @@ that this flag and the behavior it restores may disappear at any
|
|||
time, so if you need them, please let ERC know with \\[erc-bug].")
|
||||
|
||||
(defvar erc--insert-line-function nil
|
||||
"When non-nil, an alterntive to `insert' for inserting messages.")
|
||||
"When non-nil, an `insert'-like function for inserting messages.
|
||||
Modules, like `fill-wrap', that leave a marker at the beginning of an
|
||||
inserted message clearly want that marker to advance along with text
|
||||
inserted at that position. This can be addressed by binding this
|
||||
variable to `insert-before-markers' around calls to `display-message'.")
|
||||
|
||||
(defvar erc--insert-marker nil
|
||||
"Internal override for `erc-insert-marker'.")
|
||||
|
|
@ -3509,7 +3539,7 @@ also `erc-button-add-face'."
|
|||
end (next-single-property-change pos prop object to)))))
|
||||
|
||||
(defun erc--remove-from-prop-value-list (from to prop val &optional object)
|
||||
"Remove VAL from text prop value between FROM and TO.
|
||||
"Remove VAL from text PROP value between FROM and TO.
|
||||
If current value is VAL itself, remove the property entirely.
|
||||
When VAL is a list, act as if this function were called
|
||||
repeatedly with VAL set to each of VAL's members."
|
||||
|
|
@ -3573,19 +3603,45 @@ preceding newline to its last non-newline character.")
|
|||
(make-obsolete-variable 'erc-legacy-invisible-bounds-p
|
||||
"decremented interval now permanent" "30.1")
|
||||
|
||||
(defun erc--insert-before-markers-transplanting-hidden (string)
|
||||
"Insert STRING before markers and migrate any `invisible' props.
|
||||
Expect to be called with `point' at the start of an inserted message,
|
||||
i.e., one with an `erc--msg' property. Check the message prop header
|
||||
for invisibility props advertised via `erc--hide'. When found, remove
|
||||
them from the previous newline, and add them to the newline suffixing
|
||||
the inserted version of STRING."
|
||||
(let* ((after (and (not erc-legacy-invisible-bounds-p)
|
||||
(get-text-property (point) 'erc--hide)))
|
||||
(before (and after (get-text-property (1- (point)) 'invisible)))
|
||||
(a (and after (ensure-list after)))
|
||||
(b (and before (ensure-list before)))
|
||||
(new (and before (erc--solo (cl-intersection b a)))))
|
||||
(when new
|
||||
(erc--remove-from-prop-value-list (1- (point)) (point) 'invisible a))
|
||||
(prog1 (insert-before-markers string)
|
||||
(when new
|
||||
(erc--merge-prop (1- (point)) (point) 'invisible new)))))
|
||||
|
||||
(defun erc--hide-message (value)
|
||||
"Apply `invisible' text-property with VALUE to current message.
|
||||
Expect to run in a narrowed buffer during message insertion.
|
||||
Begin the invisible interval at the previous message's trailing
|
||||
newline and end before the current message's. If the preceding
|
||||
message ends in a double newline or there is no previous message,
|
||||
don't bother including the preceding newline."
|
||||
don't bother including the preceding newline. Additionally,
|
||||
record VALUE as part of the `erc--hide' property in the
|
||||
\"msg-props\" header."
|
||||
(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))
|
||||
(let ((old-hide (erc--check-msg-prop 'erc--hide))
|
||||
(beg (point-min))
|
||||
(end (point-max)))
|
||||
(puthash 'erc--hide (if old-hide
|
||||
`(,value . ,(ensure-list old-hide))
|
||||
value)
|
||||
erc--msg-props)
|
||||
(save-restriction
|
||||
(widen)
|
||||
(when (or (<= beg 4) (= ?\n (char-before (- beg 2))))
|
||||
|
|
@ -3604,9 +3660,11 @@ Treat ARG in a manner similar to mode toggles defined by
|
|||
(when (or (not arg) (natnump arg))
|
||||
(add-to-invisibility-spec prop))))
|
||||
|
||||
(defun erc--delete-inserted-message (beg-or-point &optional end)
|
||||
(defun erc--delete-inserted-message-naively (beg-or-point &optional end)
|
||||
"Remove message between BEG and END.
|
||||
Expect BEG and END to match bounds as returned by the macro
|
||||
Do this without updating messages on either side even if their
|
||||
appearance was somehow influenced by the newly absent message.
|
||||
Expect BEG and END to match bounds as returned by the function
|
||||
`erc--get-inserted-msg-bounds'. Ensure all markers residing at
|
||||
the start of the deleted message end up at the beginning of the
|
||||
subsequent message."
|
||||
|
|
@ -3626,7 +3684,7 @@ subsequent message."
|
|||
-1))))))))
|
||||
|
||||
(defvar erc--ranked-properties
|
||||
'(erc--msg erc--spkr erc--ts erc--cmd erc--ctcp erc--ephemeral))
|
||||
'(erc--msg erc--spkr erc--ts erc--cmd erc--hide erc--ctcp erc--ephemeral))
|
||||
|
||||
(defun erc--order-text-properties-from-hash (table)
|
||||
"Return a plist of text props from items in TABLE.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue