From 38760d4ba7bdb75ab8f7a14cd6388c951063bb80 Mon Sep 17 00:00:00 2001 From: "Kim F. Storm" Date: Sat, 14 Feb 2026 12:43:33 -0500 Subject: [PATCH] delsel.el: Use an overlay to track the replacement text This patch was sent back in 2014 as part of bug#18886. * lisp/delsel.el (delsel--replace-text-or-position): Delete var. (delete-selection-replacement-face): New defcustom. (delsel--replace-text, delsel--replace-overlay): New vars. (delete-active-region, delete-selection-repeat-replace-region): Use overlay to track replacement text instead of abusing the undo-list; this allows the text to be edited before it is used for substitutions. Add highlight to the replacement text. --- lisp/delsel.el | 92 ++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/lisp/delsel.el b/lisp/delsel.el index 72682a94b7c..f00f4843073 100644 --- a/lisp/delsel.el +++ b/lisp/delsel.el @@ -60,9 +60,24 @@ ;;; Code: -(defvar delete-selection-save-to-register nil +(defcustom delete-selection-save-to-register nil "If non-nil, deleted region text is stored in this register. -Value must be the register (key) to use.") +Value must be the register (key) to use." + :type '(choice + (const :tag "None" nil) + (character :tag "Register (Key)")) + :group 'editing-basics) + +(defcustom delete-selection-replacement-face 'highlight + "If non-nil, active region replacement text is shown in this face. +The highlighted text is the text that will be inserted by +the `delete-selection-repeat-replace-region' command." + :type 'face + :group 'editing-basics + :set (lambda (symbol value) + (set-default symbol value) + (if delsel--replace-overlay + (overlay-put delsel--replace-overlay 'face value)))) (defcustom delete-selection-temporary-region nil "Whether to delete only temporary regions. @@ -113,7 +128,8 @@ For compatibility with features and packages that are aware of (setq-default delete-selection-mode nil) ; But keep it globally disabled. ))) -(defvar delsel--replace-text-or-position nil) +(defvar delsel--replace-overlay nil) ;overlay +(defvar delsel--replace-text nil) ;text from overlay ;;;###autoload (defun delete-active-region (&optional killp) @@ -129,9 +145,14 @@ the active region is killed instead of deleted." (delete-selection-save-to-register (set-register delete-selection-save-to-register (funcall region-extract-function t)) - (setq delsel--replace-text-or-position - (cons (current-buffer) - (and (consp buffer-undo-list) (car buffer-undo-list))))) + (if delsel--replace-overlay + (move-overlay delsel--replace-overlay (point) (point) (current-buffer)) + (setq delsel--replace-overlay + (make-overlay (point) (point) (current-buffer) nil t)) + (if delete-selection-replacement-face + (overlay-put delsel--replace-overlay 'face + delete-selection-replacement-face))) + (setq delsel--replace-text nil)) (t (funcall region-extract-function 'delete-only)))) @@ -146,47 +167,30 @@ Just `\\[universal-argument]' means repeat until the end of the buffer's accessi (get-register delete-selection-save-to-register))) (count (if (consp arg) (point-max) (prefix-numeric-value current-prefix-arg)))) - (if (not (and old-text - (> (length old-text) 0) - (or (stringp delsel--replace-text-or-position) - (buffer-live-p (car delsel--replace-text-or-position))))) + (if (not (and old-text (> (length old-text) 0))) (message "No known previous replacement") ;; If this is the first use after overwriting regions, ;; find the replacement text by looking at the undo list. - (when (consp delsel--replace-text-or-position) - (let ((buffer (car delsel--replace-text-or-position)) - (elt (cdr delsel--replace-text-or-position))) - (setq delsel--replace-text-or-position nil) - (with-current-buffer buffer - (save-restriction - (widen) - ;; Find the text that replaced the region via the undo list. - (let ((ul buffer-undo-list) u s e) - (when elt - (while (consp ul) - (setq u (car ul) ul (cdr ul)) - (cond - ((eq u elt) ;; got it - (setq ul nil)) - ((and (consp u) (integerp (car u)) (integerp (cdr u))) - (if (and s (= (cdr u) s)) - (setq s (car u)) - (setq s (car u) e (cdr u))))))) - (cond ((and s e (<= s e) (= s (mark t))) - (setq delsel--replace-text-or-position - (filter-buffer-substring s e)) - (set-text-properties - 0 (length delsel--replace-text-or-position) - nil delsel--replace-text-or-position)) - ((and (null s) (eq u elt)) ;; Nothing inserted. - (setq delsel--replace-text-or-position "")) - (t - (message "Cannot locate replacement text")))))))) - (while (and (> count 0) - delsel--replace-text-or-position - (search-forward old-text nil t)) - (replace-match delsel--replace-text-or-position nil t) - (setq count (1- count)))))) + (when (and (null delsel--replace-text) + delsel--replace-overlay + (buffer-live-p (overlay-buffer delsel--replace-overlay))) + (with-current-buffer (overlay-buffer delsel--replace-overlay) + (let ((s (overlay-start delsel--replace-overlay)) + (e (overlay-end delsel--replace-overlay))) + (when (/= s e) + (setq delsel--replace-text + (filter-buffer-substring s e)) + (set-text-properties + 0 (length delsel--replace-text) + nil delsel--replace-text)))) + (delete-overlay delsel--replace-overlay)) + (if delsel--replace-text + (while (and (> count 0) + delsel--replace-text + (search-forward old-text nil t)) + (replace-match delsel--replace-text nil t) + (setq count (1- count))) + (message "Cannot locate replacement text"))))) (defun delete-selection-helper (type) "Delete selection according to TYPE: