branch: elpa/evil
commit a09fdca0b35ef4289cf55d7b0ffadf4cf3a5c9fc
Author: Axel Forsman <a...@axelf.se>
Commit: Axel Forsman <axels...@gmail.com>

    Reselect minibuffer when quitting command-line win
    
    Pressing C-f while editing an Ex command to open the command-line
    window and later quitting that window used to not reselect the Ex
    command line, but instead just continue the recursive edit in some
    previous window. This was specially confusing before commit
    02004bce6884619e687654d333b75c90f8fc27d0, as the evil-ex-abort hack
    did not trigger, leaving you in a bad state with
    evil-ex-current-buffer set to t.
    
    This commit fixes that by setting the delete-window window parameter
    on the command-line window to a function that switches back to the
    minibuffer after deleting the window, which closes #1379.
    
    This commit also replaces the split-window call with a display-buffer
    invocation, to always spawn the window at the bottom of the frame, but
    also allow the user to override this behavior.
---
 evil-command-window.el | 210 +++++++++++++++++++++++++------------------------
 evil-tests.el          |  74 ++++-------------
 evil-vars.el           |  10 +--
 3 files changed, 124 insertions(+), 170 deletions(-)

diff --git a/evil-command-window.el b/evil-command-window.el
index c364e41868..dd148822db 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 1bc68781a1..2e03df930b 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 bd2b29ebc7..873d1a66fd 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.")

Reply via email to