From fc1b27a4338bca1c0b354252ccd542f16dc78626 Mon Sep 17 00:00:00 2001 From: Yikai Zhao Date: Sat, 30 Mar 2024 20:24:43 +0800 Subject: [PATCH 1/3] Add interactive highlights for ex global command pattern --- evil-commands.el | 23 +++++++++++++++++++++++ evil-tests.el | 34 +++++++++++++++++++++++++++++++++- evil-types.el | 1 + evil-vars.el | 9 +++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/evil-commands.el b/evil-commands.el index cd8a66d6..da92eab6 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -4222,6 +4222,29 @@ Use `evil-flush-lines' if INVERT is nil, or `evil-keep-lines' if not." (goto-char end-marker) (set-marker end-marker nil))) +(evil-ex-define-argument-type global + "This handler highlights the pattern in :g/pattern/cmd." + :runner + (lambda (flag &optional arg) + (with-current-buffer evil-ex-original-buffer + (cond + ((eq flag 'start) + (evil-ex-make-hl 'evil-ex-global + :face 'evil-ex-global-command-matches + :win (minibuffer-selected-window)) + (setq flag 'update)) + ((eq flag 'stop) + (evil-ex-delete-hl 'evil-ex-global))) + + (when (and evil-ex-global-command-interactive-highlight + (eq flag 'update)) + (condition-case err + (let ((pattern (car (evil-ex-parse-global (or arg ""))))) + (when (length> pattern 0) + (evil-ex-hl-change 'evil-ex-global + (evil-ex-make-pattern pattern evil-ex-search-case nil)))) + (user-error (evil-ex-echo (error-message-string err)))))))) + (evil-define-operator evil-ex-global (beg end pattern command &optional invert) "The Ex global command. diff --git a/evil-tests.el b/evil-tests.el index 01af6260..dec9b6ba 100644 --- a/evil-tests.el +++ b/evil-tests.el @@ -8865,17 +8865,39 @@ maybe we need one line more with some text\n") (ert-deftest evil-test-global () "Test `evil-ex-global'." :tags '(evil ex global) + + (defun evil-test-force-invoke-ex-hl-update-timer () + "Force invoke ex-hl-update-timer." + (when evil-ex-hl-update-timer + (timer-event-handler evil-ex-hl-update-timer))) + (advice-add 'evil-ex-hl-idle-update :after + #'evil-test-force-invoke-ex-hl-update-timer) + + (defun evil-test-save-last-ex-global-hls () + "Save current 'evil-ex-global highlights to buffer-local variable." + (when-let* ((hl (cdr (assq 'evil-ex-global evil-ex-active-highlights-alist))) + (ovs (evil-ex-hl-overlays hl))) + (setq ovs (sort ovs (lambda (a b) (< (overlay-start a) (overlay-start b))))) + (setq-local evil-test-last-ex-global-hls + (seq-map (lambda (ov) (buffer-substring-no-properties (overlay-start ov) (overlay-end ov))) + ovs)))) + (advice-add 'evil-ex-hl-update-highlights :after + #'evil-test-save-last-ex-global-hls) + (ert-info ("global delete") (evil-test-buffer "[n]o 1\nno 2\nno 3\nyes 4\nno 5\nno 6\nno 7\n" (":g/yes/d" [return]) + (should (equal evil-test-last-ex-global-hls '("yes"))) "no 1\nno 2\nno 3\n[n]o 5\nno 6\nno 7\n")) (ert-info ("global delete, specifying case sensitivty") (evil-test-buffer "[a]lpha bravo charlie\nalpha Bravo charlie\nalpha BRAVO charlie\nalpha delta charlie" (":g/\\CBravo/d" [return]) + (should (equal evil-test-last-ex-global-hls '("Bravo"))) "alpha bravo charlie\n[a]lpha BRAVO charlie\nalpha delta charlie" (":g/\\cBravo/d" [return]) + (should (equal evil-test-last-ex-global-hls '("bravo" "BRAVO"))) "alpha delta charlie")) (ert-info ("global delete with arg") (evil-test-buffer @@ -8908,6 +8930,7 @@ maybe we need one line more with some text\n") ("/yes" [return]) "no 1\nno 2\nno 3\nyes 4\nno 5\nno 6\nno 7\n" (":g//d" [return]) + (should (equal evil-test-last-ex-global-hls '("yes"))) "no 1\nno 2\nno 3\n[n]o 5\nno 6\nno 7\n" (":v//d" [return]) "")) @@ -8918,6 +8941,7 @@ maybe we need one line more with some text\n") ("/isearch" [return]) "no 1\nno 2\nno 3\nisearch 4\nno 5\nno 6\nno 7\n" (":g//d" [return]) + (should (equal evil-test-last-ex-global-hls '("isearch"))) "no 1\nno 2\nno 3\n[n]o 5\nno 6\nno 7\n" (":v//d" [return]) "")) @@ -8927,27 +8951,35 @@ maybe we need one line more with some text\n") (evil-test-buffer "this\nThis\n" (":g/this/d" [return]) + (should (equal evil-test-last-ex-global-hls '("this"))) "This\n")) (let ((evil-ex-search-case 'insensitive)) (evil-test-buffer "this\nThis\n" (":g/this/d" [return]) + (should (equal evil-test-last-ex-global-hls '("this" "This"))) "")) (let ((evil-ex-search-case 'smart)) (evil-test-buffer "this\nThis\n" (":g/this/d" [return]) + (should (equal evil-test-last-ex-global-hls '("this" "This"))) "") (evil-test-buffer "this\nThis\n" (":g/This/d" [return]) + (should (equal evil-test-last-ex-global-hls '("This"))) "this\n")))) (ert-info (":global should transform vim-style regexp when appropriate") (let ((evil-ex-search-vim-style-regexp t)) (evil-test-buffer "a\n1\nb\n2\nc\n3\n" (":g/\\d/>") - "a\n 1\nb\n 2\nc\n 3\n")))) + (should (equal evil-test-last-ex-global-hls '("1" "2" "3"))) + "a\n 1\nb\n 2\nc\n 3\n"))) + + (advice-remove 'evil-ex-hl-idle-update #'evil-test-force-invoke-ex-hl-update-timer) + (advice-remove 'evil-ex-hl-update-highlights #'evil-test-save-last-ex-global-hls)) (ert-deftest evil-test-normal () "Test `evil-ex-normal'." diff --git a/evil-types.el b/evil-types.el index 360316a6..d112f763 100644 --- a/evil-types.el +++ b/evil-types.el @@ -402,6 +402,7 @@ If visual state is inactive then those values are nil." (evil-define-interactive-code "" "Ex global argument." + :ex-arg global (when evil-called-from-ex-p (evil-ex-parse-global (or evil-ex-argument "")))) diff --git a/evil-vars.el b/evil-vars.el index 2ebf4186..644b78b7 100644 --- a/evil-vars.el +++ b/evil-vars.el @@ -1353,6 +1353,15 @@ line. If this option is non-nil, this behavior is reversed." "Face for interactive replacement text." :group 'evil) +(defcustom evil-ex-global-command-interactive-highlight t + "If non-nil, pattern matches in Ex global commands are interactively highlighted." + :type 'boolean + :group 'evil) + +(defface evil-ex-global-command-matches '((t :inherit lazy-highlight)) + "Face for interactive global command matches." + :group 'evil) + (defcustom evil-command-window-height 7 "Height (in lines) of the command line window. Set to 0 to use the default height for `split-window'." From 6d011718c40d3c107ea13bcf1463f6a8d5f79ed0 Mon Sep 17 00:00:00 2001 From: Yikai Zhao Date: Sun, 31 Mar 2024 10:40:03 +0800 Subject: [PATCH 2/3] do not use `length>` for old version of emacs --- evil-commands.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evil-commands.el b/evil-commands.el index da92eab6..5ce4a20c 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -4240,7 +4240,7 @@ Use `evil-flush-lines' if INVERT is nil, or `evil-keep-lines' if not." (eq flag 'update)) (condition-case err (let ((pattern (car (evil-ex-parse-global (or arg ""))))) - (when (length> pattern 0) + (when (> (length pattern) 0) (evil-ex-hl-change 'evil-ex-global (evil-ex-make-pattern pattern evil-ex-search-case nil)))) (user-error (evil-ex-echo (error-message-string err)))))))) From 898c9858601df336c5d75cef8e03cd280792f977 Mon Sep 17 00:00:00 2001 From: Yikai Zhao Date: Fri, 5 Apr 2024 22:40:17 +0800 Subject: [PATCH 3/3] support range --- evil-commands.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/evil-commands.el b/evil-commands.el index 5ce4a20c..22b117e0 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -4238,11 +4238,14 @@ Use `evil-flush-lines' if INVERT is nil, or `evil-keep-lines' if not." (when (and evil-ex-global-command-interactive-highlight (eq flag 'update)) + (when evil-ex-range + (cl-destructuring-bind (beg end &rest) + (evil-expand-range evil-ex-range t) + (evil-ex-hl-set-region 'evil-ex-global beg end))) (condition-case err - (let ((pattern (car (evil-ex-parse-global (or arg ""))))) - (when (> (length pattern) 0) - (evil-ex-hl-change 'evil-ex-global - (evil-ex-make-pattern pattern evil-ex-search-case nil)))) + (when-let ((pattern (car (evil-ex-parse-global (or arg ""))))) + (evil-ex-hl-change 'evil-ex-global + (evil-ex-make-pattern pattern evil-ex-search-case nil))) (user-error (evil-ex-echo (error-message-string err)))))))) (evil-define-operator evil-ex-global