aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--evil-command-window.el210
-rw-r--r--evil-tests.el74
-rw-r--r--evil-vars.el10
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.")