diff options
| -rw-r--r-- | evil-command-window.el | 210 | ||||
| -rw-r--r-- | evil-tests.el | 74 | ||||
| -rw-r--r-- | evil-vars.el | 10 |
3 files changed, 124 insertions, 170 deletions
diff --git a/evil-command-window.el b/evil-command-window.el index c364e41..dd14882 100644 --- a/evil-command-window.el +++ b/evil-command-window.el @@ -1,4 +1,4 @@ -;;; evil-command-window.el --- Evil command line window implementation -*- lexical-binding: t -*- +;;; evil-command-window.el --- Evil command-line window -*- lexical-binding: t -*- ;; Author: Emanuel Evans <emanuel.evans at gmail.com> ;; Maintainer: Vegard Øye <vegard_oye at hotmail.com> @@ -26,162 +26,170 @@ ;;; Commentary: -;; This provides an implementation of the vim command line window for -;; editing and repeating past ex commands and searches. +;; This provides an implementation of the Vim command-line window for +;; editing and repeating past Ex commands and searches. ;;; Code: (require 'evil-vars) (require 'evil-common) -(require 'evil-search) (require 'evil-ex) +(require 'evil-search) (defvar evil-search-module) (defvar evil-command-window-current-buffer nil - "The buffer from which the command line window was called.") + "The buffer from which the command-line window was called.") + +(defvar evil-command-window-execute-fn nil + "The command to execute when exiting the command-line window.") + +(defvar evil--command-window-prompt nil + "The key for the command that opened the command-line window (:, /, or ?).") (define-derived-mode evil-command-window-mode fundamental-mode "Evil-cmd" - "Major mode for the Evil command line window." - (auto-fill-mode 0) - (add-hook 'after-change-functions #'evil-command-window-draw-prefix nil t)) - -(defun evil-command-window (history cmd-key execute-fn) - "Open a command line window for HISTORY with CMD-KEY and EXECUTE-FN. -HISTORY should be a list of commands. CMD-KEY should be the string of -the key whose history is being shown (one of \":\", \"/\" or \"?\"). -EXECUTE-FN should be a function of one argument to execute on the -result that the user selects." - (when (eq major-mode 'evil-command-window-mode) - (user-error "Cannot recursively open command line window")) - (dolist (win (window-list)) - (when (equal (buffer-name (window-buffer win)) "*Command Line*") - (kill-buffer (window-buffer win)) - (delete-window win))) - (split-window nil - (unless (zerop evil-command-window-height) - evil-command-window-height) - 'above) - (setq evil-command-window-current-buffer (current-buffer)) - (ignore-errors (kill-buffer "*Command Line*")) - (switch-to-buffer "*Command Line*") - (setq-local evil-command-window-execute-fn execute-fn) - (setq-local evil-command-window-cmd-key cmd-key) - (evil-command-window-mode) - (evil-command-window-insert-commands history)) + "Major mode for the Evil command-line window." + (add-hook 'after-change-functions #'evil--command-window-draw-prefix nil t) + (auto-fill-mode 0)) + +(defun evil-command-window (history prompt execute-fn) + "Open a command-line window for HISTORY with PROMPT and EXECUTE-FN. +HISTORY should be a list of commands. PROMPT should be the +command-line prompt (one of \":\", \"/\" or \"?\"). EXECUTE-FN should +be a unary function to execute on the result that the user selects." + (when (derived-mode-p 'evil-command-window-mode) + (user-error "Command-line window is already open")) + (let ((previous-buffer (current-buffer)) + (buffer (get-buffer-create "*Command Line*"))) + (with-current-buffer buffer + (erase-buffer) + (evil-command-window-mode) + (setq-local evil-command-window-current-buffer previous-buffer) + (setq-local evil-command-window-execute-fn execute-fn) + (setq-local evil--command-window-prompt prompt) + (evil--command-window-insert-commands history)) + + (let* ((action + `((display-buffer-reuse-window display-buffer-at-bottom) + ,@(unless (zerop evil-command-window-height) + `((window-height body-lines . ,evil-command-window-height) + (preserve-size nil . t))) + (dedicated . t))) + (window (display-buffer buffer action)) + (delete-window-fun + (lambda (window) + (set-window-parameter window 'delete-window nil) + (delete-window window) + (switch-to-minibuffer)))) + (when (minibufferp) + (set-window-parameter window 'delete-window delete-window-fun)) + (select-window window))) + (goto-char (point-max)) + (unless (bobp) (backward-char) (evil-adjust-cursor))) + +(defun evil--command-window-draw-prefix (beg end _old-len) + "Display `evil--command-window-prompt' as a prefix of the changed lines." + (let ((prefix (propertize evil--command-window-prompt + 'font-lock-face 'minibuffer-prompt))) + (put-text-property beg end 'line-prefix prefix))) + +(defun evil--command-window-insert-commands (history) + "Insert the commands in HISTORY." + (let ((inhibit-modification-hooks t)) + (dolist (cmd (reverse history)) (insert cmd "\n")) + (evil--command-window-draw-prefix (point-min) (point-max) nil))) + +(defun evil-command-window-execute () + "Execute the command on the current line in the appropriate buffer. +The local variable `evil-command-window-execute-fn' determines which +function to execute." + (interactive) + (let ((result (buffer-substring-no-properties + (line-beginning-position) (line-end-position))) + (original-buffer evil-command-window-current-buffer) + (execute-fn evil-command-window-execute-fn)) + (let ((ignore-window-parameters t)) + (ignore-errors (kill-buffer-and-window))) + (unless (buffer-live-p original-buffer) + (user-error "Originating buffer is no longer active")) + (let ((window (get-buffer-window original-buffer))) + (when window (select-window window))) + (with-current-buffer original-buffer (funcall execute-fn result)))) (defun evil-command-window-ex (&optional current-command execute-fn) - "Open a command line window for editing and executing ex commands. -If CURRENT-COMMAND is present, it will be inserted under the -cursor as the current command to be edited. If EXECUTE-FN is given, -it will be used as the function to execute instead of + "Open a command-line window for editing and executing Ex commands. +If CURRENT-COMMAND is present, it will be inserted under the cursor as +the current command to be edited. If EXECUTE-FN is given, it will be +used as the function to execute instead of `evil-command-window-ex-execute', the default." (interactive) (evil-command-window (cons (or current-command "") evil-ex-history) ":" - (or execute-fn 'evil-command-window-ex-execute))) + (or execute-fn #'evil-command-window-ex-execute))) (defun evil-ex-command-window () - "Start command window with ex history and current minibuffer content." + "Start command window with Ex history and current minibuffer content." (interactive) - (let ((current (minibuffer-contents)) - (config (current-window-configuration))) - (evil-ex-teardown) - (select-window (minibuffer-selected-window) t) - (evil-command-window-ex current (apply-partially 'evil-ex-command-window-execute config)))) + (evil-ex-teardown) + (let ((execute-fn (apply-partially #'evil-ex-command-window-execute + (current-window-configuration)))) + (evil-command-window-ex (minibuffer-contents) execute-fn))) (defun evil-ex-search-command-window () "Start command window with search history and current minibuffer content." (interactive) - (let ((current (minibuffer-contents)) - (config (current-window-configuration))) - (select-window (minibuffer-selected-window) t) - (evil-command-window (cons current evil-ex-search-history) + (let ((execute-fn (apply-partially #'evil-ex-command-window-execute + (current-window-configuration)))) + (evil-command-window (cons (minibuffer-contents) evil-ex-search-history) (evil-search-prompt (eq evil-ex-search-direction 'forward)) - (apply-partially 'evil-ex-command-window-execute config)))) - -(defun evil-command-window-execute () - "Execute the command on the current line in the appropriate buffer. -The local variable `evil-command-window-execute-fn' determines which -function to execute." - (interactive) - (let ((result (buffer-substring (line-beginning-position) - (line-end-position))) - (execute-fn evil-command-window-execute-fn) - (command-window (get-buffer-window))) - (select-window (previous-window)) - (unless (equal evil-command-window-current-buffer (current-buffer)) - (user-error "Originating buffer is no longer active")) - (kill-buffer "*Command Line*") - (delete-window command-window) - (funcall execute-fn result) - (setq evil-command-window-current-buffer nil))) + execute-fn))) (defun evil-command-window-ex-execute (result) "Execute RESULT as an Ex command." - (unless (string-match-p "^ *$" result) + (unless (string-match-p "\\`[ \t\n\r]*\\'" result) (unless (equal result (car evil-ex-history)) (push result evil-ex-history)) (evil-ex-execute result))) (defun evil-ex-command-window-execute (config result) - (select-window (active-minibuffer-window) t) (set-window-configuration config) (delete-minibuffer-contents) (insert result) (exit-minibuffer)) +(defun evil--command-window-search (forward) + "Open a command-line window for searches." + (evil-command-window + (cons "" (cond ((eq evil-search-module 'evil-search) + evil-ex-search-history) + (forward evil-search-forward-history) + (t evil-search-backward-history))) + (evil-search-prompt forward) + (lambda (result) (evil-command-window-search-execute result forward)))) + (defun evil-command-window-search-forward () - "Open a command line window for forward searches." + "Open a command-line window for forward searches." (interactive) - (evil-command-window - (cons "" (if (eq evil-search-module 'evil-search) - evil-ex-search-history - evil-search-forward-history)) - "/" - (lambda (result) (evil-command-window-search-execute result t)))) + (evil--command-window-search t)) (defun evil-command-window-search-backward () - "Open a command line window for backward searches." + "Open a command-line window for backward searches." (interactive) - (evil-command-window - (cons "" (if (eq evil-search-module 'evil-search) - evil-ex-search-history - evil-search-backward-history)) - "?" - (lambda (result) (evil-command-window-search-execute result nil)))) + (evil--command-window-search nil)) (defun evil-command-window-search-execute (result forward) "Search for RESULT using FORWARD to determine direction." - (unless (zerop (length result)) + (unless (string= result "") (if (eq evil-search-module 'evil-search) (progn (setq evil-ex-search-pattern (evil-ex-make-search-pattern result) evil-ex-search-direction (if forward 'forward 'backward)) - (unless (equal result (car-safe evil-ex-search-history)) + (unless (equal result (car evil-ex-search-history)) (push result evil-ex-search-history)) (evil-ex-search)) (evil-push-search-history result forward) (evil-search result forward evil-regexp-search)))) -(defun evil-command-window-draw-prefix (&rest _) - "Display `evil-command-window-cmd-key' as a prefix of the current line." - (let ((prefix (propertize evil-command-window-cmd-key - 'font-lock-face 'minibuffer-prompt))) - (set-text-properties (line-beginning-position) (line-beginning-position 2) - (list 'line-prefix prefix)))) - -(defun evil-command-window-insert-commands (hist) - "Insert the commands in HIST." - (let ((inhibit-modification-hooks t)) - (mapc (lambda (cmd) (insert cmd) (newline)) (reverse hist))) - (let ((prefix (propertize evil-command-window-cmd-key - 'font-lock-face 'minibuffer-prompt))) - (set-text-properties (point-min) (point-max) (list 'line-prefix prefix))) - (goto-char (point-max)) - (and (bolp) (not (bobp)) (backward-char)) - (evil-adjust-cursor)) - (provide 'evil-command-window) ;;; evil-command-window.el ends here diff --git a/evil-tests.el b/evil-tests.el index 1bc6878..2e03df9 100644 --- a/evil-tests.el +++ b/evil-tests.el @@ -8866,74 +8866,31 @@ Source ;;; Command line window (ert-deftest evil-test-command-window-ex () - "Test command line window for ex commands" - (skip-unless (not noninteractive)) - (let (evil-ex-history) - (evil-test-buffer - "[f]oo foo foo" - (":s/foo/bar" [return]) - "[b]ar foo foo" - (":s/foo/baz" [return]) - "[b]ar baz foo" + "Test command line window for Ex commands." + (let ((evil-ex-history (list "s/foo/baz" "s/foo/bar"))) + (evil-test-buffer "[b]ar baz foo" ("q:") "s/foo/bar\ns/foo/baz\n[]\n" ("kk:s/bar/quz" [return]) "[s]/foo/quz\ns/foo/baz\n" - ("fzrx") - "s/foo/qu[x]\ns/foo/baz\n" - ([return]) + ("fzrx" [return]) "[b]ar baz qux" - (should (equal (car evil-ex-history) - "s/foo/qux"))))) - -(ert-deftest evil-test-command-window-recursive () - "Test that recursive command windows shouldn't be allowed" - (skip-unless (not noninteractive)) - (let ((evil-command-window-height 0)) - (evil-test-buffer - "[f]oo foo foo" - (":s/foo/bar" [return]) - ("q:") - (should-error (execute-kbd-macro "q:"))))) + (should (equal (car evil-ex-history) "s/foo/qux"))))) (ert-deftest evil-test-command-window-noop () - "Test that executing a blank command does nothing" - (skip-unless (not noninteractive)) - (evil-test-buffer - "[f]oo foo foo" + "Test that executing a blank command does nothing." + (evil-test-buffer "[f]oo foo foo" ("q:") "[]\n" ([return]) "[f]oo foo foo")) -(ert-deftest evil-test-command-window-multiple () - "Test that multiple command line windows can't be visible at the same time" - (skip-unless (not noninteractive)) - (let ((evil-command-window-height 0)) - (evil-test-buffer - "[f]oo foo foo" - ("q:") - (let ((num-windows (length (window-list)))) - (select-window (previous-window)) - (execute-kbd-macro "q:") - (should (= (length (window-list)) num-windows)))))) - (ert-deftest evil-test-command-window-search-history () - "Test command window with forward and backward search history" - (skip-unless (not noninteractive)) - (let ((evil-search-module 'isearch)) - (evil-test-buffer - "[f]oo bar baz qux one two three four" - ("/qux" [return]) - "foo bar baz [q]ux one two three four" - ("/three" [return]) - "foo bar baz qux one two [t]hree four" - ("?bar" [return]) - "foo [b]ar baz qux one two three four" - ("/four" [return]) - "foo bar baz qux one two three [f]our" - ("?baz" [return]) - "foo bar [b]az qux one two three four" + "Test command window with forward and backward search history." + (let ((evil-search-module 'isearch) + (evil-search-forward-history (list "four" "three" "qux")) + (evil-search-backward-history (list "baz" "bar"))) + (evil-test-buffer "foo bar [b]az qux one two three four" ("q/") "qux\nthree\nfour\n[]\n" ("k" [return]) @@ -8944,14 +8901,11 @@ Source "bar\nbaz\n[]\n" ("k$rr" [return]) "foo [b]ar baz qux one two three four" - (should-error - (progn (execute-kbd-macro "q/iNOT THERE") - (execute-kbd-macro [return]))) + (error 'user-error "q/iNOT THERE" [return]) "foo [b]ar baz qux one two three four"))) (ert-deftest evil-test-command-window-search-word () - "Test command window history when searching for word under cursor" - (skip-unless (not noninteractive)) + "Test command window history when searching for word under cursor." (let ((evil-search-module 'isearch)) (evil-test-buffer "[f]oo bar foo bar foo" diff --git a/evil-vars.el b/evil-vars.el index bd2b29e..873d1a6 100644 --- a/evil-vars.el +++ b/evil-vars.el @@ -1351,7 +1351,7 @@ line. If this option is non-nil, this behavior is reversed." "Face for interactive replacement text." :group 'evil) -(defcustom evil-command-window-height 8 +(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'." :type 'integer @@ -1987,14 +1987,6 @@ when Ex is started interactively.") "Non-nil if the previous was a search. Otherwise the previous command is assumed as substitute.") -;;; Command line window - -(evil-define-local-var evil-command-window-execute-fn nil - "The command to execute when exiting the command line window.") - -(evil-define-local-var evil-command-window-cmd-key nil - "The key for the command that opened the command line window (:, /, or ?).") - ;; The lazy-highlighting framework (evil-define-local-var evil-ex-active-highlights-alist nil "An alist of currently active highlights.") |
