-
Notifications
You must be signed in to change notification settings - Fork 280
/
evil-command-window.el
191 lines (161 loc) · 7.47 KB
/
evil-command-window.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
;;; 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>
;; Version: 1.15.0
;;
;; This file is NOT part of GNU Emacs.
;;; License:
;; This file is part of Evil.
;;
;; Evil is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; Evil is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with Evil. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; 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-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.")
(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."
(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.
If called interactively, edit this minibuffer argument."
(interactive
(list (cons (minibuffer-contents) (minibuffer-history-value))
(or (minibuffer-prompt) (user-error "Minibuffer is inactive"))
#'evil--command-window-minibuffer-execute))
(when (derived-mode-p 'evil-command-window-mode)
(user-error "Command-line window is already open"))
(when (evil-ex-p) (evil-ex-teardown))
(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-minibuffer-execute (result)
"Terminate this minibuffer argument with RESULT."
(delete-minibuffer-contents)
(insert result)
(exit-minibuffer))
(defun evil-command-window-ex (&optional current-command)
"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."
(interactive)
(evil-command-window (cons (or current-command "") evil-ex-history)
":"
#'evil-command-window-ex-execute))
(define-obsolete-function-alias
'evil-ex-command-window #'evil-command-window "1.15.0"
"Start command window with Ex history and current minibuffer content.")
(define-obsolete-function-alias
'evil-ex-search-command-window #'evil-command-window "1.15.0"
"Start command window with search history and current minibuffer content.")
(defun evil-command-window-ex-execute (result)
"Execute RESULT as an Ex command."
(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--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."
(interactive)
(evil--command-window-search t))
(defun evil-command-window-search-backward ()
"Open a command-line window for backward searches."
(interactive)
(evil--command-window-search nil))
(defun evil-command-window-search-execute (result forward)
"Search for RESULT using FORWARD to determine direction."
(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 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))))
(provide 'evil-command-window)
;;; evil-command-window.el ends here