* viper-cmd.el (viper--key-maps): new variable.
[bpt/emacs.git] / lisp / emulation / viper-macs.el
CommitLineData
be010748
RS
1;;; viper-macs.el --- functions implementing keyboard macros for Viper
2
7f1c31c9 3;; Copyright (C) 1994, 95, 96, 97, 2000, 01, 02, 2005 Free Software Foundation, Inc.
d6fd318f 4
50a07e18 5;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
02f34c70 6
6c2e12f4
KH
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software; you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation; either version 2, or (at your option)
12;; any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
b578f267 20;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
21;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22;; Boston, MA 02110-1301, USA.
6c2e12f4 23
60370d40
PJ
24;;; Commentary:
25
26;;; Code:
03fc1246 27
9b70a748
MK
28(provide 'viper-macs)
29
30;; compiler pacifier
8626cfa2
MK
31(defvar viper-ex-work-buf)
32(defvar viper-custom-file-name)
33(defvar viper-current-state)
34(defvar viper-fast-keyseq-timeout)
9b70a748 35
726e270f
MK
36;; loading happens only in non-interactive compilation
37;; in order to spare non-viperized emacs from being viperized
38(if noninteractive
39 (eval-when-compile
40 (let ((load-path (cons (expand-file-name ".") load-path)))
41 (or (featurep 'viper-util)
42 (load "viper-util.el" nil nil 'nosuffix))
43 (or (featurep 'viper-keym)
44 (load "viper-keym.el" nil nil 'nosuffix))
45 (or (featurep 'viper-mous)
46 (load "viper-mous.el" nil nil 'nosuffix))
47 (or (featurep 'viper-cmd)
48 (load "viper-cmd.el" nil nil 'nosuffix))
49 )))
9b70a748
MK
50;; end pacifier
51
6c2e12f4 52(require 'viper-util)
03fc1246
MK
53(require 'viper-keym)
54
6c2e12f4
KH
55
56;;; Variables
57
58;; Register holding last macro.
8626cfa2 59(defvar viper-last-macro-reg nil)
6c2e12f4 60
a1506d29 61;; format of the elements of kbd alists:
6c2e12f4
KH
62;; (name ((buf . macr)...(buf . macr)) ((maj-mode . macr)...) (t . macr))
63;; kbd macro alist for Vi state
8626cfa2 64(defvar viper-vi-kbd-macro-alist nil)
6c2e12f4 65;; same for insert/replace state
8626cfa2 66(defvar viper-insert-kbd-macro-alist nil)
6c2e12f4 67;; same for emacs state
8626cfa2 68(defvar viper-emacs-kbd-macro-alist nil)
6c2e12f4
KH
69
70;; Internal var that passes info between start-kbd-macro and end-kbd-macro
71;; in :map and :map!
8626cfa2 72(defvar viper-kbd-macro-parameters nil)
6c2e12f4 73
8626cfa2 74(defvar viper-this-kbd-macro nil
6c2e12f4 75 "Vector of keys representing the name of currently running Viper kbd macro.")
8626cfa2 76(defvar viper-last-kbd-macro nil
6c2e12f4
KH
77 "Vector of keys representing the name of last Viper keyboard macro.")
78
8626cfa2 79(defcustom viper-repeat-from-history-key 'f12
1e70790f 80 "Prefix key for accessing previously typed Vi commands.
6c2e12f4 81
3af0304a 82The previous command is accessible, as usual, via `.'. The command before this
1e70790f
MK
83can be invoked as `<this key> 1', and the command before that, and the command
84before that one is accessible as `<this key> 2'.
3af0304a 85The notation for these keys is borrowed from XEmacs. Basically,
6c2e12f4 86a key is a symbol, e.g., `a', `\\1', `f2', etc., or a list, e.g.,
1e70790f 87`(meta control f1)'."
eabbaad8 88 :type 'sexp
1e70790f 89 :group 'viper)
6c2e12f4
KH
90
91
92\f
93;;; Code
94
546fe085 95;; Ex map command
6c2e12f4 96(defun ex-map ()
6c2e12f4
KH
97 (let ((mod-char "")
98 macro-name macro-body map-args ins)
99 (save-window-excursion
8626cfa2 100 (set-buffer viper-ex-work-buf)
6c2e12f4
KH
101 (if (looking-at "!")
102 (progn
103 (setq ins t
104 mod-char "!")
105 (forward-char 1))))
106 (setq map-args (ex-map-read-args mod-char)
107 macro-name (car map-args)
108 macro-body (cdr map-args))
8626cfa2 109 (setq viper-kbd-macro-parameters (list ins mod-char macro-name macro-body))
6c2e12f4 110 (if macro-body
8626cfa2 111 (viper-end-mapping-kbd-macro 'ignore)
6c2e12f4 112 (ex-fixup-history (format "map%s %S" mod-char
8626cfa2 113 (viper-display-macro macro-name)))
6c2e12f4 114 ;; if defining macro for insert, switch there for authentic WYSIWYG
8626cfa2 115 (if ins (viper-change-state-to-insert))
6c2e12f4 116 (start-kbd-macro nil)
8626cfa2
MK
117 (define-key viper-vi-intercept-map "\C-x)" 'viper-end-mapping-kbd-macro)
118 (define-key viper-insert-intercept-map "\C-x)" 'viper-end-mapping-kbd-macro)
119 (define-key viper-emacs-intercept-map "\C-x)" 'viper-end-mapping-kbd-macro)
3af0304a 120 (message "Mapping %S in %s state. Hit `C-x )' to complete the mapping"
8626cfa2 121 (viper-display-macro macro-name)
6c2e12f4
KH
122 (if ins "Insert" "Vi")))
123 ))
a1506d29 124
6c2e12f4 125
546fe085 126;; Ex unmap
6c2e12f4 127(defun ex-unmap ()
6c2e12f4
KH
128 (let ((mod-char "")
129 temp macro-name ins)
130 (save-window-excursion
8626cfa2 131 (set-buffer viper-ex-work-buf)
6c2e12f4
KH
132 (if (looking-at "!")
133 (progn
134 (setq ins t
135 mod-char "!")
136 (forward-char 1))))
137
138 (setq macro-name (ex-unmap-read-args mod-char))
8626cfa2 139 (setq temp (viper-fixup-macro (vconcat macro-name))) ;; copy and fixup
6c2e12f4 140 (ex-fixup-history (format "unmap%s %S" mod-char
8626cfa2
MK
141 (viper-display-macro temp)))
142 (viper-unrecord-kbd-macro macro-name (if ins 'insert-state 'vi-state))
6c2e12f4 143 ))
a1506d29 144
6c2e12f4
KH
145
146;; read arguments for ex-map
147(defun ex-map-read-args (variant)
148 (let ((cursor-in-echo-area t)
149 (key-seq [])
150 temp key event message
151 macro-name macro-body args)
a1506d29 152
6c2e12f4
KH
153 (condition-case nil
154 (setq args (concat (ex-get-inline-cmd-args ".*map[!]*[ \t]?" "\n\C-m")
155 " nil nil ")
156 temp (read-from-string args)
157 macro-name (car temp)
158 macro-body (car (read-from-string args (cdr temp))))
159 (error
160 (signal
a1506d29 161 'error
6c2e12f4 162 '("map: Macro name and body must be a quoted string or a vector"))))
a1506d29 163
6c2e12f4
KH
164 ;; We expect macro-name to be a vector, a string, or a quoted string.
165 ;; In the second case, it will emerge as a symbol when read from
3af0304a 166 ;; the above read-from-string. So we need to convert it into a string
6c2e12f4
KH
167 (if macro-name
168 (cond ((vectorp macro-name) nil)
a1506d29 169 ((stringp macro-name)
6c2e12f4
KH
170 (setq macro-name (vconcat macro-name)))
171 (t (setq macro-name (vconcat (prin1-to-string macro-name)))))
172 (message ":map%s <Name>" variant)(sit-for 2)
173 (while
174 (not (member key
175 '(?\C-m ?\n (control m) (control j) return linefeed)))
176 (setq key-seq (vconcat key-seq (if key (vector key) [])))
177 ;; the only keys available for editing are these-- no help while there
178 (if (member
179 key
180 '(?\b ?\d '^? '^H (control h) (control \?) backspace delete))
8ea74b0e 181 (setq key-seq (viper-subseq key-seq 0 (- (length key-seq) 2))))
6c2e12f4 182 (setq message
edff961b
MK
183 (format
184 ":map%s %s"
185 variant (if (> (length key-seq) 0)
8626cfa2 186 (prin1-to-string (viper-display-macro key-seq))
edff961b 187 "")))
95d70c42 188 (message message)
8626cfa2
MK
189 (setq event (viper-read-key))
190 ;;(setq event (viper-read-event))
6c2e12f4 191 (setq key
8626cfa2 192 (if (viper-mouse-event-p event)
6c2e12f4
KH
193 (progn
194 (message "%s (No mouse---only keyboard keys, please)"
195 message)
196 (sit-for 2)
197 nil)
8626cfa2 198 (viper-event-key event)))
6c2e12f4
KH
199 )
200 (setq macro-name key-seq))
a1506d29 201
6c2e12f4
KH
202 (if (= (length macro-name) 0)
203 (error "Can't map an empty macro name"))
8626cfa2
MK
204 (setq macro-name (viper-fixup-macro macro-name))
205 (if (viper-char-array-p macro-name)
206 (setq macro-name (viper-char-array-to-macro macro-name)))
a1506d29 207
6c2e12f4 208 (if macro-body
8626cfa2
MK
209 (cond ((viper-char-array-p macro-body)
210 (setq macro-body (viper-char-array-to-macro macro-body)))
6c2e12f4
KH
211 ((vectorp macro-body) nil)
212 (t (error "map: Invalid syntax in macro definition"))))
546fe085
KH
213 (setq cursor-in-echo-area nil)(sit-for 0) ; this overcomes xemacs tty bug
214 (cons macro-name macro-body)))
a1506d29 215
6c2e12f4
KH
216
217
218;; read arguments for ex-unmap
219(defun ex-unmap-read-args (variant)
220 (let ((cursor-in-echo-area t)
221 (macro-alist (if (string= variant "!")
8626cfa2
MK
222 viper-insert-kbd-macro-alist
223 viper-vi-kbd-macro-alist))
6c2e12f4
KH
224 ;; these are disabled just in case, to avoid surprises when doing
225 ;; completing-read
8626cfa2
MK
226 viper-vi-kbd-minor-mode viper-insert-kbd-minor-mode
227 viper-emacs-kbd-minor-mode
228 viper-vi-intercept-minor-mode viper-insert-intercept-minor-mode
229 viper-emacs-intercept-minor-mode
6c2e12f4
KH
230 event message
231 key key-seq macro-name)
232 (setq macro-name (ex-get-inline-cmd-args ".*unma?p?[!]*[ \t]*"))
a1506d29 233
6c2e12f4
KH
234 (if (> (length macro-name) 0)
235 ()
236 (message ":unmap%s <Name>" variant) (sit-for 2)
237 (while
238 (not
239 (member key '(?\C-m ?\n (control m) (control j) return linefeed)))
240 (setq key-seq (vconcat key-seq (if key (vector key) [])))
241 ;; the only keys available for editing are these-- no help while there
242 (cond ((member
243 key
244 '(?\b ?\d '^? '^H (control h) (control \?) backspace delete))
8ea74b0e 245 (setq key-seq (viper-subseq key-seq 0 (- (length key-seq) 2))))
6c2e12f4 246 ((member key '(tab (control i) ?\t))
8ea74b0e 247 (setq key-seq (viper-subseq key-seq 0 (1- (length key-seq))))
a1506d29 248 (setq message
edff961b
MK
249 (format
250 ":unmap%s %s"
251 variant (if (> (length key-seq) 0)
252 (prin1-to-string
8626cfa2 253 (viper-display-macro key-seq))
edff961b 254 "")))
6c2e12f4 255 (setq key-seq
8626cfa2 256 (viper-do-sequence-completion key-seq macro-alist message))
6c2e12f4 257 ))
a1506d29 258 (setq message
edff961b
MK
259 (format
260 ":unmap%s %s"
261 variant (if (> (length key-seq) 0)
262 (prin1-to-string
8626cfa2 263 (viper-display-macro key-seq))
edff961b 264 "")))
95d70c42 265 (message message)
8626cfa2
MK
266 (setq event (viper-read-key))
267 ;;(setq event (viper-read-event))
6c2e12f4 268 (setq key
8626cfa2 269 (if (viper-mouse-event-p event)
6c2e12f4
KH
270 (progn
271 (message "%s (No mouse---only keyboard keys, please)"
272 message)
273 (sit-for 2)
274 nil)
8626cfa2 275 (viper-event-key event)))
6c2e12f4
KH
276 )
277 (setq macro-name key-seq))
278
279 (if (= (length macro-name) 0)
280 (error "Can't unmap an empty macro name"))
a1506d29 281
6c2e12f4
KH
282 ;; convert macro names into vector, if starts with a `['
283 (if (memq (elt macro-name 0) '(?\[ ?\"))
284 (car (read-from-string macro-name))
285 (vconcat macro-name))
286 ))
a1506d29
JB
287
288
edff961b
MK
289;; Terminate a Vi kbd macro.
290;; optional argument IGNORE, if t, indicates that we are dealing with an
291;; existing macro that needs to be registered, but there is no need to
292;; terminate a kbd macro.
8626cfa2 293(defun viper-end-mapping-kbd-macro (&optional ignore)
6c2e12f4 294 (interactive)
8626cfa2
MK
295 (define-key viper-vi-intercept-map "\C-x)" nil)
296 (define-key viper-insert-intercept-map "\C-x)" nil)
297 (define-key viper-emacs-intercept-map "\C-x)" nil)
6c2e12f4 298 (if (and (not ignore)
8626cfa2 299 (or (not viper-kbd-macro-parameters)
6c2e12f4
KH
300 (not defining-kbd-macro)))
301 (error "Not mapping a kbd-macro"))
8626cfa2
MK
302 (let ((mod-char (nth 1 viper-kbd-macro-parameters))
303 (ins (nth 0 viper-kbd-macro-parameters))
304 (macro-name (nth 2 viper-kbd-macro-parameters))
305 (macro-body (nth 3 viper-kbd-macro-parameters)))
306 (setq viper-kbd-macro-parameters nil)
6c2e12f4
KH
307 (or ignore
308 (progn
309 (end-kbd-macro nil)
8626cfa2 310 (setq macro-body (viper-events-to-macro last-kbd-macro))
6c2e12f4
KH
311 ;; always go back to Vi, since this is where we started
312 ;; defining macro
8626cfa2 313 (viper-change-state-to-vi)))
a1506d29 314
8626cfa2 315 (viper-record-kbd-macro macro-name
6c2e12f4 316 (if ins 'insert-state 'vi-state)
8626cfa2 317 (viper-display-macro macro-body))
a1506d29 318
6c2e12f4 319 (ex-fixup-history (format "map%s %S %S" mod-char
8626cfa2
MK
320 (viper-display-macro macro-name)
321 (viper-display-macro macro-body)))
6c2e12f4
KH
322 ))
323
324
325
6c2e12f4
KH
326\f
327;;; Recording, unrecording, executing
328
3af0304a 329;; Accepts as macro names: strings and vectors.
6c2e12f4 330;; strings must be strings of characters; vectors must be vectors of keys
3af0304a 331;; in canonic form. The canonic form is essentially the form used in XEmacs
3f9526a3
MK
332;; More general definitions are inherited by more specific scopes:
333;; global->major mode->buffer. More specific definitions override more general
8626cfa2 334(defun viper-record-kbd-macro (macro-name state macro-body &optional scope)
3af0304a
MK
335 "Record a Vi macro. Can be used in `.viper' file to define permanent macros.
336MACRO-NAME is a string of characters or a vector of keys. STATE is
337either `vi-state' or `insert-state'. It specifies the Viper state in which to
338define the macro. MACRO-BODY is a string that represents the keyboard macro.
6c2e12f4
KH
339Optional SCOPE says whether the macro should be global \(t\), mode-specific
340\(a major-mode symbol\), or buffer-specific \(buffer name, a string\).
341If SCOPE is nil, the user is asked to specify the scope."
a1506d29 342 (let* (state-name keymap
6c2e12f4
KH
343 (macro-alist-var
344 (cond ((eq state 'vi-state)
345 (setq state-name "Vi state"
8626cfa2
MK
346 keymap viper-vi-kbd-map)
347 'viper-vi-kbd-macro-alist)
6c2e12f4
KH
348 ((memq state '(insert-state replace-state))
349 (setq state-name "Insert state"
8626cfa2
MK
350 keymap viper-insert-kbd-map)
351 'viper-insert-kbd-macro-alist)
6c2e12f4
KH
352 (t
353 (setq state-name "Emacs state"
8626cfa2
MK
354 keymap viper-emacs-kbd-map)
355 'viper-emacs-kbd-macro-alist)
6c2e12f4
KH
356 ))
357 new-elt old-elt old-sub-elt msg
358 temp lis lis2)
a1506d29 359
6c2e12f4
KH
360 (if (= (length macro-name) 0)
361 (error "Can't map an empty macro name"))
a1506d29 362
3af0304a
MK
363 ;; Macro-name is usually a vector. However, command history or macros
364 ;; recorded in ~/.viper may be recorded as strings. So, convert to
a1506d29 365 ;; vectors.
8626cfa2
MK
366 (setq macro-name (viper-fixup-macro macro-name))
367 (if (viper-char-array-p macro-name)
368 (setq macro-name (viper-char-array-to-macro macro-name)))
369 (setq macro-body (viper-fixup-macro macro-body))
370 (if (viper-char-array-p macro-body)
371 (setq macro-body (viper-char-array-to-macro macro-body)))
a1506d29 372
6c2e12f4
KH
373 ;; don't ask if scope is given and is of the right type
374 (or (eq scope t)
375 (stringp scope)
376 (and scope (symbolp scope))
377 (progn
378 (setq scope
379 (cond
380 ((y-or-n-p
381 (format
382 "Map this macro for buffer `%s' only? "
383 (buffer-name)))
384 (setq msg
385 (format
386 "%S is mapped to %s for %s in `%s'"
8626cfa2
MK
387 (viper-display-macro macro-name)
388 (viper-abbreviate-string
6c2e12f4
KH
389 (format
390 "%S"
8626cfa2 391 (setq temp (viper-display-macro macro-body)))
6c2e12f4
KH
392 14 "" ""
393 (if (stringp temp) " ....\"" " ....]"))
394 state-name (buffer-name)))
395 (buffer-name))
396 ((y-or-n-p
397 (format
398 "Map this macro for the major mode `%S' only? "
399 major-mode))
400 (setq msg
401 (format
402 "%S is mapped to %s for %s in `%S'"
8626cfa2
MK
403 (viper-display-macro macro-name)
404 (viper-abbreviate-string
6c2e12f4
KH
405 (format
406 "%S"
8626cfa2 407 (setq temp (viper-display-macro macro-body)))
6c2e12f4
KH
408 14 "" ""
409 (if (stringp macro-body) " ....\"" " ....]"))
410 state-name major-mode))
411 major-mode)
412 (t
413 (setq msg
414 (format
415 "%S is globally mapped to %s in %s"
8626cfa2
MK
416 (viper-display-macro macro-name)
417 (viper-abbreviate-string
6c2e12f4
KH
418 (format
419 "%S"
8626cfa2 420 (setq temp (viper-display-macro macro-body)))
6c2e12f4
KH
421 14 "" ""
422 (if (stringp macro-body) " ....\"" " ....]"))
423 state-name))
424 t)))
c992e211
MK
425 (if (y-or-n-p
426 (format "Save this macro in %s? "
8626cfa2 427 (viper-abbreviate-file-name viper-custom-file-name)))
a1506d29 428 (viper-save-string-in-file
8626cfa2
MK
429 (format "\n(viper-record-kbd-macro %S '%S %s '%S)"
430 (viper-display-macro macro-name)
f90edb57
MK
431 state
432 ;; if we don't let vector macro-body through %S,
433 ;; the symbols `\.' `\[' etc will be converted into
434 ;; characters, causing invalid read error on recorded
8626cfa2 435 ;; macros in .viper.
f90edb57
MK
436 ;; I am not sure is macro-body can still be a string at
437 ;; this point, but I am preserving this option anyway.
438 (if (vectorp macro-body)
439 (format "%S" macro-body)
440 macro-body)
a1506d29 441 scope)
8626cfa2 442 viper-custom-file-name))
a1506d29 443
95d70c42 444 (message msg)
6c2e12f4 445 ))
a1506d29 446
6c2e12f4
KH
447 (setq new-elt
448 (cons macro-name
449 (cond ((eq scope t) (list nil nil (cons t nil)))
450 ((symbolp scope)
451 (list nil (list (cons scope nil)) (cons t nil)))
452 ((stringp scope)
453 (list (list (cons scope nil)) nil (cons t nil))))))
454 (setq old-elt (assoc macro-name (eval macro-alist-var)))
455
3f9526a3
MK
456 (if (null old-elt)
457 (progn
458 ;; insert new-elt in macro-alist-var and keep the list sorted
459 (define-key
460 keymap
461 (vector (viper-key-to-emacs-key (aref macro-name 0)))
462 'viper-exec-mapped-kbd-macro)
463 (setq lis (eval macro-alist-var))
464 (while (and lis (string< (viper-array-to-string (car (car lis)))
465 (viper-array-to-string macro-name)))
466 (setq lis2 (cons (car lis) lis2))
467 (setq lis (cdr lis)))
468
469 (setq lis2 (reverse lis2))
470 (set macro-alist-var (append lis2 (cons new-elt lis)))
471 (setq old-elt new-elt)))
6c2e12f4 472 (setq old-sub-elt
8626cfa2
MK
473 (cond ((eq scope t) (viper-kbd-global-pair old-elt))
474 ((symbolp scope) (assoc scope (viper-kbd-mode-alist old-elt)))
475 ((stringp scope) (assoc scope (viper-kbd-buf-alist old-elt)))))
a1506d29 476 (if old-sub-elt
6c2e12f4
KH
477 (setcdr old-sub-elt macro-body)
478 (cond ((symbolp scope) (setcar (cdr (cdr old-elt))
479 (cons (cons scope macro-body)
8626cfa2 480 (viper-kbd-mode-alist old-elt))))
6c2e12f4
KH
481 ((stringp scope) (setcar (cdr old-elt)
482 (cons (cons scope macro-body)
8626cfa2 483 (viper-kbd-buf-alist old-elt))))))
6c2e12f4 484 ))
6c2e12f4 485
a1506d29
JB
486
487
8626cfa2 488;; macro name must be a vector of viper-style keys
3f9526a3
MK
489;; viper-unrecord-kbd-macro doesn't have scope. Macro definitions are inherited
490;; from global -> major mode -> buffer
491;; More specific definition overrides more general
492;; Can't unrecord definition for more specific, if a more general definition is
493;; in effect
8626cfa2 494(defun viper-unrecord-kbd-macro (macro-name state)
546fe085 495 "Delete macro MACRO-NAME from Viper STATE.
3af0304a 496MACRO-NAME must be a vector of viper-style keys. This command is used by Viper
8626cfa2 497internally, but the user can also use it in ~/.viper to delete pre-defined
3af0304a
MK
498macros supplied with Viper. The best way to avoid mistakes in macro names to
499be passed to this function is to use viper-describe-kbd-macros and copy the
500name from there."
a1506d29 501 (let* (state-name keymap
6c2e12f4
KH
502 (macro-alist-var
503 (cond ((eq state 'vi-state)
504 (setq state-name "Vi state"
8626cfa2
MK
505 keymap viper-vi-kbd-map)
506 'viper-vi-kbd-macro-alist)
6c2e12f4
KH
507 ((memq state '(insert-state replace-state))
508 (setq state-name "Insert state"
8626cfa2
MK
509 keymap viper-insert-kbd-map)
510 'viper-insert-kbd-macro-alist)
6c2e12f4
KH
511 (t
512 (setq state-name "Emacs state"
8626cfa2
MK
513 keymap viper-emacs-kbd-map)
514 'viper-emacs-kbd-macro-alist)
6c2e12f4
KH
515 ))
516 buf-mapping mode-mapping global-mapping
517 macro-pair macro-entry)
a1506d29 518
3af0304a
MK
519 ;; Macro-name is usually a vector. However, command history or macros
520 ;; recorded in ~/.viper may appear as strings. So, convert to vectors.
8626cfa2
MK
521 (setq macro-name (viper-fixup-macro macro-name))
522 (if (viper-char-array-p macro-name)
523 (setq macro-name (viper-char-array-to-macro macro-name)))
6c2e12f4
KH
524
525 (setq macro-entry (assoc macro-name (eval macro-alist-var)))
526 (if (= (length macro-name) 0)
527 (error "Can't unmap an empty macro name"))
528 (if (null macro-entry)
529 (error "%S is not mapped to a macro for %s in `%s'"
8626cfa2 530 (viper-display-macro macro-name)
6c2e12f4 531 state-name (buffer-name)))
a1506d29 532
8626cfa2
MK
533 (setq buf-mapping (viper-kbd-buf-pair macro-entry)
534 mode-mapping (viper-kbd-mode-pair macro-entry)
535 global-mapping (viper-kbd-global-pair macro-entry))
a1506d29 536
6c2e12f4
KH
537 (cond ((and (cdr buf-mapping)
538 (or (and (not (cdr mode-mapping)) (not (cdr global-mapping)))
539 (y-or-n-p
540 (format "Unmap %S for `%s' only? "
8626cfa2 541 (viper-display-macro macro-name)
6c2e12f4
KH
542 (buffer-name)))))
543 (setq macro-pair buf-mapping)
a1506d29 544 (message "%S is unmapped for %s in `%s'"
8626cfa2 545 (viper-display-macro macro-name)
6c2e12f4
KH
546 state-name (buffer-name)))
547 ((and (cdr mode-mapping)
548 (or (not (cdr global-mapping))
549 (y-or-n-p
550 (format "Unmap %S for the major mode `%S' only? "
8626cfa2 551 (viper-display-macro macro-name)
6c2e12f4
KH
552 major-mode))))
553 (setq macro-pair mode-mapping)
554 (message "%S is unmapped for %s in %S"
8626cfa2 555 (viper-display-macro macro-name) state-name major-mode))
3f9526a3 556 ((cdr (setq macro-pair global-mapping))
6c2e12f4 557 (message
1e70790f 558 "Global mapping for %S in %s is removed"
8626cfa2 559 (viper-display-macro macro-name) state-name))
6c2e12f4 560 (t (error "%S is not mapped to a macro for %s in `%s'"
8626cfa2 561 (viper-display-macro macro-name)
6c2e12f4
KH
562 state-name (buffer-name))))
563 (setcdr macro-pair nil)
564 (or (cdr buf-mapping)
565 (cdr mode-mapping)
566 (cdr global-mapping)
567 (progn
568 (set macro-alist-var (delq macro-entry (eval macro-alist-var)))
a1506d29 569 (if (viper-can-release-key (aref macro-name 0)
3f9526a3 570 (eval macro-alist-var))
6c2e12f4
KH
571 (define-key
572 keymap
8626cfa2 573 (vector (viper-key-to-emacs-key (aref macro-name 0)))
6c2e12f4
KH
574 nil))
575 ))
576 ))
a1506d29 577
546fe085 578;; Check if MACRO-ALIST has an entry for a macro name starting with
3af0304a 579;; CHAR. If not, this indicates that the binding for this char
8626cfa2
MK
580;; in viper-vi/insert-kbd-map can be released.
581(defun viper-can-release-key (char macro-alist)
6c2e12f4
KH
582 (let ((lis macro-alist)
583 (can-release t)
584 macro-name)
a1506d29 585
6c2e12f4
KH
586 (while (and lis can-release)
587 (setq macro-name (car (car lis)))
588 (if (eq char (aref macro-name 0))
589 (setq can-release nil))
590 (setq lis (cdr lis)))
591 can-release))
592
593
8626cfa2 594(defun viper-exec-mapped-kbd-macro (count)
6c2e12f4
KH
595 "Dispatch kbd macro."
596 (interactive "P")
8626cfa2
MK
597 (let* ((macro-alist (cond ((eq viper-current-state 'vi-state)
598 viper-vi-kbd-macro-alist)
599 ((memq viper-current-state
6c2e12f4 600 '(insert-state replace-state))
8626cfa2 601 viper-insert-kbd-macro-alist)
6c2e12f4 602 (t
8626cfa2 603 viper-emacs-kbd-macro-alist)))
6c2e12f4
KH
604 (unmatched-suffix "")
605 ;; Macros and keys are executed with other macros turned off
606 ;; For macros, this is done to avoid macro recursion
8626cfa2
MK
607 viper-vi-kbd-minor-mode viper-insert-kbd-minor-mode
608 viper-emacs-kbd-minor-mode
6c2e12f4
KH
609 next-best-match keyseq event-seq
610 macro-first-char macro-alist-elt macro-body
611 command)
a1506d29 612
6c2e12f4 613 (setq macro-first-char last-command-event
8626cfa2
MK
614 event-seq (viper-read-fast-keysequence macro-first-char macro-alist)
615 keyseq (viper-events-to-macro event-seq)
6c2e12f4 616 macro-alist-elt (assoc keyseq macro-alist)
8626cfa2 617 next-best-match (viper-find-best-matching-macro macro-alist keyseq))
a1506d29 618
6c2e12f4
KH
619 (if (null macro-alist-elt)
620 (setq macro-alist-elt (car next-best-match)
8ea74b0e 621 unmatched-suffix (viper-subseq event-seq (cdr next-best-match))))
6c2e12f4
KH
622
623 (cond ((null macro-alist-elt))
8626cfa2
MK
624 ((setq macro-body (viper-kbd-buf-definition macro-alist-elt)))
625 ((setq macro-body (viper-kbd-mode-definition macro-alist-elt)))
626 ((setq macro-body (viper-kbd-global-definition macro-alist-elt))))
a1506d29 627
6c2e12f4
KH
628 ;; when defining keyboard macro, don't use the macro mappings
629 (if (and macro-body (not defining-kbd-macro))
630 ;; block cmd executed as part of a macro from entering command history
631 (let ((command-history command-history))
8626cfa2
MK
632 (setq viper-this-kbd-macro (car macro-alist-elt))
633 (execute-kbd-macro (viper-macro-to-events macro-body) count)
634 (setq viper-this-kbd-macro nil
635 viper-last-kbd-macro (car macro-alist-elt))
636 (viper-set-unread-command-events unmatched-suffix))
6c2e12f4
KH
637 ;; If not a macro, or the macro is suppressed while defining another
638 ;; macro, put keyseq back on the event queue
8626cfa2 639 (viper-set-unread-command-events event-seq)
6c2e12f4
KH
640 ;; if the user typed arg, then use it if prefix arg is not set by
641 ;; some other command (setting prefix arg can happen if we do, say,
3af0304a 642 ;; 2dw and there is a macro starting with 2. Then control will go to
6c2e12f4 643 ;; this routine
a1506d29 644 (or prefix-arg (setq prefix-arg count))
6c2e12f4
KH
645 (setq command (key-binding (read-key-sequence nil)))
646 (if (commandp command)
647 (command-execute command)
648 (beep 1)))
649 ))
650
651
652\f
653;;; Displaying and completing macros
a1506d29 654
8626cfa2 655(defun viper-describe-kbd-macros ()
6c2e12f4
KH
656 "Show currently defined keyboard macros."
657 (interactive)
8626cfa2 658 (with-output-to-temp-buffer " *viper-info*"
6c2e12f4 659 (princ "Macros in Vi state:\n===================\n")
8626cfa2 660 (mapcar 'viper-describe-one-macro viper-vi-kbd-macro-alist)
6c2e12f4 661 (princ "\n\nMacros in Insert and Replace states:\n====================================\n")
8626cfa2 662 (mapcar 'viper-describe-one-macro viper-insert-kbd-macro-alist)
6c2e12f4 663 (princ "\n\nMacros in Emacs state:\n======================\n")
8626cfa2 664 (mapcar 'viper-describe-one-macro viper-emacs-kbd-macro-alist)
6c2e12f4 665 ))
a1506d29 666
8626cfa2 667(defun viper-describe-one-macro (macro)
6c2e12f4 668 (princ (format "\n *** Mappings for %S:\n ------------\n"
8626cfa2 669 (viper-display-macro (car macro))))
6c2e12f4 670 (princ " ** Buffer-specific:")
8626cfa2
MK
671 (if (viper-kbd-buf-alist macro)
672 (mapcar 'viper-describe-one-macro-elt (viper-kbd-buf-alist macro))
6c2e12f4
KH
673 (princ " none\n"))
674 (princ "\n ** Mode-specific:")
8626cfa2
MK
675 (if (viper-kbd-mode-alist macro)
676 (mapcar 'viper-describe-one-macro-elt (viper-kbd-mode-alist macro))
6c2e12f4
KH
677 (princ " none\n"))
678 (princ "\n ** Global:")
8626cfa2
MK
679 (if (viper-kbd-global-definition macro)
680 (princ (format "\n %S" (cdr (viper-kbd-global-pair macro))))
6c2e12f4
KH
681 (princ " none"))
682 (princ "\n"))
a1506d29 683
8626cfa2 684(defun viper-describe-one-macro-elt (elt)
6c2e12f4
KH
685 (let ((name (car elt))
686 (defn (cdr elt)))
687 (princ (format "\n * %S:\n %S\n" name defn))))
a1506d29
JB
688
689
690
6c2e12f4 691;; check if SEQ is a prefix of some car of an element in ALIST
8626cfa2
MK
692(defun viper-keyseq-is-a-possible-macro (seq alist)
693 (let ((converted-seq (viper-events-to-macro seq)))
a1506d29 694 (eval (cons 'or
6c2e12f4 695 (mapcar
3af0304a 696 (lambda (elt) (viper-prefix-subseq-p converted-seq elt))
8626cfa2 697 (viper-this-buffer-macros alist))))))
a1506d29 698
6c2e12f4 699;; whether SEQ1 is a prefix of SEQ2
8626cfa2 700(defun viper-prefix-subseq-p (seq1 seq2)
6c2e12f4
KH
701 (let ((len1 (length seq1))
702 (len2 (length seq2)))
703 (if (<= len1 len2)
8ea74b0e 704 (equal seq1 (viper-subseq seq2 0 len1)))))
a1506d29 705
6c2e12f4 706;; find the longest common prefix
8626cfa2 707(defun viper-common-seq-prefix (&rest seqs)
6c2e12f4
KH
708 (let* ((first (car seqs))
709 (rest (cdr seqs))
710 (pref [])
711 (idx 0)
712 len)
713 (if (= (length seqs) 0)
714 (setq len 0)
715 (setq len (apply 'min (mapcar 'length seqs))))
716 (while (< idx len)
a1506d29 717 (if (eval (cons 'and
3af0304a 718 (mapcar (lambda (s) (equal (elt first idx) (elt s idx)))
6c2e12f4
KH
719 rest)))
720 (setq pref (vconcat pref (vector (elt first idx)))))
721 (setq idx (1+ idx)))
722 pref))
a1506d29 723
6c2e12f4 724;; get all sequences that match PREFIX from a given A-LIST
8626cfa2 725(defun viper-extract-matching-alist-members (pref alist)
3af0304a 726 (delq nil (mapcar (lambda (elt) (if (viper-prefix-subseq-p pref elt) elt))
8626cfa2 727 (viper-this-buffer-macros alist))))
a1506d29 728
8626cfa2
MK
729(defun viper-do-sequence-completion (seq alist compl-message)
730 (let* ((matches (viper-extract-matching-alist-members seq alist))
731 (new-seq (apply 'viper-common-seq-prefix matches))
6c2e12f4
KH
732 )
733 (cond ((and (equal seq new-seq) (= (length matches) 1))
734 (message "%s (Sole completion)" compl-message)
735 (sit-for 2))
a1506d29 736 ((null matches)
6c2e12f4
KH
737 (message "%s (No match)" compl-message)
738 (sit-for 2)
739 (setq new-seq seq))
a1506d29 740 ((member seq matches)
6c2e12f4
KH
741 (message "%s (Complete, but not unique)" compl-message)
742 (sit-for 2)
8626cfa2 743 (viper-display-vector-completions matches))
6c2e12f4 744 ((equal seq new-seq)
8626cfa2 745 (viper-display-vector-completions matches)))
6c2e12f4 746 new-seq))
a1506d29
JB
747
748
8626cfa2 749(defun viper-display-vector-completions (list)
6c2e12f4 750 (with-output-to-temp-buffer "*Completions*"
a1506d29 751 (display-completion-list
6c2e12f4 752 (mapcar 'prin1-to-string
8626cfa2 753 (mapcar 'viper-display-macro list)))))
a1506d29
JB
754
755
756
6c2e12f4
KH
757;; alist is the alist of macros
758;; str is the fast key sequence entered
759;; returns: (matching-macro-def . unmatched-suffix-start-index)
8626cfa2 760(defun viper-find-best-matching-macro (alist str)
6c2e12f4
KH
761 (let ((lis alist)
762 (def-len 0)
763 (str-len (length str))
764 match unmatched-start-idx found macro-def)
765 (while (and (not found) lis)
766 (setq macro-def (car lis)
767 def-len (length (car macro-def)))
768 (if (and (>= str-len def-len)
8ea74b0e 769 (equal (car macro-def) (viper-subseq str 0 def-len)))
8626cfa2
MK
770 (if (or (viper-kbd-buf-definition macro-def)
771 (viper-kbd-mode-definition macro-def)
772 (viper-kbd-global-definition macro-def))
6c2e12f4
KH
773 (setq found t))
774 )
775 (setq lis (cdr lis)))
a1506d29 776
6c2e12f4
KH
777 (if found
778 (setq match macro-def
779 unmatched-start-idx def-len)
780 (setq match nil
781 unmatched-start-idx 0))
a1506d29 782
6c2e12f4 783 (cons match unmatched-start-idx)))
a1506d29
JB
784
785
786
6c2e12f4 787;; returns a list of names of macros defined for the current buffer
8626cfa2 788(defun viper-this-buffer-macros (macro-alist)
6c2e12f4
KH
789 (let (candidates)
790 (setq candidates
3af0304a
MK
791 (mapcar (lambda (elt)
792 (if (or (viper-kbd-buf-definition elt)
793 (viper-kbd-mode-definition elt)
794 (viper-kbd-global-definition elt))
795 (car elt)))
6c2e12f4
KH
796 macro-alist))
797 (setq candidates (delq nil candidates))))
a1506d29
JB
798
799
546fe085 800;; if seq of Viper key symbols (representing a macro) can be converted to a
3af0304a 801;; string--do so. Otherwise, do nothing.
8626cfa2
MK
802(defun viper-display-macro (macro-name-or-body)
803 (cond ((viper-char-symbol-sequence-p macro-name-or-body)
edff961b 804 (mapconcat 'symbol-name macro-name-or-body ""))
8626cfa2 805 ((viper-char-array-p macro-name-or-body)
edff961b
MK
806 (mapconcat 'char-to-string macro-name-or-body ""))
807 (t macro-name-or-body)))
a1506d29 808
df93b82b
MK
809;; convert sequence of events (that came presumably from emacs kbd macro) into
810;; Viper's macro, which is a vector of the form
811;; [ desc desc ... ]
812;; Each desc is either a symbol of (meta symb), (shift symb), etc.
3af0304a 813;; Here we purge events that happen to be lists. In most cases, these events
df93b82b
MK
814;; got into a macro definition unintentionally; say, when the user moves mouse
815;; during a macro definition, then something like (switch-frame ...) might get
3af0304a 816;; in. Another reason for purging lists-events is that we can't store them in
df93b82b 817;; textual form (say, in .emacs) and then read them back.
8626cfa2 818(defun viper-events-to-macro (event-seq)
3af0304a
MK
819 (vconcat (delq nil (mapcar (lambda (elt) (if (consp elt)
820 nil
821 (viper-event-key elt)))
df93b82b 822 event-seq))))
a1506d29 823
546fe085 824;; convert strings or arrays of characters to Viper macro form
8626cfa2 825(defun viper-char-array-to-macro (array)
6c2e12f4
KH
826 (let ((vec (vconcat array))
827 macro)
8626cfa2 828 (if viper-xemacs-p
6c2e12f4
KH
829 (setq macro (mapcar 'character-to-event vec))
830 (setq macro vec))
8626cfa2 831 (vconcat (mapcar 'viper-event-key macro))))
a1506d29 832
edff961b 833;; For macros bodies and names, goes over MACRO and checks if all members are
6c2e12f4 834;; names of keys (actually, it only checks if they are symbols or lists
edff961b
MK
835;; if a digit is found, it is converted into a symbol (e.g., 0 -> \0, etc).
836;; If MACRO is not a list or vector -- doesn't change MACRO.
8626cfa2 837(defun viper-fixup-macro (macro)
6c2e12f4
KH
838 (let ((len (length macro))
839 (idx 0)
840 elt break)
841 (if (or (vectorp macro) (listp macro))
842 (while (and (< idx len) (not break))
843 (setq elt (elt macro idx))
844 (cond ((numberp elt)
845 ;; fix number
846 (if (and (<= 0 elt) (<= elt 9))
847 (cond ((arrayp macro)
848 (aset macro
849 idx
850 (intern (char-to-string (+ ?0 elt)))))
851 ((listp macro)
852 (setcar (nthcdr idx macro)
853 (intern (char-to-string (+ ?0 elt)))))
854 )))
6c2e12f4 855 ((listp elt)
8626cfa2 856 (viper-fixup-macro elt))
6c2e12f4
KH
857 ((symbolp elt) nil)
858 (t (setq break t)))
859 (setq idx (1+ idx))))
a1506d29 860
6c2e12f4
KH
861 (if break
862 (error "Wrong type macro component, symbol-or-listp, %S" elt)
863 macro)))
a1506d29 864
8626cfa2
MK
865(defun viper-macro-to-events (macro-body)
866 (vconcat (mapcar 'viper-key-to-emacs-key macro-body)))
a1506d29
JB
867
868
6c2e12f4
KH
869\f
870;;; Reading fast key sequences
a1506d29 871
6c2e12f4 872;; Assuming that CHAR was the first character in a fast succession of key
3af0304a 873;; strokes, read the rest. Return the vector of keys that was entered in
6c2e12f4
KH
874;; this fast succession of key strokes.
875;; A fast keysequence is one that is terminated by a pause longer than
8626cfa2
MK
876;; viper-fast-keyseq-timeout.
877(defun viper-read-fast-keysequence (event macro-alist)
6c2e12f4
KH
878 (let ((lis (vector event))
879 next-event)
8626cfa2
MK
880 (while (and (viper-fast-keysequence-p)
881 (viper-keyseq-is-a-possible-macro lis macro-alist))
882 (setq next-event (viper-read-key))
883 ;;(setq next-event (viper-read-event))
884 (or (viper-mouse-event-p next-event)
6c2e12f4
KH
885 (setq lis (vconcat lis (vector next-event)))))
886 lis))
887
888\f
889;;; Keyboard macros in registers
890
891;; sets register to last-kbd-macro carefully.
8626cfa2 892(defun viper-set-register-macro (reg)
6c2e12f4 893 (if (get-register reg)
3af0304a 894 (if (y-or-n-p "Register contains data. Overwrite? ")
6c2e12f4
KH
895 ()
896 (error
3af0304a 897 "Macro not saved in register. Can still be invoked via `C-x e'")))
6c2e12f4
KH
898 (set-register reg last-kbd-macro))
899
8626cfa2 900(defun viper-register-macro (count)
6c2e12f4
KH
901 "Keyboard macros in registers - a modified \@ command."
902 (interactive "P")
903 (let ((reg (downcase (read-char))))
904 (cond ((or (and (<= ?a reg) (<= reg ?z)))
8626cfa2 905 (setq viper-last-macro-reg reg)
6c2e12f4
KH
906 (if defining-kbd-macro
907 (progn
908 (end-kbd-macro)
8626cfa2 909 (viper-set-register-macro reg))
6c2e12f4
KH
910 (execute-kbd-macro (get-register reg) count)))
911 ((or (= ?@ reg) (= ?\^j reg) (= ?\^m reg))
a1506d29 912 (if viper-last-macro-reg
6c2e12f4
KH
913 nil
914 (error "No previous kbd macro"))
8626cfa2 915 (execute-kbd-macro (get-register viper-last-macro-reg) count))
6c2e12f4
KH
916 ((= ?\# reg)
917 (start-kbd-macro count))
918 ((= ?! reg)
919 (setq reg (downcase (read-char)))
920 (if (or (and (<= ?a reg) (<= reg ?z)))
921 (progn
8626cfa2
MK
922 (setq viper-last-macro-reg reg)
923 (viper-set-register-macro reg))))
6c2e12f4 924 (t
ae37fce9 925 (error "`%c': Unknown register" reg)))))
a1506d29 926
6c2e12f4 927
8626cfa2 928(defun viper-global-execute ()
6c2e12f4
KH
929 "Call last keyboad macro for each line in the region."
930 (if (> (point) (mark t)) (exchange-point-and-mark))
931 (beginning-of-line)
932 (call-last-kbd-macro)
933 (while (< (point) (mark t))
934 (forward-line 1)
935 (beginning-of-line)
936 (call-last-kbd-macro)))
937
938
ab5796a9 939;;; arch-tag: ecd3cc5c-8cd0-4bbe-b2ec-7e75a4b7d0aa
60370d40 940;;; viper-macs.el ends here