emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/substitute 847e29406a: Refactor highlighting to use our


From: ELPA Syncer
Subject: [elpa] externals/substitute 847e29406a: Refactor highlighting to use our own method
Date: Mon, 16 Jan 2023 23:58:10 -0500 (EST)

branch: externals/substitute
commit 847e29406ae40c60dd6f3426b38548f3c1cc0882
Author: Protesilaos Stavrou <info@protesilaos.com>
Commit: Protesilaos Stavrou <info@protesilaos.com>

    Refactor highlighting to use our own method
    
    The old design was simpler on our end but it had the disadvantage of
    highlighting targets regardless of their scope.  This was confusing as
    it would show matches outside the context of the given command.
    
    Thanks to Tomasz Hołubowicz for bringing this matter to my attention
    and for Jeremy Clark for recommending a look at the 'symbol-overlay'
    package:
    
https://lists.sr.ht/~protesilaos/general-issues/%3C87y1q27400.fsf%40alternateved.com%3E
---
 substitute.el | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 71 insertions(+), 10 deletions(-)

diff --git a/substitute.el b/substitute.el
index 85bcea487f..27c7ede54b 100644
--- a/substitute.el
+++ b/substitute.el
@@ -58,6 +58,11 @@ For a reference function, see `substitute-report-operation'."
   :group 'substitute
   :type 'hook)
 
+(defface substitute-match
+  `((t :inherit ,(substitute--determine-face)))
+  "Face to highlight matches of the given target."
+  :group 'substitute)
+
 (defvar substitute--history '()
   "Minibuffer history for substitution commands.")
 
@@ -89,9 +94,21 @@ and related."
       face
     'secondary-selection))
 
+(defun substitute--remove-highlights ()
+  "Remove `substitute-match' overlays."
+  (remove-overlays nil nil 'face 'substitute-match))
+
+(defun substitute--add-highlight (beg end)
+  "Add overlay of `substitute-match' between BEG and END positions."
+  (goto-char beg)
+  (let ((highlight (make-overlay beg end)))
+    (overlay-put highlight 'priority 100)
+    (overlay-put highlight 'face 'substitute-match)))
+
 (defun substitute--prompt-without-highlight (target scope)
   "Prompt for string while referencing TARGET and SCOPE."
   (let ((pretty-target (substitute--prettify-target-description target)))
+    (substitute--collect-targets target scope)
     (read-string
      (format "Substitute `%s' %s with: "
              (propertize pretty-target 'face 'error)
@@ -107,9 +124,11 @@ Highlight the TARGET's matching occurences per the user 
option
   (let ((pretty-target (substitute--prettify-target-description target)))
     (unwind-protect
         (progn
-          (highlight-regexp target (substitute--determine-face))
+          (substitute--collect-targets target scope)
+          (substitute--highlight-targets)
           (substitute--prompt-without-highlight pretty-target scope))
-      (unhighlight-regexp target))))
+      (substitute--remove-highlights)
+      (setq-local substitute--last-matches nil))))
 
 (defun substitute--prompt (target scope)
   "Return appropriate prompt based on `substitute-highlight'.
@@ -157,19 +176,61 @@ Pass to it the TARGET and SCOPE arguments."
     ('defun (substitute--scope-current-defun))
     (_ (substitute--scope-top-of-buffer))))
 
-(defun substitute--operate (target sub &optional scope)
-  "Substitute TARGET with SUB in SCOPE.
-This is the subroutine of `substitute-target' and related."
-  (let ((search-fn (if (eq scope 'above) 're-search-backward 
're-search-forward))
-        count)
+
+(defvar-local substitute--last-matches nil
+  "Alist of the last matching substitution targets.
+Each entry is a list of the symbol and its buffer positions.")
+
+(defun substitute--collect-targets (target scope)
+  "Store occurrences of TARGET in SCOPE in `substitute--last-matches'."
+  (let ((search-fn (if (eq scope 'above) 're-search-backward 
're-search-forward)))
     (save-excursion
       (save-restriction
         (substitute--setup-scope target scope)
         (while (funcall search-fn target nil t)
-          (push (match-string-no-properties 0) count)
-          (replace-match sub nil t))))
+          (push (list (match-string-no-properties 0)
+                      (match-beginning 0)
+                      (match-end 0))
+                substitute--last-matches))))
+    substitute--last-matches))
+
+(defun substitute--beg-end (beg end)
+  "Determine if BEG is smaller than END and return ordered list."
+  (if (< beg end)
+      (list beg end)
+    (list end beg)))
+
+(defun substitute--highlight-targets ()
+  "Highlight `substitute--last-matches'."
+  (when-let ((targets substitute--last-matches))
+    (save-excursion
+      (save-restriction
+        (mapcar (lambda (target)
+                  (substitute--add-highlight (nth 1 target)
+                                             (nth 2 target)))
+                targets)))))
+
+(defun substitute--replace-targets (sub)
+  "Replace `substitute--last-matches' target with SUB."
+  (when-let ((targets substitute--last-matches))
+    (save-excursion
+      (save-restriction
+        (mapcar (lambda (target)
+                  (let ((ps (substitute--beg-end (nth 1 target) (nth 2 
target))))
+                    (goto-char (car ps))
+                    (re-search-forward (car target))
+                    (replace-match sub)))
+                targets)))))
+
+(defun substitute--operate (target sub &optional scope)
+  "Operate on TARGET with SUB in SCOPE."
+  (let* ((targets (or substitute--last-matches
+                      (substitute--collect-targets target scope)))
+         (count (length targets)))
+    (substitute--replace-targets sub)
+    (setq-local substitute--last-matches nil)
     (run-hook-with-args 'substitute-post-replace-hook
-                        target sub (length count)
+                        target sub count
                         (substitute--scope-description scope))))
 
 (defun substitute--determine-target ()



reply via email to

[Prev in Thread] Current Thread [Next in Thread]