diff --git a/evil-commands.el b/evil-commands.el index 62a65972..f1efbd84 100644 --- a/evil-commands.el +++ b/evil-commands.el @@ -4187,6 +4187,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 8dfaf1d1..386ec5ab 100644 --- a/evil-tests.el +++ b/evil-tests.el @@ -8651,17 +8651,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 @@ -8694,6 +8716,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]) "")) @@ -8704,6 +8727,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]) "")) @@ -8713,27 +8737,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 395dc2fa..389b3c63 100644 --- a/evil-types.el +++ b/evil-types.el @@ -388,6 +388,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 aea7eeea..88470c02 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'."