new version
[emacs.git] / lisp / emulation / viper-cmd.el
blob85eb264a208bccd6fd0623a4cda026eae76d20fe
1 ;;; viper-cmd.el --- Vi command support for Viper
2 ;; Copyright (C) 1997 Free Software Foundation, Inc.
5 ;; Code
7 (provide 'viper-cmd)
8 (require 'advice)
10 ;; Compiler pacifier
11 (defvar viper-minibuffer-current-face)
12 (defvar viper-minibuffer-insert-face)
13 (defvar viper-minibuffer-vi-face)
14 (defvar viper-minibuffer-emacs-face)
15 (defvar viper-always)
16 (defvar viper-mode-string)
17 (defvar viper-custom-file-name)
18 (defvar iso-accents-mode)
19 (defvar quail-mode)
20 (defvar quail-current-str)
21 (defvar zmacs-region-stays)
22 (defvar mark-even-if-inactive)
24 ;; loading happens only in non-interactive compilation
25 ;; in order to spare non-viperized emacs from being viperized
26 (if noninteractive
27 (eval-when-compile
28 (let ((load-path (cons (expand-file-name ".") load-path)))
29 (or (featurep 'viper-util)
30 (load "viper-util.el" nil nil 'nosuffix))
31 (or (featurep 'viper-keym)
32 (load "viper-keym.el" nil nil 'nosuffix))
33 (or (featurep 'viper-mous)
34 (load "viper-mous.el" nil nil 'nosuffix))
35 (or (featurep 'viper-macs)
36 (load "viper-macs.el" nil nil 'nosuffix))
37 (or (featurep 'viper-ex)
38 (load "viper-ex.el" nil nil 'nosuffix))
39 )))
40 ;; end pacifier
43 (require 'viper-util)
44 (require 'viper-keym)
45 (require 'viper-mous)
46 (require 'viper-macs)
47 (require 'viper-ex)
51 ;; Generic predicates
53 ;; These test functions are shamelessly lifted from vip 4.4.2 by Aamod Sane
55 ;; generate test functions
56 ;; given symbol foo, foo-p is the test function, foos is the set of
57 ;; Viper command keys
58 ;; (macroexpand '(viper-test-com-defun foo))
59 ;; (defun foo-p (com) (consp (memq (if (< com 0) (- com) com) foos)))
61 (defmacro viper-test-com-defun (name)
62 (let* ((snm (symbol-name name))
63 (nm-p (intern (concat snm "-p")))
64 (nms (intern (concat snm "s"))))
65 (` (defun (, nm-p) (com)
66 (consp (memq (if (and (viper-characterp com) (< com 0))
67 (- com) com) (, nms)))))))
69 ;; Variables for defining VI commands
71 ;; Modifying commands that can be prefixes to movement commands
72 (defconst viper-prefix-commands '(?c ?d ?y ?! ?= ?# ?< ?> ?\"))
73 ;; define viper-prefix-command-p
74 (viper-test-com-defun viper-prefix-command)
76 ;; Commands that are pairs eg. dd. r and R here are a hack
77 (defconst viper-charpair-commands '(?c ?d ?y ?! ?= ?< ?> ?r ?R))
78 ;; define viper-charpair-command-p
79 (viper-test-com-defun viper-charpair-command)
81 (defconst viper-movement-commands '(?b ?B ?e ?E ?f ?F ?G ?h ?H ?j ?k ?l
82 ?H ?M ?L ?n ?t ?T ?w ?W ?$ ?%
83 ?^ ?( ?) ?- ?+ ?| ?{ ?} ?[ ?] ?' ?`
84 ?; ?, ?0 ?? ?/ ?\C-m ?\
85 space return
86 delete backspace
88 "Movement commands")
89 ;; define viper-movement-command-p
90 (viper-test-com-defun viper-movement-command)
92 ;; Vi digit commands
93 (defconst viper-digit-commands '(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9))
95 ;; define viper-digit-command-p
96 (viper-test-com-defun viper-digit-command)
98 ;; Commands that can be repeated by . (dotted)
99 (defconst viper-dotable-commands '(?c ?d ?C ?s ?S ?D ?> ?<))
100 ;; define viper-dotable-command-p
101 (viper-test-com-defun viper-dotable-command)
103 ;; Commands that can follow a #
104 (defconst viper-hash-commands '(?c ?C ?g ?q ?s))
105 ;; define viper-hash-command-p
106 (viper-test-com-defun viper-hash-command)
108 ;; Commands that may have registers as prefix
109 (defconst viper-regsuffix-commands '(?d ?y ?Y ?D ?p ?P ?x ?X))
110 ;; define viper-regsuffix-command-p
111 (viper-test-com-defun viper-regsuffix-command)
113 (defconst viper-vi-commands (append viper-movement-commands
114 viper-digit-commands
115 viper-dotable-commands
116 viper-charpair-commands
117 viper-hash-commands
118 viper-prefix-commands
119 viper-regsuffix-commands)
120 "The list of all commands in Vi-state.")
121 ;; define viper-vi-command-p
122 (viper-test-com-defun viper-vi-command)
125 ;;; CODE
127 ;; sentinels
129 ;; Runs viper-after-change-functions inside after-change-functions
130 (defun viper-after-change-sentinel (beg end len)
131 (let ((list viper-after-change-functions))
132 (while list
133 (funcall (car list) beg end len)
134 (setq list (cdr list)))))
136 ;; Runs viper-before-change-functions inside before-change-functions
137 (defun viper-before-change-sentinel (beg end)
138 (let ((list viper-before-change-functions))
139 (while list
140 (funcall (car list) beg end)
141 (setq list (cdr list)))))
143 (defsubst viper-post-command-sentinel ()
144 (run-hooks 'viper-post-command-hooks))
146 (defsubst viper-pre-command-sentinel ()
147 (run-hooks 'viper-pre-command-hooks))
149 ;; Needed so that Viper will be able to figure the last inserted
150 ;; chunk of text with reasonable accuracy.
151 (defsubst viper-insert-state-post-command-sentinel ()
152 (if (and (memq viper-current-state '(insert-state replace-state))
153 viper-insert-point
154 (>= (point) viper-insert-point))
155 (setq viper-last-posn-while-in-insert-state (point-marker)))
156 (if (eq viper-current-state 'insert-state)
157 (progn
158 (or (stringp viper-saved-cursor-color)
159 (string= (viper-get-cursor-color) viper-insert-state-cursor-color)
160 (setq viper-saved-cursor-color (viper-get-cursor-color)))
161 (if (stringp viper-saved-cursor-color)
162 (viper-change-cursor-color viper-insert-state-cursor-color))
164 (if (and (eq this-command 'dabbrev-expand)
165 (integerp viper-pre-command-point)
166 (markerp viper-insert-point)
167 (marker-position viper-insert-point)
168 (> viper-insert-point viper-pre-command-point))
169 (viper-move-marker-locally viper-insert-point viper-pre-command-point))
172 (defsubst viper-insert-state-pre-command-sentinel ()
173 (or (memq this-command '(self-insert-command))
174 (memq (viper-event-key last-command-event)
175 '(up down left right (meta f) (meta b)
176 (control n) (control p) (control f) (control b)))
177 (viper-restore-cursor-color-after-insert))
178 (if (and (eq this-command 'dabbrev-expand)
179 (markerp viper-insert-point)
180 (marker-position viper-insert-point))
181 (setq viper-pre-command-point (marker-position viper-insert-point))))
183 (defsubst viper-R-state-post-command-sentinel ()
184 ;; Restoring cursor color is needed despite
185 ;; viper-replace-state-pre-command-sentinel: When you jump to another buffer
186 ;; in another frame, the pre-command hook won't change cursor color to
187 ;; default in that other frame. So, if the second frame cursor was red and
188 ;; we set the point outside the replacement region, then the cursor color
189 ;; will remain red. Restoring the default, below, prevents this.
190 (if (and (<= (viper-replace-start) (point))
191 (<= (point) (viper-replace-end)))
192 (viper-change-cursor-color viper-replace-overlay-cursor-color)
193 (viper-restore-cursor-color-after-replace)
196 ;; to speed up, don't change cursor color before self-insert
197 ;; and common move commands
198 (defsubst viper-replace-state-pre-command-sentinel ()
199 (or (memq this-command '(self-insert-command))
200 (memq (viper-event-key last-command-event)
201 '(up down left right (meta f) (meta b)
202 (control n) (control p) (control f) (control b)))
203 (viper-restore-cursor-color-after-replace)))
205 (defun viper-replace-state-post-command-sentinel ()
206 ;; Restoring cursor color is needed despite
207 ;; viper-replace-state-pre-command-sentinel: When one jumps to another buffer
208 ;; in another frame, the pre-command hook won't change cursor color to
209 ;; default in that other frame. So, if the second frame cursor was red and
210 ;; we set the point outside the replacement region, then the cursor color
211 ;; will remain red. Restoring the default, below, fixes this problem.
213 ;; We optimize for self-insert-command's here, since they either don't change
214 ;; cursor color or, if they terminate replace mode, the color will be changed
215 ;; in viper-finish-change
216 (or (memq this-command '(self-insert-command))
217 (viper-restore-cursor-color-after-replace))
218 (cond
219 ((eq viper-current-state 'replace-state)
220 ;; delete characters to compensate for inserted chars.
221 (let ((replace-boundary (viper-replace-end)))
222 (save-excursion
223 (goto-char viper-last-posn-in-replace-region)
224 (viper-trim-replace-chars-to-delete-if-necessary)
225 (delete-char viper-replace-chars-to-delete)
226 (setq viper-replace-chars-to-delete 0)
227 ;; terminate replace mode if reached replace limit
228 (if (= viper-last-posn-in-replace-region (viper-replace-end))
229 (viper-finish-change)))
231 (if (viper-pos-within-region
232 (point) (viper-replace-start) replace-boundary)
233 (progn
234 ;; the state may have changed in viper-finish-change above
235 (if (eq viper-current-state 'replace-state)
236 (viper-change-cursor-color viper-replace-overlay-cursor-color))
237 (setq viper-last-posn-in-replace-region (point-marker))))
239 ;; terminate replace mode if changed Viper states.
240 (t (viper-finish-change))))
243 ;; changing mode
245 ;; Change state to NEW-STATE---either emacs-state, vi-state, or insert-state.
246 (defun viper-change-state (new-state)
247 ;; Keep viper-post/pre-command-hooks fresh.
248 ;; We remove then add viper-post/pre-command-sentinel since it is very
249 ;; desirable that viper-pre-command-sentinel is the last hook and
250 ;; viper-post-command-sentinel is the first hook.
251 (remove-hook 'post-command-hook 'viper-post-command-sentinel)
252 (add-hook 'post-command-hook 'viper-post-command-sentinel)
253 (remove-hook 'pre-command-hook 'viper-pre-command-sentinel)
254 (add-hook 'pre-command-hook 'viper-pre-command-sentinel t)
255 ;; These hooks will be added back if switching to insert/replace mode
256 (viper-remove-hook 'viper-post-command-hooks
257 'viper-insert-state-post-command-sentinel)
258 (viper-remove-hook 'viper-pre-command-hooks
259 'viper-insert-state-pre-command-sentinel)
260 (setq viper-intermediate-command nil)
261 (cond ((eq new-state 'vi-state)
262 (cond ((member viper-current-state '(insert-state replace-state))
264 ;; move viper-last-posn-while-in-insert-state
265 ;; This is a normal hook that is executed in insert/replace
266 ;; states after each command. In Vi/Emacs state, it does
267 ;; nothing. We need to execute it here to make sure that
268 ;; the last posn was recorded when we hit ESC.
269 ;; It may be left unrecorded if the last thing done in
270 ;; insert/repl state was dabbrev-expansion or abbrev
271 ;; expansion caused by hitting ESC
272 (viper-insert-state-post-command-sentinel)
274 (condition-case conds
275 (progn
276 (viper-save-last-insertion
277 viper-insert-point
278 viper-last-posn-while-in-insert-state)
279 (if viper-began-as-replace
280 (setq viper-began-as-replace nil)
281 ;; repeat insert commands if numerical arg > 1
282 (save-excursion
283 (viper-repeat-insert-command))))
284 (error
285 (viper-message-conditions conds)))
287 (if (> (length viper-last-insertion) 0)
288 (viper-push-onto-ring viper-last-insertion
289 'viper-insertion-ring))
291 (if viper-ex-style-editing
292 (or (bolp) (backward-char 1))))
295 ;; insert or replace
296 ((memq new-state '(insert-state replace-state))
297 (if (memq viper-current-state '(emacs-state vi-state))
298 (viper-move-marker-locally 'viper-insert-point (point)))
299 (viper-move-marker-locally
300 'viper-last-posn-while-in-insert-state (point))
301 (viper-add-hook 'viper-post-command-hooks
302 'viper-insert-state-post-command-sentinel t)
303 (viper-add-hook 'viper-pre-command-hooks
304 'viper-insert-state-pre-command-sentinel t))
305 ) ; outermost cond
307 ;; Nothing needs to be done to switch to emacs mode! Just set some
308 ;; variables, which is already done in viper-change-state-to-emacs!
310 ;; ISO accents
311 ;; always turn off iso-accents-mode in vi-state, or else we won't be able to
312 ;; use the keys `,',^ , as they will do accents instead of Vi actions.
313 (cond ((eq new-state 'vi-state) (viper-set-iso-accents-mode nil));accents off
314 (viper-automatic-iso-accents (viper-set-iso-accents-mode t));accents on
315 (t (viper-set-iso-accents-mode nil)))
316 ;; Always turn off quail mode in vi state
317 (cond ((eq new-state 'vi-state) (viper-set-input-method nil)) ;intl input off
318 (viper-special-input-method (viper-set-input-method t)) ;intl input on
319 (t (viper-set-input-method nil)))
321 (setq viper-current-state new-state)
323 (viper-update-syntax-classes)
324 (viper-normalize-minor-mode-map-alist)
325 (viper-adjust-keys-for new-state)
326 (viper-set-mode-vars-for new-state)
327 (viper-refresh-mode-line)
332 (defun viper-adjust-keys-for (state)
333 "Make necessary adjustments to keymaps before entering STATE."
334 (cond ((memq state '(insert-state replace-state))
335 (if viper-auto-indent
336 (progn
337 (define-key viper-insert-basic-map "\C-m" 'viper-autoindent)
338 (if viper-want-emacs-keys-in-insert
339 ;; expert
340 (define-key viper-insert-basic-map "\C-j" nil)
341 ;; novice
342 (define-key viper-insert-basic-map "\C-j" 'viper-autoindent)))
343 (define-key viper-insert-basic-map "\C-m" nil)
344 (define-key viper-insert-basic-map "\C-j" nil))
346 (setq viper-insert-diehard-minor-mode
347 (not viper-want-emacs-keys-in-insert))
349 (if viper-want-ctl-h-help
350 (progn
351 (define-key viper-insert-basic-map [backspace] 'help-command)
352 (define-key viper-replace-map [backspace] 'help-command)
353 (define-key viper-insert-basic-map [(control h)] 'help-command)
354 (define-key viper-replace-map [(control h)] 'help-command))
355 (define-key viper-insert-basic-map
356 [backspace] 'viper-del-backward-char-in-insert)
357 (define-key viper-replace-map
358 [backspace] 'viper-del-backward-char-in-replace)
359 (define-key viper-insert-basic-map
360 [(control h)] 'viper-del-backward-char-in-insert)
361 (define-key viper-replace-map
362 [(control h)] 'viper-del-backward-char-in-replace)))
364 (t ; Vi state
365 (setq viper-vi-diehard-minor-mode (not viper-want-emacs-keys-in-vi))
366 (if viper-want-ctl-h-help
367 (progn
368 (define-key viper-vi-basic-map [backspace] 'help-command)
369 (define-key viper-vi-basic-map [(control h)] 'help-command))
370 (define-key viper-vi-basic-map [backspace] 'viper-backward-char)
371 (define-key viper-vi-basic-map [(control h)] 'viper-backward-char)))
375 ;; Normalizes minor-mode-map-alist by putting Viper keymaps first.
376 ;; This ensures that Viper bindings are in effect, regardless of which minor
377 ;; modes were turned on by the user or by other packages.
378 (defun viper-normalize-minor-mode-map-alist ()
379 (setq minor-mode-map-alist
380 (viper-append-filter-alist
381 (list
382 (cons 'viper-vi-intercept-minor-mode viper-vi-intercept-map)
383 (cons 'viper-vi-minibuffer-minor-mode viper-minibuffer-map)
384 (cons 'viper-vi-local-user-minor-mode viper-vi-local-user-map)
385 (cons 'viper-vi-kbd-minor-mode viper-vi-kbd-map)
386 (cons 'viper-vi-global-user-minor-mode viper-vi-global-user-map)
387 (cons 'viper-vi-state-modifier-minor-mode
388 (if (keymapp
389 (cdr (assoc major-mode
390 viper-vi-state-modifier-alist)))
391 (cdr (assoc major-mode viper-vi-state-modifier-alist))
392 viper-empty-keymap))
393 (cons 'viper-vi-diehard-minor-mode viper-vi-diehard-map)
394 (cons 'viper-vi-basic-minor-mode viper-vi-basic-map)
395 (cons 'viper-insert-intercept-minor-mode
396 viper-insert-intercept-map)
397 (cons 'viper-replace-minor-mode viper-replace-map)
398 ;; viper-insert-minibuffer-minor-mode must come after
399 ;; viper-replace-minor-mode
400 (cons 'viper-insert-minibuffer-minor-mode
401 viper-minibuffer-map)
402 (cons 'viper-insert-local-user-minor-mode
403 viper-insert-local-user-map)
404 (cons 'viper-insert-kbd-minor-mode viper-insert-kbd-map)
405 (cons 'viper-insert-global-user-minor-mode
406 viper-insert-global-user-map)
407 (cons 'viper-insert-state-modifier-minor-mode
408 (if (keymapp
409 (cdr (assoc major-mode
410 viper-insert-state-modifier-alist)))
411 (cdr (assoc major-mode
412 viper-insert-state-modifier-alist))
413 viper-empty-keymap))
414 (cons 'viper-insert-diehard-minor-mode viper-insert-diehard-map)
415 (cons 'viper-insert-basic-minor-mode viper-insert-basic-map)
416 (cons 'viper-emacs-intercept-minor-mode
417 viper-emacs-intercept-map)
418 (cons 'viper-emacs-local-user-minor-mode
419 viper-emacs-local-user-map)
420 (cons 'viper-emacs-kbd-minor-mode viper-emacs-kbd-map)
421 (cons 'viper-emacs-global-user-minor-mode
422 viper-emacs-global-user-map)
423 (cons 'viper-emacs-state-modifier-minor-mode
424 (if (keymapp
425 (cdr
426 (assoc major-mode viper-emacs-state-modifier-alist)))
427 (cdr
428 (assoc major-mode viper-emacs-state-modifier-alist))
429 viper-empty-keymap))
431 minor-mode-map-alist)))
437 ;; Viper mode-changing commands and utilities
439 ;; Modifies mode-line-buffer-identification.
440 (defun viper-refresh-mode-line ()
441 (setq viper-mode-string
442 (cond ((eq viper-current-state 'emacs-state) viper-emacs-state-id)
443 ((eq viper-current-state 'vi-state) viper-vi-state-id)
444 ((eq viper-current-state 'replace-state) viper-replace-state-id)
445 ((eq viper-current-state 'insert-state) viper-insert-state-id)))
447 ;; Sets Viper mode string in global-mode-string
448 (force-mode-line-update))
451 ;; Switch from Insert state to Vi state.
452 (defun viper-exit-insert-state ()
453 (interactive)
454 (viper-change-state-to-vi))
456 (defun viper-set-mode-vars-for (state)
457 "Sets Viper minor mode variables to put Viper's state STATE in effect."
459 ;; Emacs state
460 (setq viper-vi-minibuffer-minor-mode nil
461 viper-insert-minibuffer-minor-mode nil
462 viper-vi-intercept-minor-mode nil
463 viper-insert-intercept-minor-mode nil
465 viper-vi-local-user-minor-mode nil
466 viper-vi-kbd-minor-mode nil
467 viper-vi-global-user-minor-mode nil
468 viper-vi-state-modifier-minor-mode nil
469 viper-vi-diehard-minor-mode nil
470 viper-vi-basic-minor-mode nil
472 viper-replace-minor-mode nil
474 viper-insert-local-user-minor-mode nil
475 viper-insert-kbd-minor-mode nil
476 viper-insert-global-user-minor-mode nil
477 viper-insert-state-modifier-minor-mode nil
478 viper-insert-diehard-minor-mode nil
479 viper-insert-basic-minor-mode nil
480 viper-emacs-intercept-minor-mode t
481 viper-emacs-local-user-minor-mode t
482 viper-emacs-kbd-minor-mode (not (viper-is-in-minibuffer))
483 viper-emacs-global-user-minor-mode t
484 viper-emacs-state-modifier-minor-mode t
487 ;; Vi state
488 (if (eq state 'vi-state) ; adjust for vi-state
489 (setq
490 viper-vi-intercept-minor-mode t
491 viper-vi-minibuffer-minor-mode (viper-is-in-minibuffer)
492 viper-vi-local-user-minor-mode t
493 viper-vi-kbd-minor-mode (not (viper-is-in-minibuffer))
494 viper-vi-global-user-minor-mode t
495 viper-vi-state-modifier-minor-mode t
496 ;; don't let the diehard keymap block command completion
497 ;; and other things in the minibuffer
498 viper-vi-diehard-minor-mode (not
499 (or viper-want-emacs-keys-in-vi
500 (viper-is-in-minibuffer)))
501 viper-vi-basic-minor-mode t
502 viper-emacs-intercept-minor-mode nil
503 viper-emacs-local-user-minor-mode nil
504 viper-emacs-kbd-minor-mode nil
505 viper-emacs-global-user-minor-mode nil
506 viper-emacs-state-modifier-minor-mode nil
509 ;; Insert and Replace states
510 (if (member state '(insert-state replace-state))
511 (setq
512 viper-insert-intercept-minor-mode t
513 viper-replace-minor-mode (eq state 'replace-state)
514 viper-insert-minibuffer-minor-mode (viper-is-in-minibuffer)
515 viper-insert-local-user-minor-mode t
516 viper-insert-kbd-minor-mode (not (viper-is-in-minibuffer))
517 viper-insert-global-user-minor-mode t
518 viper-insert-state-modifier-minor-mode t
519 ;; don't let the diehard keymap block command completion
520 ;; and other things in the minibuffer
521 viper-insert-diehard-minor-mode (not
523 viper-want-emacs-keys-in-insert
524 (viper-is-in-minibuffer)))
525 viper-insert-basic-minor-mode t
526 viper-emacs-intercept-minor-mode nil
527 viper-emacs-local-user-minor-mode nil
528 viper-emacs-kbd-minor-mode nil
529 viper-emacs-global-user-minor-mode nil
530 viper-emacs-state-modifier-minor-mode nil
533 ;; minibuffer faces
534 (if (viper-has-face-support-p)
535 (setq viper-minibuffer-current-face
536 (cond ((eq state 'emacs-state) viper-minibuffer-emacs-face)
537 ((eq state 'vi-state) viper-minibuffer-vi-face)
538 ((memq state '(insert-state replace-state))
539 viper-minibuffer-insert-face))))
541 (if (viper-is-in-minibuffer)
542 (viper-set-minibuffer-overlay))
545 ;; This also takes care of the annoying incomplete lines in files.
546 ;; Also, this fixes `undo' to work vi-style for complex commands.
547 (defun viper-change-state-to-vi ()
548 "Change Viper state to Vi."
549 (interactive)
550 (if (and viper-first-time (not (viper-is-in-minibuffer)))
551 (viper-mode)
552 (if overwrite-mode (overwrite-mode nil))
553 (if abbrev-mode (expand-abbrev))
554 (if (and auto-fill-function (> (current-column) fill-column))
555 (funcall auto-fill-function))
556 ;; don't leave whitespace lines around
557 (if (and (memq last-command
558 '(viper-autoindent
559 viper-open-line viper-Open-line
560 viper-replace-state-exit-cmd))
561 (viper-over-whitespace-line))
562 (indent-to-left-margin))
563 (viper-add-newline-at-eob-if-necessary)
564 (viper-adjust-undo)
565 (viper-change-state 'vi-state)
567 (viper-restore-cursor-color-after-insert)
569 ;; Protect against user errors in hooks
570 (condition-case conds
571 (run-hooks 'viper-vi-state-hook)
572 (error
573 (viper-message-conditions conds)))))
575 (defun viper-change-state-to-insert ()
576 "Change Viper state to Insert."
577 (interactive)
578 (viper-change-state 'insert-state)
580 (or (stringp viper-saved-cursor-color)
581 (string= (viper-get-cursor-color) viper-insert-state-cursor-color)
582 (setq viper-saved-cursor-color (viper-get-cursor-color)))
583 ;; Commented out, because if viper-change-state-to-insert is executed
584 ;; non-interactively then the old cursor color may get lost. Same old Emacs
585 ;; bug related to local variables?
586 ;;;(if (stringp viper-saved-cursor-color)
587 ;;; (viper-change-cursor-color viper-insert-state-cursor-color))
589 ;; Protect against user errors in hooks
590 (condition-case conds
591 (run-hooks 'viper-insert-state-hook)
592 (error
593 (viper-message-conditions conds))))
595 (defsubst viper-downgrade-to-insert ()
596 (setq viper-current-state 'insert-state
597 viper-replace-minor-mode nil))
601 ;; Change to replace state. When the end of replacement region is reached,
602 ;; replace state changes to insert state.
603 (defun viper-change-state-to-replace (&optional non-R-cmd)
604 (viper-change-state 'replace-state)
605 ;; Run insert-state-hook
606 (condition-case conds
607 (run-hooks 'viper-insert-state-hook 'viper-replace-state-hook)
608 (error
609 (viper-message-conditions conds)))
611 (if non-R-cmd
612 (viper-start-replace)
613 ;; 'R' is implemented using Emacs's overwrite-mode
614 (viper-start-R-mode))
618 (defun viper-change-state-to-emacs ()
619 "Change Viper state to Emacs."
620 (interactive)
621 (viper-change-state 'emacs-state)
623 ;; Protect agains user errors in hooks
624 (condition-case conds
625 (run-hooks 'viper-emacs-state-hook)
626 (error
627 (viper-message-conditions conds))))
629 ;; escape to emacs mode termporarily
630 (defun viper-escape-to-emacs (arg &optional events)
631 "Escape to Emacs state from Vi state for one Emacs command.
632 ARG is used as the prefix value for the executed command. If
633 EVENTS is a list of events, which become the beginning of the command."
634 (interactive "P")
635 (if (= last-command-char ?\\)
636 (message "Switched to EMACS state for the next command..."))
637 (viper-escape-to-state arg events 'emacs-state))
639 ;; escape to Vi mode termporarily
640 (defun viper-escape-to-vi (arg)
641 "Escape from Emacs state to Vi state for one Vi 1-character command.
642 If the Vi command that the user types has a prefix argument, e.g., `d2w', then
643 Vi's prefix argument will be used. Otherwise, the prefix argument passed to
644 `viper-escape-to-vi' is used."
645 (interactive "P")
646 (message "Switched to VI state for the next command...")
647 (viper-escape-to-state arg nil 'vi-state))
649 ;; Escape to STATE mode for one Emacs command.
650 (defun viper-escape-to-state (arg events state)
651 ;;(let (com key prefix-arg)
652 (let (com key)
653 ;; this temporarily turns off Viper's minor mode keymaps
654 (viper-set-mode-vars-for state)
655 (viper-normalize-minor-mode-map-alist)
656 (if events (viper-set-unread-command-events events))
658 ;; protect against keyboard quit and other errors
659 (condition-case nil
660 (let (viper-vi-kbd-minor-mode
661 viper-insert-kbd-minor-mode
662 viper-emacs-kbd-minor-mode)
663 (unwind-protect
664 (progn
665 (setq com (key-binding (setq key
666 (if viper-xemacs-p
667 (read-key-sequence nil)
668 (read-key-sequence nil t)))))
669 ;; In case of binding indirection--chase definitions.
670 ;; Have to do it here because we execute this command under
671 ;; different keymaps, so command-execute may not do the
672 ;; right thing there
673 (while (vectorp com) (setq com (key-binding com))))
674 nil)
675 ;; Execute command com in the original Viper state, not in state
676 ;; `state'. Otherwise, if we switch buffers while executing the
677 ;; escaped to command, Viper's mode vars will remain those of
678 ;; `state'. When we return to the orig buffer, the bindings will be
679 ;; screwed up.
680 (viper-set-mode-vars-for viper-current-state)
682 ;; this-command, last-command-char, last-command-event
683 (setq this-command com)
684 (if viper-xemacs-p ; XEmacs represents key sequences as vectors
685 (setq last-command-event
686 (viper-copy-event (viper-seq-last-elt key))
687 last-command-char (event-to-character last-command-event))
688 ;; Emacs represents them as sequences (str or vec)
689 (setq last-command-event
690 (viper-copy-event (viper-seq-last-elt key))
691 last-command-char last-command-event))
693 (if (commandp com)
694 (progn
695 (setq prefix-arg (or prefix-arg arg))
696 (command-execute com)))
698 (quit (ding))
699 (error (beep 1))))
700 ;; set state in the new buffer
701 (viper-set-mode-vars-for viper-current-state))
703 (defun viper-exec-form-in-vi (form)
704 "Execute FORM in Vi state, regardless of the Ccurrent Vi state."
705 (let ((buff (current-buffer))
706 result)
707 (viper-set-mode-vars-for 'vi-state)
709 (condition-case nil
710 (let (viper-vi-kbd-minor-mode) ; execute without kbd macros
711 (setq result (eval form))
713 (error
714 (signal 'quit nil)))
716 (if (not (equal buff (current-buffer))) ; cmd switched buffer
717 (save-excursion
718 (set-buffer buff)
719 (viper-set-mode-vars-for viper-current-state)))
720 (viper-set-mode-vars-for viper-current-state)
721 result))
723 (defun viper-exec-form-in-emacs (form)
724 "Execute FORM in Emacs, temporarily disabling Viper's minor modes.
725 Similar to viper-escape-to-emacs, but accepts forms rather than keystrokes."
726 (let ((buff (current-buffer))
727 result)
728 (viper-set-mode-vars-for 'emacs-state)
729 (setq result (eval form))
730 (if (not (equal buff (current-buffer))) ; cmd switched buffer
731 (save-excursion
732 (set-buffer buff)
733 (viper-set-mode-vars-for viper-current-state)))
734 (viper-set-mode-vars-for viper-current-state)
735 result))
738 ;; This is needed because minor modes sometimes override essential Viper
739 ;; bindings. By letting Viper know which files these modes are in, it will
740 ;; arrange to reorganize minor-mode-map-alist so that things will work right.
741 (defun viper-harness-minor-mode (load-file)
742 "Familiarize Viper with a minor mode defined in LOAD_FILE.
743 Minor modes that have their own keymaps may overshadow Viper keymaps.
744 This function is designed to make Viper aware of the packages that define
745 such minor modes.
746 Usage:
747 (viper-harness-minor-mode load-file)
749 LOAD-FILE is a name of the file where the specific minor mode is defined.
750 Suffixes such as .el or .elc should be stripped."
752 (interactive "sEnter name of the load file: ")
754 (eval-after-load load-file '(viper-normalize-minor-mode-map-alist))
756 ;; Change the default for minor-mode-map-alist each time a harnessed minor
757 ;; mode adds its own keymap to the a-list.
758 (eval-after-load
759 load-file '(setq-default minor-mode-map-alist minor-mode-map-alist))
763 (defun viper-ESC (arg)
764 "Emulate ESC key in Emacs.
765 Prevents multiple escape keystrokes if viper-no-multiple-ESC is true.
766 If viper-no-multiple-ESC is 'twice double ESC would ding in vi-state.
767 Other ESC sequences are emulated via the current Emacs's major mode
768 keymap. This is more convenient on TTYs, since this won't block
769 function keys such as up,down, etc. ESC will also will also work as
770 a Meta key in this case. When viper-no-multiple-ESC is nil, ESC functions
771 as a Meta key and any number of multiple escapes is allowed."
772 (interactive "P")
773 (let (char)
774 (cond ((and (not viper-no-multiple-ESC) (eq viper-current-state 'vi-state))
775 (setq char (viper-read-char-exclusive))
776 (viper-escape-to-emacs arg (list ?\e char) ))
777 ((and (eq viper-no-multiple-ESC 'twice)
778 (eq viper-current-state 'vi-state))
779 (setq char (viper-read-char-exclusive))
780 (if (= char (string-to-char viper-ESC-key))
781 (ding)
782 (viper-escape-to-emacs arg (list ?\e char) )))
783 (t (ding)))
786 (defun viper-alternate-Meta-key (arg)
787 "Simulate Emacs Meta key."
788 (interactive "P")
789 (sit-for 1) (message "ESC-")
790 (viper-escape-to-emacs arg '(?\e)))
792 (defun viper-toggle-key-action ()
793 "Action bound to `viper-toggle-key'."
794 (interactive)
795 (if (and (< viper-expert-level 2) (equal viper-toggle-key "\C-z"))
796 (if (viper-window-display-p)
797 (viper-iconify)
798 (suspend-emacs))
799 (viper-change-state-to-emacs)))
802 ;; Intercept ESC sequences on dumb terminals.
803 ;; Based on the idea contributed by Marcelino Veiga Tuimil <mveiga@dit.upm.es>
805 ;; Check if last key was ESC and if so try to reread it as a function key.
806 ;; But only if there are characters to read during a very short time.
807 ;; Returns the last event, if any.
808 (defun viper-envelop-ESC-key ()
809 (let ((event last-input-event)
810 (keyseq [nil])
811 inhibit-quit)
812 (if (viper-ESC-event-p event)
813 (progn
814 (if (viper-fast-keysequence-p)
815 (progn
816 (let (minor-mode-map-alist)
817 (viper-set-unread-command-events event)
818 (setq keyseq
819 (funcall
820 (ad-get-orig-definition 'read-key-sequence) nil))
821 ) ; let
822 ;; If keyseq translates into something that still has ESC
823 ;; at the beginning, separate ESC from the rest of the seq.
824 ;; In XEmacs we check for events that are keypress meta-key
825 ;; and convert them into [escape key]
827 ;; This is needed for the following reason:
828 ;; If ESC is the first symbol, we interpret it as if the
829 ;; user typed ESC and then quickly some other symbols.
830 ;; If ESC is not the first one, then the key sequence
831 ;; entered was apparently translated into a function key or
832 ;; something (e.g., one may have
833 ;; (define-key function-key-map "\e[192z" [f11])
834 ;; which would translate the escape-sequence generated by
835 ;; f11 in an xterm window into the symbolic key f11.
837 ;; If `first-key' is not an ESC event, we make it into the
838 ;; last-command-event in order to pretend that this key was
839 ;; pressed. This is needed to allow arrow keys to be bound to
840 ;; macros. Otherwise, viper-exec-mapped-kbd-macro will think
841 ;; that the last event was ESC and so it'll execute whatever is
842 ;; bound to ESC. (Viper macros can't be bound to
843 ;; ESC-sequences).
844 (let* ((first-key (elt keyseq 0))
845 (key-mod (event-modifiers first-key)))
846 (cond ((viper-ESC-event-p first-key)
847 ;; put keys following ESC on the unread list
848 ;; and return ESC as the key-sequence
849 (viper-set-unread-command-events (subseq keyseq 1))
850 (setq last-input-event event
851 keyseq (if viper-emacs-p
852 "\e"
853 (vector (character-to-event ?\e)))))
854 ((and viper-xemacs-p
855 (key-press-event-p first-key)
856 (equal '(meta) key-mod))
857 (viper-set-unread-command-events
858 (vconcat (vector
859 (character-to-event (event-key first-key)))
860 (subseq keyseq 1)))
861 (setq last-input-event event
862 keyseq (vector (character-to-event ?\e))))
863 ((eventp first-key)
864 (setq last-command-event
865 (viper-copy-event first-key)))
867 ) ; end progn
869 ;; this is escape event with nothing after it
870 ;; put in unread-command-event and then re-read
871 (viper-set-unread-command-events event)
872 (setq keyseq
873 (funcall (ad-get-orig-definition 'read-key-sequence) nil))
875 ;; not an escape event
876 (setq keyseq (vector event)))
877 keyseq))
881 ;; Listen to ESC key.
882 ;; If a sequence of keys starting with ESC is issued with very short delays,
883 ;; interpret these keys in Emacs mode, so ESC won't be interpreted as a Vi key.
884 (defun viper-intercept-ESC-key ()
885 "Function that implements ESC key in Viper emulation of Vi."
886 (interactive)
887 (let ((cmd (or (key-binding (viper-envelop-ESC-key))
888 '(lambda () (interactive) (error "")))))
890 ;; call the actual function to execute ESC (if no other symbols followed)
891 ;; or the key bound to the ESC sequence (if the sequence was issued
892 ;; with very short delay between characters.
893 (if (eq cmd 'viper-intercept-ESC-key)
894 (setq cmd
895 (cond ((eq viper-current-state 'vi-state)
896 'viper-ESC)
897 ((eq viper-current-state 'insert-state)
898 'viper-exit-insert-state)
899 ((eq viper-current-state 'replace-state)
900 'viper-replace-state-exit-cmd)
901 (t 'viper-change-state-to-vi)
903 (call-interactively cmd)))
908 ;; prefix argument for Vi mode
910 ;; In Vi mode, prefix argument is a dotted pair (NUM . COM) where NUM
911 ;; represents the numeric value of the prefix argument and COM represents
912 ;; command prefix such as "c", "d", "m" and "y".
914 ;; Get value part of prefix-argument ARG.
915 (defsubst viper-p-val (arg)
916 (cond ((null arg) 1)
917 ((consp arg)
918 (if (or (null (car arg)) (equal (car arg) '(nil)))
919 1 (car arg)))
920 (t arg)))
922 ;; Get raw value part of prefix-argument ARG.
923 (defsubst viper-P-val (arg)
924 (cond ((consp arg) (car arg))
925 (t arg)))
927 ;; Get com part of prefix-argument ARG.
928 (defsubst viper-getcom (arg)
929 (cond ((null arg) nil)
930 ((consp arg) (cdr arg))
931 (t nil)))
933 ;; Get com part of prefix-argument ARG and modify it.
934 (defun viper-getCom (arg)
935 (let ((com (viper-getcom arg)))
936 (cond ((equal com ?c) ?c)
937 ;; Previously, ?c was being converted to ?C, but this prevented
938 ;; multiline replace regions.
939 ;;((equal com ?c) ?C)
940 ((equal com ?d) ?D)
941 ((equal com ?y) ?Y)
942 (t com))))
945 ;; Compute numeric prefix arg value.
946 ;; Invoked by EVENT. COM is the command part obtained so far.
947 (defun viper-prefix-arg-value (event com)
948 (let ((viper-intermediate-command 'viper-digit-argument)
949 value func)
950 ;; read while number
951 (while (and (viper-characterp event) (>= event ?0) (<= event ?9))
952 (setq value (+ (* (if (integerp value) value 0) 10) (- event ?0)))
953 (setq event (viper-read-event-convert-to-char)))
955 (setq prefix-arg value)
956 (if com (setq prefix-arg (cons prefix-arg com)))
957 (while (eq event ?U)
958 (viper-describe-arg prefix-arg)
959 (setq event (viper-read-event-convert-to-char)))
961 (if (or com (and (not (eq viper-current-state 'vi-state))
962 ;; make sure it is a Vi command
963 (viper-characterp event) (viper-vi-command-p event)
965 ;; If appears to be one of the vi commands,
966 ;; then execute it with funcall and clear prefix-arg in order to not
967 ;; confuse subsequent commands
968 (progn
969 ;; last-command-char is the char we want emacs to think was typed
970 ;; last. If com is not nil, the viper-digit-argument command was
971 ;; called from within viper-prefix-arg command, such as `d', `w',
972 ;; etc., i.e., the user typed, say, d2. In this case, `com' would be
973 ;; `d', `w', etc. If viper-digit-argument was invoked by
974 ;; viper-escape-to-vi (which is indicated by the fact that the
975 ;; current state is not vi-state), then `event' represents the vi
976 ;; command to be executed (e.g., `d', `w', etc). Again,
977 ;; last-command-char must make emacs believe that this is the command
978 ;; we typed.
979 (cond ((eq event 'return) (setq event ?\C-m))
980 ((eq event 'delete) (setq event ?\C-?))
981 ((eq event 'backspace) (setq event ?\C-h))
982 ((eq event 'space) (setq event ?\ )))
983 (setq last-command-char (or com event))
984 (setq func (viper-exec-form-in-vi
985 (` (key-binding (char-to-string (, event))))))
986 (funcall func prefix-arg)
987 (setq prefix-arg nil))
988 ;; some other command -- let emacs do it in its own way
989 (viper-set-unread-command-events event))
993 ;; Vi operator as prefix argument."
994 (defun viper-prefix-arg-com (char value com)
995 (let ((cont t)
996 cmd-info
997 cmd-to-exec-at-end)
998 (while (and cont
999 (memq char
1000 (list ?c ?d ?y ?! ?< ?> ?= ?# ?r ?R ?\"
1001 viper-buffer-search-char)))
1002 (if com
1003 ;; this means that we already have a command character, so we
1004 ;; construct a com list and exit while. however, if char is "
1005 ;; it is an error.
1006 (progn
1007 ;; new com is (CHAR . OLDCOM)
1008 (if (memq char '(?# ?\")) (error ""))
1009 (setq com (cons char com))
1010 (setq cont nil))
1011 ;; If com is nil we set com as char, and read more. Again, if char is
1012 ;; ", we read the name of register and store it in viper-use-register.
1013 ;; if char is !, =, or #, a complete com is formed so we exit the while
1014 ;; loop.
1015 (cond ((memq char '(?! ?=))
1016 (setq com char)
1017 (setq char (read-char))
1018 (setq cont nil))
1019 ((= char ?#)
1020 ;; read a char and encode it as com
1021 (setq com (+ 128 (read-char)))
1022 (setq char (read-char)))
1023 ((= char ?\")
1024 (let ((reg (read-char)))
1025 (if (viper-valid-register reg)
1026 (setq viper-use-register reg)
1027 (error ""))
1028 (setq char (read-char))))
1030 (setq com char)
1031 (setq char (read-char))))))
1033 (if (atom com)
1034 ;; `com' is a single char, so we construct the command argument
1035 ;; and if `char' is `?', we describe the arg; otherwise
1036 ;; we prepare the command that will be executed at the end.
1037 (progn
1038 (setq cmd-info (cons value com))
1039 (while (= char ?U)
1040 (viper-describe-arg cmd-info)
1041 (setq char (read-char)))
1042 ;; `char' is a movement cmd, a digit arg cmd, or a register cmd---so we
1043 ;; execute it at the very end
1044 (or (viper-movement-command-p char)
1045 (viper-digit-command-p char)
1046 (viper-regsuffix-command-p char)
1047 (= char ?!) ; bang command
1048 (error ""))
1049 (setq cmd-to-exec-at-end
1050 (viper-exec-form-in-vi
1051 (` (key-binding (char-to-string (, char)))))))
1053 ;; as com is non-nil, this means that we have a command to execute
1054 (if (memq (car com) '(?r ?R))
1055 ;; execute apropriate region command.
1056 (let ((char (car com)) (com (cdr com)))
1057 (setq prefix-arg (cons value com))
1058 (if (= char ?r) (viper-region prefix-arg)
1059 (viper-Region prefix-arg))
1060 ;; reset prefix-arg
1061 (setq prefix-arg nil))
1062 ;; otherwise, reset prefix arg and call appropriate command
1063 (setq value (if (null value) 1 value))
1064 (setq prefix-arg nil)
1065 (cond
1066 ;; If we change ?C to ?c here, then cc will enter replacement mode
1067 ;; rather than deleting lines. However, it will affect 1 less line than
1068 ;; normal. We decided to not use replacement mode here and follow Vi,
1069 ;; since replacement mode on n full lines can be achieved with nC.
1070 ((equal com '(?c . ?c)) (viper-line (cons value ?C)))
1071 ((equal com '(?d . ?d)) (viper-line (cons value ?D)))
1072 ((equal com '(?d . ?y)) (viper-yank-defun))
1073 ((equal com '(?y . ?y)) (viper-line (cons value ?Y)))
1074 ((equal com '(?< . ?<)) (viper-line (cons value ?<)))
1075 ((equal com '(?> . ?>)) (viper-line (cons value ?>)))
1076 ((equal com '(?! . ?!)) (viper-line (cons value ?!)))
1077 ((equal com '(?= . ?=)) (viper-line (cons value ?=)))
1078 (t (error "")))))
1080 (if cmd-to-exec-at-end
1081 (progn
1082 (setq last-command-char char)
1083 (setq last-command-event
1084 (viper-copy-event
1085 (if viper-xemacs-p (character-to-event char) char)))
1086 (condition-case nil
1087 (funcall cmd-to-exec-at-end cmd-info)
1088 (error
1089 (error "")))))
1092 (defun viper-describe-arg (arg)
1093 (let (val com)
1094 (setq val (viper-P-val arg)
1095 com (viper-getcom arg))
1096 (if (null val)
1097 (if (null com)
1098 (message "Value is nil, and command is nil")
1099 (message "Value is nil, and command is `%c'" com))
1100 (if (null com)
1101 (message "Value is `%d', and command is nil" val)
1102 (message "Value is `%d', and command is `%c'" val com)))))
1104 (defun viper-digit-argument (arg)
1105 "Begin numeric argument for the next command."
1106 (interactive "P")
1107 (viper-leave-region-active)
1108 (viper-prefix-arg-value
1109 last-command-char (if (consp arg) (cdr arg) nil)))
1111 (defun viper-command-argument (arg)
1112 "Accept a motion command as an argument."
1113 (interactive "P")
1114 (let ((viper-intermediate-command 'viper-command-argument))
1115 (condition-case nil
1116 (viper-prefix-arg-com
1117 last-command-char
1118 (cond ((null arg) nil)
1119 ((consp arg) (car arg))
1120 ((integerp arg) arg)
1121 (t (error viper-InvalidCommandArgument)))
1122 (cond ((null arg) nil)
1123 ((consp arg) (cdr arg))
1124 ((integerp arg) nil)
1125 (t (error viper-InvalidCommandArgument))))
1126 (quit (setq viper-use-register nil)
1127 (signal 'quit nil)))
1128 (viper-deactivate-mark)))
1131 ;; repeat last destructive command
1133 ;; Append region to text in register REG.
1134 ;; START and END are buffer positions indicating what to append.
1135 (defsubst viper-append-to-register (reg start end)
1136 (set-register reg (concat (if (stringp (get-register reg))
1137 (get-register reg) "")
1138 (buffer-substring start end))))
1140 ;; Saves last inserted text for possible use by viper-repeat command.
1141 (defun viper-save-last-insertion (beg end)
1142 (condition-case nil
1143 (setq viper-last-insertion (buffer-substring beg end))
1144 (error
1145 ;; beg or end marker are somehow screwed up
1146 (setq viper-last-insertion nil)))
1147 (setq viper-last-insertion (buffer-substring beg end))
1148 (or (< (length viper-d-com) 5)
1149 (setcar (nthcdr 4 viper-d-com) viper-last-insertion))
1150 (or (null viper-command-ring)
1151 (ring-empty-p viper-command-ring)
1152 (progn
1153 (setcar (nthcdr 4 (viper-current-ring-item viper-command-ring))
1154 viper-last-insertion)
1155 ;; del most recent elt, if identical to the second most-recent
1156 (viper-cleanup-ring viper-command-ring)))
1159 (defsubst viper-yank-last-insertion ()
1160 "Inserts the text saved by the previous viper-save-last-insertion command."
1161 (condition-case nil
1162 (insert viper-last-insertion)
1163 (error nil)))
1166 ;; define functions to be executed
1168 ;; invoked by the `C' command
1169 (defun viper-exec-change (m-com com)
1170 (or (and (markerp viper-com-point) (marker-position viper-com-point))
1171 (set-marker viper-com-point (point) (current-buffer)))
1172 ;; handle C cmd at the eol and at eob.
1173 (if (or (and (eolp) (= viper-com-point (point)))
1174 (= viper-com-point (point-max)))
1175 (progn
1176 (insert " ")(backward-char 1)))
1177 (if (= viper-com-point (point))
1178 (viper-forward-char-carefully))
1179 (set-mark viper-com-point)
1180 (if (eq m-com 'viper-next-line-at-bol)
1181 (viper-enlarge-region (mark t) (point)))
1182 (if (< (point) (mark t))
1183 (exchange-point-and-mark))
1184 (if (eq (preceding-char) ?\n)
1185 (viper-backward-char-carefully)) ; give back the newline
1186 (if (= com ?c)
1187 (viper-change (mark t) (point))
1188 (viper-change-subr (mark t) (point))))
1190 ;; this is invoked by viper-substitute-line
1191 (defun viper-exec-Change (m-com com)
1192 (save-excursion
1193 (set-mark viper-com-point)
1194 (viper-enlarge-region (mark t) (point))
1195 (if viper-use-register
1196 (progn
1197 (cond ((viper-valid-register viper-use-register '(letter digit))
1198 (copy-to-register
1199 viper-use-register (mark t) (point) nil))
1200 ((viper-valid-register viper-use-register '(Letter))
1201 (viper-append-to-register
1202 (downcase viper-use-register) (mark t) (point)))
1203 (t (setq viper-use-register nil)
1204 (error viper-InvalidRegister viper-use-register)))
1205 (setq viper-use-register nil)))
1206 (delete-region (mark t) (point)))
1207 (open-line 1)
1208 (if (= com ?C)
1209 (viper-change-state-to-insert)
1210 (viper-yank-last-insertion)))
1212 (defun viper-exec-delete (m-com com)
1213 (or (and (markerp viper-com-point) (marker-position viper-com-point))
1214 (set-marker viper-com-point (point) (current-buffer)))
1215 (if viper-use-register
1216 (progn
1217 (cond ((viper-valid-register viper-use-register '(letter digit))
1218 (copy-to-register
1219 viper-use-register viper-com-point (point) nil))
1220 ((viper-valid-register viper-use-register '(Letter))
1221 (viper-append-to-register
1222 (downcase viper-use-register) viper-com-point (point)))
1223 (t (setq viper-use-register nil)
1224 (error viper-InvalidRegister viper-use-register)))
1225 (setq viper-use-register nil)))
1226 (setq last-command
1227 (if (eq last-command 'd-command) 'kill-region nil))
1228 (kill-region viper-com-point (point))
1229 (setq this-command 'd-command)
1230 (if viper-ex-style-motion
1231 (if (and (eolp) (not (bolp))) (backward-char 1))))
1233 (defun viper-exec-Delete (m-com com)
1234 (save-excursion
1235 (set-mark viper-com-point)
1236 (viper-enlarge-region (mark t) (point))
1237 (if viper-use-register
1238 (progn
1239 (cond ((viper-valid-register viper-use-register '(letter digit))
1240 (copy-to-register
1241 viper-use-register (mark t) (point) nil))
1242 ((viper-valid-register viper-use-register '(Letter))
1243 (viper-append-to-register
1244 (downcase viper-use-register) (mark t) (point)))
1245 (t (setq viper-use-register nil)
1246 (error viper-InvalidRegister viper-use-register)))
1247 (setq viper-use-register nil)))
1248 (setq last-command
1249 (if (eq last-command 'D-command) 'kill-region nil))
1250 (kill-region (mark t) (point))
1251 (if (eq m-com 'viper-line) (setq this-command 'D-command)))
1252 (back-to-indentation))
1254 (defun viper-exec-yank (m-com com)
1255 (or (and (markerp viper-com-point) (marker-position viper-com-point))
1256 (set-marker viper-com-point (point) (current-buffer)))
1257 (if viper-use-register
1258 (progn
1259 (cond ((viper-valid-register viper-use-register '(letter digit))
1260 (copy-to-register
1261 viper-use-register viper-com-point (point) nil))
1262 ((viper-valid-register viper-use-register '(Letter))
1263 (viper-append-to-register
1264 (downcase viper-use-register) viper-com-point (point)))
1265 (t (setq viper-use-register nil)
1266 (error viper-InvalidRegister viper-use-register)))
1267 (setq viper-use-register nil)))
1268 (setq last-command nil)
1269 (copy-region-as-kill viper-com-point (point))
1270 (goto-char viper-com-point))
1272 (defun viper-exec-Yank (m-com com)
1273 (save-excursion
1274 (set-mark viper-com-point)
1275 (viper-enlarge-region (mark t) (point))
1276 (if viper-use-register
1277 (progn
1278 (cond ((viper-valid-register viper-use-register '(letter digit))
1279 (copy-to-register
1280 viper-use-register (mark t) (point) nil))
1281 ((viper-valid-register viper-use-register '(Letter))
1282 (viper-append-to-register
1283 (downcase viper-use-register) (mark t) (point)))
1284 (t (setq viper-use-register nil)
1285 (error viper-InvalidRegister viper-use-register)))
1286 (setq viper-use-register nil)))
1287 (setq last-command nil)
1288 (copy-region-as-kill (mark t) (point)))
1289 (viper-deactivate-mark)
1290 (goto-char viper-com-point))
1292 (defun viper-exec-bang (m-com com)
1293 (save-excursion
1294 (set-mark viper-com-point)
1295 (viper-enlarge-region (mark t) (point))
1296 (exchange-point-and-mark)
1297 (shell-command-on-region
1298 (mark t) (point)
1299 (if (= com ?!)
1300 (setq viper-last-shell-com
1301 (viper-read-string-with-history
1304 'viper-shell-history
1305 (car viper-shell-history)
1307 viper-last-shell-com)
1308 t)))
1310 (defun viper-exec-equals (m-com com)
1311 (save-excursion
1312 (set-mark viper-com-point)
1313 (viper-enlarge-region (mark t) (point))
1314 (if (> (mark t) (point)) (exchange-point-and-mark))
1315 (indent-region (mark t) (point) nil)))
1317 (defun viper-exec-shift (m-com com)
1318 (save-excursion
1319 (set-mark viper-com-point)
1320 (viper-enlarge-region (mark t) (point))
1321 (if (> (mark t) (point)) (exchange-point-and-mark))
1322 (indent-rigidly (mark t) (point)
1323 (if (= com ?>)
1324 viper-shift-width
1325 (- viper-shift-width))))
1326 ;; return point to where it was before shift
1327 (goto-char viper-com-point))
1329 ;; this is needed because some commands fake com by setting it to ?r, which
1330 ;; denotes repeated insert command.
1331 (defsubst viper-exec-dummy (m-com com)
1332 nil)
1334 (defun viper-exec-buffer-search (m-com com)
1335 (setq viper-s-string (buffer-substring (point) viper-com-point))
1336 (setq viper-s-forward t)
1337 (setq viper-search-history (cons viper-s-string viper-search-history))
1338 (viper-search viper-s-string viper-s-forward 1))
1340 (defvar viper-exec-array (make-vector 128 nil))
1342 ;; Using a dispatch array allows adding functions like buffer search
1343 ;; without affecting other functions. Buffer search can now be bound
1344 ;; to any character.
1346 (aset viper-exec-array ?c 'viper-exec-change)
1347 (aset viper-exec-array ?C 'viper-exec-Change)
1348 (aset viper-exec-array ?d 'viper-exec-delete)
1349 (aset viper-exec-array ?D 'viper-exec-Delete)
1350 (aset viper-exec-array ?y 'viper-exec-yank)
1351 (aset viper-exec-array ?Y 'viper-exec-Yank)
1352 (aset viper-exec-array ?r 'viper-exec-dummy)
1353 (aset viper-exec-array ?! 'viper-exec-bang)
1354 (aset viper-exec-array ?< 'viper-exec-shift)
1355 (aset viper-exec-array ?> 'viper-exec-shift)
1356 (aset viper-exec-array ?= 'viper-exec-equals)
1360 ;; This function is called by various movement commands to execute a
1361 ;; destructive command on the region specified by the movement command. For
1362 ;; instance, if the user types cw, then the command viper-forward-word will
1363 ;; call viper-execute-com to execute viper-exec-change, which eventually will
1364 ;; call viper-change to invoke the replace mode on the region.
1366 ;; The var viper-d-com is set to (M-COM VAL COM REG INSETED-TEXT COMMAND-KEYS)
1367 ;; via a call to viper-set-destructive-command, for later use by viper-repeat.
1368 (defun viper-execute-com (m-com val com)
1369 (let ((reg viper-use-register))
1370 ;; this is the special command `#'
1371 (if (> com 128)
1372 (viper-special-prefix-com (- com 128))
1373 (let ((fn (aref viper-exec-array (if (< com 0) (- com) com))))
1374 (if (null fn)
1375 (error "%c: %s" com viper-InvalidViCommand)
1376 (funcall fn m-com com))))
1377 (if (viper-dotable-command-p com)
1378 (viper-set-destructive-command
1379 (list m-com val
1380 (if (memq com (list ?c ?C ?!)) (- com) com)
1381 reg nil nil)))
1385 (defun viper-repeat (arg)
1386 "Re-execute last destructive command.
1387 Use the info in viper-d-com, which has the form
1388 \(com val ch reg inserted-text command-keys\),
1389 where `com' is the command to be re-executed, `val' is the
1390 argument to `com', `ch' is a flag for repeat, and `reg' is optional;
1391 if it exists, it is the name of the register for `com'.
1392 If the prefix argument, ARG, is non-nil, it is used instead of `val'."
1393 (interactive "P")
1394 (let ((save-point (point)) ; save point before repeating prev cmd
1395 ;; Pass along that we are repeating a destructive command
1396 ;; This tells viper-set-destructive-command not to update
1397 ;; viper-command-ring
1398 (viper-intermediate-command 'viper-repeat))
1399 (if (eq last-command 'viper-undo)
1400 ;; if the last command was viper-undo, then undo-more
1401 (viper-undo-more)
1402 ;; otherwise execute the command stored in viper-d-com. if arg is
1403 ;; non-nil its prefix value is used as new prefix value for the command.
1404 (let ((m-com (car viper-d-com))
1405 (val (viper-P-val arg))
1406 (com (nth 2 viper-d-com))
1407 (reg (nth 3 viper-d-com)))
1408 (if (null val) (setq val (nth 1 viper-d-com)))
1409 (if (null m-com) (error "No previous command to repeat."))
1410 (setq viper-use-register reg)
1411 (if (nth 4 viper-d-com) ; text inserted by command
1412 (setq viper-last-insertion (nth 4 viper-d-com)
1413 viper-d-char (nth 4 viper-d-com)))
1414 (funcall m-com (cons val com))
1415 (cond ((and (< save-point (point)) viper-keep-point-on-repeat)
1416 (goto-char save-point)) ; go back to before repeat.
1417 ((and (< save-point (point)) viper-ex-style-editing)
1418 (or (bolp) (backward-char 1))))
1419 (if (and (eolp) (not (bolp)))
1420 (backward-char 1))
1422 (viper-adjust-undo) ; take care of undo
1423 ;; If the prev cmd was rotating the command ring, this means that `.' has
1424 ;; just executed a command from that ring. So, push it on the ring again.
1425 ;; If we are just executing previous command , then don't push viper-d-com
1426 ;; because viper-d-com is not fully constructed in this case (its keys and
1427 ;; the inserted text may be nil). Besides, in this case, the command
1428 ;; executed by `.' is already on the ring.
1429 (if (eq last-command 'viper-display-current-destructive-command)
1430 (viper-push-onto-ring viper-d-com 'viper-command-ring))
1431 (viper-deactivate-mark)
1434 (defun viper-repeat-from-history ()
1435 "Repeat a destructive command from history.
1436 Doesn't change viper-command-ring in any way, so `.' will work as before
1437 executing this command.
1438 This command is supposed to be bound to a two-character Vi macro where
1439 the second character is a digit 0 to 9. The digit indicates which
1440 history command to execute. `<char>0' is equivalent to `.', `<char>1'
1441 invokes the command before that, etc."
1442 (interactive)
1443 (let* ((viper-intermediate-command 'repeating-display-destructive-command)
1444 (idx (cond (viper-this-kbd-macro
1445 (string-to-number
1446 (symbol-name (elt viper-this-kbd-macro 1))))
1447 (t 0)))
1448 (num idx)
1449 (viper-d-com viper-d-com))
1451 (or (and (numberp num) (<= 0 num) (<= num 9))
1452 (progn
1453 (setq idx 0
1454 num 0)
1455 (message
1456 "`viper-repeat-from-history' must be invoked as a Vi macro bound to `<key><digit>'")))
1457 (while (< 0 num)
1458 (setq viper-d-com (viper-special-ring-rotate1 viper-command-ring -1))
1459 (setq num (1- num)))
1460 (viper-repeat nil)
1461 (while (> idx num)
1462 (viper-special-ring-rotate1 viper-command-ring 1)
1463 (setq num (1+ num)))
1467 ;; The hash-command. It is invoked interactively by the key sequence #<char>.
1468 ;; The chars that can follow `#' are determined by viper-hash-command-p
1469 (defun viper-special-prefix-com (char)
1470 (cond ((= char ?c)
1471 (downcase-region (min viper-com-point (point))
1472 (max viper-com-point (point))))
1473 ((= char ?C)
1474 (upcase-region (min viper-com-point (point))
1475 (max viper-com-point (point))))
1476 ((= char ?g)
1477 (push-mark viper-com-point t)
1478 (viper-global-execute))
1479 ((= char ?q)
1480 (push-mark viper-com-point t)
1481 (viper-quote-region))
1482 ((= char ?s) (funcall viper-spell-function viper-com-point (point)))
1483 (t (error "#%c: %s" char viper-InvalidViCommand))))
1486 ;; undoing
1488 (defun viper-undo ()
1489 "Undo previous change."
1490 (interactive)
1491 (message "undo!")
1492 (let ((modified (buffer-modified-p))
1493 (before-undo-pt (point-marker))
1494 (after-change-functions after-change-functions)
1495 undo-beg-posn undo-end-posn)
1497 ;; no need to remove this hook, since this var has scope inside a let.
1498 (add-hook 'after-change-functions
1499 '(lambda (beg end len)
1500 (setq undo-beg-posn beg
1501 undo-end-posn (or end beg))))
1503 (undo-start)
1504 (undo-more 2)
1505 (setq undo-beg-posn (or undo-beg-posn before-undo-pt)
1506 undo-end-posn (or undo-end-posn undo-beg-posn))
1508 (goto-char undo-beg-posn)
1509 (sit-for 0)
1510 (if (and viper-keep-point-on-undo
1511 (pos-visible-in-window-p before-undo-pt))
1512 (progn
1513 (push-mark (point-marker) t)
1514 (viper-sit-for-short 300)
1515 (goto-char undo-end-posn)
1516 (viper-sit-for-short 300)
1517 (if (and (> (viper-chars-in-region undo-beg-posn before-undo-pt) 1)
1518 (> (viper-chars-in-region undo-end-posn before-undo-pt) 1))
1519 (goto-char before-undo-pt)
1520 (goto-char undo-beg-posn)))
1521 (push-mark before-undo-pt t))
1522 (if (and (eolp) (not (bolp))) (backward-char 1))
1523 (if (not modified) (set-buffer-modified-p t)))
1524 (setq this-command 'viper-undo))
1526 ;; Continue undoing previous changes.
1527 (defun viper-undo-more ()
1528 (message "undo more!")
1529 (condition-case nil
1530 (undo-more 1)
1531 (error (beep)
1532 (message "No further undo information in this buffer")))
1533 (if (and (eolp) (not (bolp))) (backward-char 1))
1534 (setq this-command 'viper-undo))
1536 ;; The following two functions are used to set up undo properly.
1537 ;; In VI, unlike Emacs, if you open a line, say, and add a bunch of lines,
1538 ;; they are undone all at once.
1539 (defun viper-adjust-undo ()
1540 (if viper-undo-needs-adjustment
1541 (let ((inhibit-quit t)
1542 tmp tmp2)
1543 (setq viper-undo-needs-adjustment nil)
1544 (if (listp buffer-undo-list)
1545 (if (setq tmp (memq viper-buffer-undo-list-mark buffer-undo-list))
1546 (progn
1547 (setq tmp2 (cdr tmp)) ; the part after mark
1549 ;; cut tail from buffer-undo-list temporarily by direct
1550 ;; manipulation with pointers in buffer-undo-list
1551 (setcdr tmp nil)
1553 (setq buffer-undo-list (delq nil buffer-undo-list))
1554 (setq buffer-undo-list
1555 (delq viper-buffer-undo-list-mark buffer-undo-list))
1556 ;; restore tail of buffer-undo-list
1557 (setq buffer-undo-list (nconc buffer-undo-list tmp2)))
1558 (setq buffer-undo-list (delq nil buffer-undo-list)))))
1562 (defun viper-set-complex-command-for-undo ()
1563 (if (listp buffer-undo-list)
1564 (if (not viper-undo-needs-adjustment)
1565 (let ((inhibit-quit t))
1566 (setq buffer-undo-list
1567 (cons viper-buffer-undo-list-mark buffer-undo-list))
1568 (setq viper-undo-needs-adjustment t)))))
1573 (defun viper-display-current-destructive-command ()
1574 (let ((text (nth 4 viper-d-com))
1575 (keys (nth 5 viper-d-com))
1576 (max-text-len 30))
1578 (setq this-command 'viper-display-current-destructive-command)
1580 (message " `.' runs %s%s"
1581 (concat "`" (viper-array-to-string keys) "'")
1582 (viper-abbreviate-string
1583 (if viper-xemacs-p
1584 (replace-in-string
1585 (cond ((characterp text) (char-to-string text))
1586 ((stringp text) text)
1587 (t ""))
1588 "\n" "^J")
1589 text)
1590 max-text-len
1591 " inserting `" "'" " ......."))
1595 ;; don't change viper-d-com if it was viper-repeat command invoked with `.'
1596 ;; or in some other way (non-interactively).
1597 (defun viper-set-destructive-command (list)
1598 (or (eq viper-intermediate-command 'viper-repeat)
1599 (progn
1600 (setq viper-d-com list)
1601 (setcar (nthcdr 5 viper-d-com)
1602 (viper-array-to-string (if (arrayp viper-this-command-keys)
1603 viper-this-command-keys
1604 (this-command-keys))))
1605 (viper-push-onto-ring viper-d-com 'viper-command-ring)))
1606 (setq viper-this-command-keys nil))
1608 (defun viper-prev-destructive-command (next)
1609 "Find previous destructive command in the history of destructive commands.
1610 With prefix argument, find next destructive command."
1611 (interactive "P")
1612 (let (cmd viper-intermediate-command)
1613 (if (eq last-command 'viper-display-current-destructive-command)
1614 ;; repeated search through command history
1615 (setq viper-intermediate-command
1616 'repeating-display-destructive-command)
1617 ;; first search through command history--set temp ring
1618 (setq viper-temp-command-ring (copy-list viper-command-ring)))
1619 (setq cmd (if next
1620 (viper-special-ring-rotate1 viper-temp-command-ring 1)
1621 (viper-special-ring-rotate1 viper-temp-command-ring -1)))
1622 (if (null cmd)
1624 (setq viper-d-com cmd))
1625 (viper-display-current-destructive-command)))
1627 (defun viper-next-destructive-command ()
1628 "Find next destructive command in the history of destructive commands."
1629 (interactive)
1630 (viper-prev-destructive-command 'next))
1632 (defun viper-insert-prev-from-insertion-ring (arg)
1633 "Cycle through insertion ring in the direction of older insertions.
1634 Undoes previous insertion and inserts new.
1635 With prefix argument, cycles in the direction of newer elements.
1636 In minibuffer, this command executes whatever the invocation key is bound
1637 to in the global map, instead of cycling through the insertion ring."
1638 (interactive "P")
1639 (let (viper-intermediate-command)
1640 (if (eq last-command 'viper-insert-from-insertion-ring)
1641 (progn ; repeated search through insertion history
1642 (setq viper-intermediate-command 'repeating-insertion-from-ring)
1643 (if (eq viper-current-state 'replace-state)
1644 (undo 1)
1645 (if viper-last-inserted-string-from-insertion-ring
1646 (backward-delete-char
1647 (length viper-last-inserted-string-from-insertion-ring))))
1649 ;;first search through insertion history
1650 (setq viper-temp-insertion-ring (copy-list viper-insertion-ring)))
1651 (setq this-command 'viper-insert-from-insertion-ring)
1652 ;; so that things will be undone properly
1653 (setq buffer-undo-list (cons nil buffer-undo-list))
1654 (setq viper-last-inserted-string-from-insertion-ring
1655 (viper-special-ring-rotate1 viper-temp-insertion-ring (if arg 1 -1)))
1657 ;; this change of viper-intermediate-command must come after
1658 ;; viper-special-ring-rotate1, so that the ring will rotate, but before the
1659 ;; insertion.
1660 (setq viper-intermediate-command nil)
1661 (if viper-last-inserted-string-from-insertion-ring
1662 (insert viper-last-inserted-string-from-insertion-ring))
1665 (defun viper-insert-next-from-insertion-ring ()
1666 "Cycle through insertion ring in the direction of older insertions.
1667 Undo previous insertion and inserts new."
1668 (interactive)
1669 (viper-insert-prev-from-insertion-ring 'next))
1672 ;; some region utilities
1674 ;; If at the last line of buffer, add \\n before eob, if newline is missing.
1675 (defun viper-add-newline-at-eob-if-necessary ()
1676 (save-excursion
1677 (end-of-line)
1678 ;; make sure all lines end with newline, unless in the minibuffer or
1679 ;; when requested otherwise (require-final-newline is nil)
1680 (if (and (eobp)
1681 (not (bolp))
1682 require-final-newline
1683 (not (viper-is-in-minibuffer))
1684 (not buffer-read-only))
1685 (insert "\n"))))
1687 (defun viper-yank-defun ()
1688 (mark-defun)
1689 (copy-region-as-kill (point) (mark t)))
1691 ;; Enlarge region between BEG and END.
1692 (defun viper-enlarge-region (beg end)
1693 (or beg (setq beg end)) ; if beg is nil, set to end
1694 (or end (setq end beg)) ; if end is nil, set to beg
1696 (if (< beg end)
1697 (progn (goto-char beg) (set-mark end))
1698 (goto-char end)
1699 (set-mark beg))
1700 (beginning-of-line)
1701 (exchange-point-and-mark)
1702 (if (or (not (eobp)) (not (bolp))) (forward-line 1))
1703 (if (not (eobp)) (beginning-of-line))
1704 (if (> beg end) (exchange-point-and-mark)))
1707 ;; Quote region by each line with a user supplied string.
1708 (defun viper-quote-region ()
1709 (setq viper-quote-string
1710 (viper-read-string-with-history
1711 "Quote string: "
1713 'viper-quote-region-history
1714 viper-quote-string))
1715 (viper-enlarge-region (point) (mark t))
1716 (if (> (point) (mark t)) (exchange-point-and-mark))
1717 (insert viper-quote-string)
1718 (beginning-of-line)
1719 (forward-line 1)
1720 (while (and (< (point) (mark t)) (bolp))
1721 (insert viper-quote-string)
1722 (beginning-of-line)
1723 (forward-line 1)))
1725 ;; Tells whether BEG is on the same line as END.
1726 ;; If one of the args is nil, it'll return nil.
1727 (defun viper-same-line (beg end)
1728 (let ((selective-display nil)
1729 (incr 0)
1730 temp)
1731 (if (and beg end (> beg end))
1732 (setq temp beg
1733 beg end
1734 end temp))
1735 (if (and beg end)
1736 (cond ((or (> beg (point-max)) (> end (point-max))) ; out of range
1737 nil)
1739 ;; This 'if' is needed because Emacs treats the next empty line
1740 ;; as part of the previous line.
1741 (if (= (viper-line-pos 'start) end)
1742 (setq incr 1))
1743 (<= (+ incr (count-lines beg end)) 1))))
1747 ;; Check if the string ends with a newline.
1748 (defun viper-end-with-a-newline-p (string)
1749 (or (string= string "")
1750 (= (viper-seq-last-elt string) ?\n)))
1752 (defun viper-tmp-insert-at-eob (msg)
1753 (let ((savemax (point-max)))
1754 (goto-char savemax)
1755 (insert msg)
1756 (sit-for 2)
1757 (goto-char savemax) (delete-region (point) (point-max))
1762 ;;; Minibuffer business
1764 (defsubst viper-set-minibuffer-style ()
1765 (add-hook 'minibuffer-setup-hook 'viper-minibuffer-setup-sentinel))
1768 (defun viper-minibuffer-setup-sentinel ()
1769 (let ((hook (if viper-vi-style-in-minibuffer
1770 'viper-change-state-to-insert
1771 'viper-change-state-to-emacs)))
1772 (funcall hook)
1775 ;; Interpret last event in the local map first; if fails, use exit-minibuffer.
1776 ;; Run viper-minibuffer-exit-hook before exiting.
1777 (defun viper-exit-minibuffer ()
1778 "Exit minibuffer Viper way."
1779 (interactive)
1780 (let (command)
1781 (setq command (local-key-binding (char-to-string last-command-char)))
1782 (run-hooks 'viper-minibuffer-exit-hook)
1783 (if command
1784 (command-execute command)
1785 (exit-minibuffer))))
1788 (defcustom viper-smart-suffix-list
1789 '("" "tex" "c" "cc" "C" "el" "java" "html" "htm" "pl" "P" "p")
1790 "*List of suffixes that Viper automatically tries to append to filenames ending with a `.'.
1791 This is useful when you the current directory contains files with the same
1792 prefix and many different suffixes. Usually, only one of the suffixes
1793 represents an editable file. However, file completion will stop at the `.'
1794 The smart suffix feature lets you hit RET in such a case, and Viper will
1795 select the appropriate suffix.
1797 Suffixes are tried in the order given and the first suffix for which a
1798 corresponding file exists is selected. If no file exists for any of the
1799 suffixes, the user is asked to confirm.
1801 To turn this feature off, set this variable to nil."
1802 :type '(set string)
1803 :group 'viper)
1806 ;; Try to add a suitable suffix to files whose name ends with a `.'
1807 ;; Useful when the user hits RET on a non-completed file name.
1808 ;; Used as a minibuffer exit hook in read-file-name
1809 (defun viper-file-add-suffix ()
1810 (let ((count 0)
1811 (len (length viper-smart-suffix-list))
1812 (file (buffer-string))
1813 found key cmd suff)
1814 (goto-char (point-max))
1815 (if (and viper-smart-suffix-list (string-match "\\.$" file))
1816 (progn
1817 (while (and (not found) (< count len))
1818 (setq suff (nth count viper-smart-suffix-list)
1819 count (1+ count))
1820 (if (file-exists-p
1821 (format "%s%s" (substitute-in-file-name file) suff))
1822 (progn
1823 (setq found t)
1824 (insert suff))))
1826 (if found
1828 (viper-tmp-insert-at-eob " [Please complete file name]")
1829 (unwind-protect
1830 (while (not (memq cmd
1831 '(exit-minibuffer viper-exit-minibuffer)))
1832 (setq cmd
1833 (key-binding (setq key (read-key-sequence nil))))
1834 (cond ((eq cmd 'self-insert-command)
1835 (if viper-xemacs-p
1836 (insert (events-to-keys key))
1837 (insert key)))
1838 ((memq cmd '(exit-minibuffer viper-exit-minibuffer))
1839 nil)
1840 (t (command-execute cmd)))
1842 ))))
1845 (defun viper-minibuffer-trim-tail ()
1846 "Delete junk at the end of the first line of the minibuffer input.
1847 Remove this function from `viper-minibuffer-exit-hook', if this causes
1848 problems."
1849 (if (viper-is-in-minibuffer)
1850 (progn
1851 (goto-char (point-min))
1852 (end-of-line)
1853 (delete-region (point) (point-max)))))
1856 ;;; Reading string with history
1858 (defun viper-read-string-with-history (prompt &optional initial
1859 history-var default keymap)
1860 ;; Read string, prompting with PROMPT and inserting the INITIAL
1861 ;; value. Uses HISTORY-VAR. DEFAULT is the default value to accept if the
1862 ;; input is an empty string. Use KEYMAP, if given, or the
1863 ;; minibuffer-local-map.
1864 ;; Default value is displayed until the user types something in the
1865 ;; minibuffer.
1866 (let ((minibuffer-setup-hook
1867 '(lambda ()
1868 (if (stringp initial)
1869 (progn
1870 ;; don't wait if we have unread events or in kbd macro
1871 (or unread-command-events
1872 executing-kbd-macro
1873 (sit-for 840))
1874 (erase-buffer)
1875 (insert initial)))
1876 (viper-minibuffer-setup-sentinel)))
1877 (val "")
1878 (padding "")
1879 temp-msg)
1881 (setq keymap (or keymap minibuffer-local-map)
1882 initial (or initial "")
1883 temp-msg (if default
1884 (format "(default: %s) " default)
1885 ""))
1887 (setq viper-incomplete-ex-cmd nil)
1888 (setq val (read-from-minibuffer prompt
1889 (concat temp-msg initial val padding)
1890 keymap nil history-var))
1891 (setq minibuffer-setup-hook nil
1892 padding (viper-array-to-string (this-command-keys))
1893 temp-msg "")
1894 ;; the following tries to be smart about what to put in history
1895 (if (not (string= val (car (eval history-var))))
1896 (set history-var (cons val (eval history-var))))
1897 (if (or (string= (nth 0 (eval history-var)) (nth 1 (eval history-var)))
1898 (string= (nth 0 (eval history-var)) ""))
1899 (set history-var (cdr (eval history-var))))
1900 ;; If the user enters nothing but the prev cmd wasn't viper-ex,
1901 ;; viper-command-argument, or `! shell-command', this probably means
1902 ;; that the user typed something then erased. Return "" in this case, not
1903 ;; the default---the default is too confusing in this case.
1904 (cond ((and (string= val "")
1905 (not (string= prompt "!")) ; was a `! shell-command'
1906 (not (memq last-command
1907 '(viper-ex
1908 viper-command-argument
1912 ((string= val "") (or default ""))
1913 (t val))
1918 ;; insertion commands
1920 ;; Called when state changes from Insert Vi command mode.
1921 ;; Repeats the insertion command if Insert state was entered with prefix
1922 ;; argument > 1.
1923 (defun viper-repeat-insert-command ()
1924 (let ((i-com (car viper-d-com))
1925 (val (nth 1 viper-d-com))
1926 (char (nth 2 viper-d-com)))
1927 (if (and val (> val 1)) ; first check that val is non-nil
1928 (progn
1929 (setq viper-d-com (list i-com (1- val) ?r nil nil nil))
1930 (viper-repeat nil)
1931 (setq viper-d-com (list i-com val char nil nil nil))
1932 ))))
1934 (defun viper-insert (arg)
1935 "Insert before point."
1936 (interactive "P")
1937 (viper-set-complex-command-for-undo)
1938 (let ((val (viper-p-val arg))
1939 (com (viper-getcom arg)))
1940 (viper-set-destructive-command (list 'viper-insert val ?r nil nil nil))
1941 (if com
1942 (viper-loop val (viper-yank-last-insertion))
1943 (viper-change-state-to-insert))))
1945 (defun viper-append (arg)
1946 "Append after point."
1947 (interactive "P")
1948 (viper-set-complex-command-for-undo)
1949 (let ((val (viper-p-val arg))
1950 (com (viper-getcom arg)))
1951 (viper-set-destructive-command (list 'viper-append val ?r nil nil nil))
1952 (if (not (eolp)) (forward-char))
1953 (if (equal com ?r)
1954 (viper-loop val (viper-yank-last-insertion))
1955 (viper-change-state-to-insert))))
1957 (defun viper-Append (arg)
1958 "Append at end of line."
1959 (interactive "P")
1960 (viper-set-complex-command-for-undo)
1961 (let ((val (viper-p-val arg))
1962 (com (viper-getcom arg)))
1963 (viper-set-destructive-command (list 'viper-Append val ?r nil nil nil))
1964 (end-of-line)
1965 (if (equal com ?r)
1966 (viper-loop val (viper-yank-last-insertion))
1967 (viper-change-state-to-insert))))
1969 (defun viper-Insert (arg)
1970 "Insert before first non-white."
1971 (interactive "P")
1972 (viper-set-complex-command-for-undo)
1973 (let ((val (viper-p-val arg))
1974 (com (viper-getcom arg)))
1975 (viper-set-destructive-command (list 'viper-Insert val ?r nil nil nil))
1976 (back-to-indentation)
1977 (if (equal com ?r)
1978 (viper-loop val (viper-yank-last-insertion))
1979 (viper-change-state-to-insert))))
1981 (defun viper-open-line (arg)
1982 "Open line below."
1983 (interactive "P")
1984 (viper-set-complex-command-for-undo)
1985 (let ((val (viper-p-val arg))
1986 (com (viper-getcom arg)))
1987 (viper-set-destructive-command (list 'viper-open-line val ?r nil nil nil))
1988 (let ((col (current-indentation)))
1989 (if (equal com ?r)
1990 (viper-loop val
1991 (end-of-line)
1992 (newline 1)
1993 (if viper-auto-indent
1994 (progn
1995 (setq viper-cted t)
1996 (if viper-electric-mode
1997 (indent-according-to-mode)
1998 (indent-to col))
2000 (viper-yank-last-insertion))
2001 (end-of-line)
2002 (newline 1)
2003 (if viper-auto-indent
2004 (progn
2005 (setq viper-cted t)
2006 (if viper-electric-mode
2007 (indent-according-to-mode)
2008 (indent-to col))))
2009 (viper-change-state-to-insert)))))
2011 (defun viper-Open-line (arg)
2012 "Open line above."
2013 (interactive "P")
2014 (viper-set-complex-command-for-undo)
2015 (let ((val (viper-p-val arg))
2016 (com (viper-getcom arg)))
2017 (viper-set-destructive-command (list 'viper-Open-line val ?r nil nil nil))
2018 (let ((col (current-indentation)))
2019 (if (equal com ?r)
2020 (viper-loop val
2021 (beginning-of-line)
2022 (open-line 1)
2023 (if viper-auto-indent
2024 (progn
2025 (setq viper-cted t)
2026 (if viper-electric-mode
2027 (indent-according-to-mode)
2028 (indent-to col))
2030 (viper-yank-last-insertion))
2031 (beginning-of-line)
2032 (open-line 1)
2033 (if viper-auto-indent
2034 (progn
2035 (setq viper-cted t)
2036 (if viper-electric-mode
2037 (indent-according-to-mode)
2038 (indent-to col))
2040 (viper-change-state-to-insert)))))
2042 (defun viper-open-line-at-point (arg)
2043 "Open line at point."
2044 (interactive "P")
2045 (viper-set-complex-command-for-undo)
2046 (let ((val (viper-p-val arg))
2047 (com (viper-getcom arg)))
2048 (viper-set-destructive-command
2049 (list 'viper-open-line-at-point val ?r nil nil nil))
2050 (if (equal com ?r)
2051 (viper-loop val
2052 (open-line 1)
2053 (viper-yank-last-insertion))
2054 (open-line 1)
2055 (viper-change-state-to-insert))))
2057 (defun viper-substitute (arg)
2058 "Substitute characters."
2059 (interactive "P")
2060 (let ((val (viper-p-val arg))
2061 (com (viper-getcom arg)))
2062 (push-mark nil t)
2063 (forward-char val)
2064 (if (equal com ?r)
2065 (viper-change-subr (mark t) (point))
2066 (viper-change (mark t) (point)))
2067 (viper-set-destructive-command (list 'viper-substitute val ?r nil nil nil))
2070 ;; Command bound to S
2071 (defun viper-substitute-line (arg)
2072 "Substitute lines."
2073 (interactive "p")
2074 (viper-set-complex-command-for-undo)
2075 (viper-line (cons arg ?C)))
2077 ;; Prepare for replace
2078 (defun viper-start-replace ()
2079 (setq viper-began-as-replace t
2080 viper-sitting-in-replace t
2081 viper-replace-chars-to-delete 0)
2082 (viper-add-hook
2083 'viper-after-change-functions 'viper-replace-mode-spy-after t)
2084 (viper-add-hook
2085 'viper-before-change-functions 'viper-replace-mode-spy-before t)
2086 ;; this will get added repeatedly, but no harm
2087 (add-hook 'after-change-functions 'viper-after-change-sentinel t)
2088 (add-hook 'before-change-functions 'viper-before-change-sentinel t)
2089 (viper-move-marker-locally 'viper-last-posn-in-replace-region
2090 (viper-replace-start))
2091 (viper-add-hook
2092 'viper-post-command-hooks 'viper-replace-state-post-command-sentinel t)
2093 (viper-add-hook
2094 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel t)
2095 ;; guard against a smartie who switched from R-replace to normal replace
2096 (viper-remove-hook
2097 'viper-post-command-hooks 'viper-R-state-post-command-sentinel)
2098 (if overwrite-mode (overwrite-mode nil))
2102 (defun viper-replace-mode-spy-before (beg end)
2103 (setq viper-replace-region-chars-deleted (viper-chars-in-region beg end))
2106 ;; Invoked as an after-change-function to calculate how many chars have to be
2107 ;; deleted. This function may be called several times within a single command,
2108 ;; if this command performs several separate buffer changes. Therefore, if adds
2109 ;; up the number of chars inserted and subtracts the number of chars deleted.
2110 (defun viper-replace-mode-spy-after (beg end length)
2111 (if (memq viper-intermediate-command
2112 '(dabbrev-expand repeating-insertion-from-ring))
2113 ;; Take special care of text insertion from insertion ring inside
2114 ;; replacement overlays.
2115 (progn
2116 (setq viper-replace-chars-to-delete 0)
2117 (viper-move-marker-locally
2118 'viper-last-posn-in-replace-region (point)))
2120 (let* ((real-end (min end (viper-replace-end)))
2121 (column-shift (- (save-excursion (goto-char real-end)
2122 (current-column))
2123 (save-excursion (goto-char beg)
2124 (current-column))))
2125 (chars-deleted 0))
2127 (if (> length 0)
2128 (setq chars-deleted viper-replace-region-chars-deleted))
2129 (setq viper-replace-region-chars-deleted 0)
2130 (setq viper-replace-chars-to-delete
2131 (+ viper-replace-chars-to-delete
2133 ;; if column shift is bigger, due to a TAB insertion, take
2134 ;; column-shift instead of the number of inserted chars
2135 (max (viper-chars-in-region beg real-end)
2136 ;; This test accounts for Chinese/Japanese/... chars,
2137 ;; which occupy 2 columns instead of one. If we use
2138 ;; column-shift here, we may delete two chars instead of
2139 ;; one when the user types one Chinese character. Deleting
2140 ;; two would be OK, if they were European chars, but it is
2141 ;; not OK if they are Chinese chars. Since it is hard to
2142 ;; figure out which characters are being deleted in any
2143 ;; given region, we decided to treat Eastern and European
2144 ;; characters equally, even though Eastern chars may
2145 ;; occupy more columns.
2146 (if (memq this-command '(self-insert-command
2147 quoted-insert viper-insert-tab))
2148 column-shift
2150 ;; the number of deleted chars
2151 chars-deleted)))
2153 (viper-move-marker-locally
2154 'viper-last-posn-in-replace-region
2155 (max (if (> end (viper-replace-end)) (viper-replace-end) end)
2156 (or (marker-position viper-last-posn-in-replace-region)
2157 (viper-replace-start))
2162 ;; Make sure we don't delete more than needed.
2163 ;; This is executed at viper-last-posn-in-replace-region
2164 (defsubst viper-trim-replace-chars-to-delete-if-necessary ()
2165 (setq viper-replace-chars-to-delete
2166 (max 0
2167 (min viper-replace-chars-to-delete
2168 ;; Don't delete more than to the end of repl overlay
2169 (viper-chars-in-region
2170 (viper-replace-end) viper-last-posn-in-replace-region)
2171 ;; point is viper-last-posn-in-replace-region now
2172 ;; So, this limits deletion to the end of line
2173 (viper-chars-in-region (point) (viper-line-pos 'end))
2174 ))))
2177 ;; Delete stuff between viper-last-posn-in-replace-region and the end of
2178 ;; viper-replace-overlay-marker, if viper-last-posn-in-replace-region is within
2179 ;; the overlay and current point is before the end of the overlay.
2180 ;; Don't delete anything if current point is past the end of the overlay.
2181 (defun viper-finish-change ()
2182 (viper-remove-hook
2183 'viper-after-change-functions 'viper-replace-mode-spy-after)
2184 (viper-remove-hook
2185 'viper-before-change-functions 'viper-replace-mode-spy-before)
2186 (viper-remove-hook
2187 'viper-post-command-hooks 'viper-replace-state-post-command-sentinel)
2188 (viper-remove-hook
2189 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel)
2190 (viper-restore-cursor-color-after-replace)
2191 (setq viper-sitting-in-replace nil) ; just in case we'll need to know it
2192 (save-excursion
2193 (if (and viper-replace-overlay
2194 (viper-pos-within-region viper-last-posn-in-replace-region
2195 (viper-replace-start)
2196 (viper-replace-end))
2197 (< (point) (viper-replace-end)))
2198 (delete-region
2199 viper-last-posn-in-replace-region (viper-replace-end))))
2201 (if (eq viper-current-state 'replace-state)
2202 (viper-downgrade-to-insert))
2203 ;; replace mode ended => nullify viper-last-posn-in-replace-region
2204 (viper-move-marker-locally 'viper-last-posn-in-replace-region nil)
2205 (viper-hide-replace-overlay)
2206 (viper-refresh-mode-line)
2207 (viper-put-string-on-kill-ring viper-last-replace-region)
2210 ;; Make STRING be the first element of the kill ring.
2211 (defun viper-put-string-on-kill-ring (string)
2212 (setq kill-ring (cons string kill-ring))
2213 (if (> (length kill-ring) kill-ring-max)
2214 (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))
2215 (setq kill-ring-yank-pointer kill-ring))
2217 (defun viper-finish-R-mode ()
2218 (viper-remove-hook
2219 'viper-post-command-hooks 'viper-R-state-post-command-sentinel)
2220 (viper-remove-hook
2221 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel)
2222 (viper-downgrade-to-insert))
2224 (defun viper-start-R-mode ()
2225 ;; Leave arg as 1, not t: XEmacs insists that it must be a pos number
2226 (overwrite-mode 1)
2227 (viper-add-hook
2228 'viper-post-command-hooks 'viper-R-state-post-command-sentinel t)
2229 (viper-add-hook
2230 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel t)
2231 ;; guard against a smartie who switched from R-replace to normal replace
2232 (viper-remove-hook
2233 'viper-post-command-hooks 'viper-replace-state-post-command-sentinel)
2238 (defun viper-replace-state-exit-cmd ()
2239 "Binding for keys that cause Replace state to switch to Vi or to Insert.
2240 These keys are ESC, RET, and LineFeed"
2241 (interactive)
2242 (if overwrite-mode ; if in replace mode invoked via 'R'
2243 (viper-finish-R-mode)
2244 (viper-finish-change))
2245 (let (com)
2246 (if (eq this-command 'viper-intercept-ESC-key)
2247 (setq com 'viper-exit-insert-state)
2248 (viper-set-unread-command-events last-input-char)
2249 (setq com (key-binding (read-key-sequence nil))))
2251 (condition-case conds
2252 (command-execute com)
2253 (error
2254 (viper-message-conditions conds)))
2256 (viper-hide-replace-overlay))
2259 (defun viper-replace-state-carriage-return ()
2260 "Carriage return in Viper replace state."
2261 (interactive)
2262 ;; If Emacs start supporting overlay maps, as it currently supports
2263 ;; text-property maps, we could do away with viper-replace-minor-mode and
2264 ;; just have keymap attached to replace overlay. Then the "if part" of this
2265 ;; statement can be deleted.
2266 (if (or (< (point) (viper-replace-start))
2267 (> (point) (viper-replace-end)))
2268 (let (viper-replace-minor-mode com)
2269 (viper-set-unread-command-events last-input-char)
2270 (setq com (key-binding (read-key-sequence nil)))
2271 (condition-case conds
2272 (command-execute com)
2273 (error
2274 (viper-message-conditions conds))))
2275 (if (not viper-allow-multiline-replace-regions)
2276 (viper-replace-state-exit-cmd)
2277 (if (viper-same-line (point) (viper-replace-end))
2278 (viper-replace-state-exit-cmd)
2279 ;; delete the rest of line
2280 (delete-region (point) (viper-line-pos 'end))
2281 (save-excursion
2282 (end-of-line)
2283 (if (eobp) (error "Last line in buffer")))
2284 ;; skip to the next line
2285 (forward-line 1)
2286 (back-to-indentation)
2287 ))))
2290 ;; This is the function bound to 'R'---unlimited replace.
2291 ;; Similar to Emacs's own overwrite-mode.
2292 (defun viper-overwrite (arg)
2293 "Begin overwrite mode."
2294 (interactive "P")
2295 (let ((val (viper-p-val arg))
2296 (com (viper-getcom arg)) (len))
2297 (viper-set-destructive-command (list 'viper-overwrite val ?r nil nil nil))
2298 (if com
2299 (progn
2300 ;; Viper saves inserted text in viper-last-insertion
2301 (setq len (length viper-last-insertion))
2302 (delete-char len)
2303 (viper-loop val (viper-yank-last-insertion)))
2304 (setq last-command 'viper-overwrite)
2305 (viper-set-complex-command-for-undo)
2306 (viper-set-replace-overlay (point) (viper-line-pos 'end))
2307 (viper-change-state-to-replace)
2311 ;; line commands
2313 (defun viper-line (arg)
2314 (let ((val (car arg))
2315 (com (cdr arg)))
2316 (viper-move-marker-locally 'viper-com-point (point))
2317 (if (not (eobp))
2318 (viper-next-line-carefully (1- val)))
2319 ;; this ensures that dd, cc, D, yy will do the right thing on the last
2320 ;; line of buffer when this line has no \n.
2321 (viper-add-newline-at-eob-if-necessary)
2322 (viper-execute-com 'viper-line val com))
2323 (if (and (eobp) (not (bobp))) (forward-line -1))
2326 (defun viper-yank-line (arg)
2327 "Yank ARG lines (in Vi's sense)."
2328 (interactive "P")
2329 (let ((val (viper-p-val arg)))
2330 (viper-line (cons val ?Y))))
2333 ;; region commands
2335 (defun viper-region (arg)
2336 "Execute command on a region."
2337 (interactive "P")
2338 (let ((val (viper-P-val arg))
2339 (com (viper-getcom arg)))
2340 (viper-move-marker-locally 'viper-com-point (point))
2341 (exchange-point-and-mark)
2342 (viper-execute-com 'viper-region val com)))
2344 (defun viper-Region (arg)
2345 "Execute command on a Region."
2346 (interactive "P")
2347 (let ((val (viper-P-val arg))
2348 (com (viper-getCom arg)))
2349 (viper-move-marker-locally 'viper-com-point (point))
2350 (exchange-point-and-mark)
2351 (viper-execute-com 'viper-Region val com)))
2353 (defun viper-replace-char (arg)
2354 "Replace the following ARG chars by the character read."
2355 (interactive "P")
2356 (if (and (eolp) (bolp)) (error "No character to replace here"))
2357 (let ((val (viper-p-val arg))
2358 (com (viper-getcom arg)))
2359 (viper-replace-char-subr com val)
2360 (if (and (eolp) (not (bolp))) (forward-char 1))
2361 (setq viper-this-command-keys
2362 (format "%sr" (if (integerp arg) arg "")))
2363 (viper-set-destructive-command
2364 (list 'viper-replace-char val ?r nil viper-d-char nil))
2367 (defun viper-replace-char-subr (com arg)
2368 (let (char)
2369 (setq char (if (equal com ?r)
2370 viper-d-char
2371 (read-char)))
2372 (let (inhibit-quit) ; preserve consistency of undo-list and iso-accents
2373 (if (and viper-automatic-iso-accents (memq char '(?' ?\" ?^ ?~)))
2374 ;; get European characters
2375 (progn
2376 (viper-set-iso-accents-mode t)
2377 (viper-set-unread-command-events char)
2378 (setq char (aref (read-key-sequence nil) 0))
2379 (viper-set-iso-accents-mode nil)))
2380 (viper-set-complex-command-for-undo)
2381 (if (eq char ?\C-m) (setq char ?\n))
2382 (if (and viper-special-input-method (fboundp 'quail-start-translation))
2383 ;; get Intl. characters
2384 (progn
2385 (viper-set-input-method t)
2386 (setq last-command-event
2387 (viper-copy-event
2388 (if viper-xemacs-p (character-to-event char) char)))
2389 (delete-char 1 t)
2390 (condition-case nil
2391 (if com
2392 (insert char)
2393 (if viper-emacs-p
2394 (quail-start-translation 1)
2395 (quail-start-translation)))
2396 (error))
2397 ;; quail translation failed
2398 (if (and (not (stringp quail-current-str))
2399 (not (viper-characterp quail-current-str)))
2400 (progn
2401 (viper-adjust-undo)
2402 (undo-start)
2403 (undo-more 1)
2404 (viper-set-input-method nil)
2405 (error "Composing character failed, changes undone")))
2406 ;; quail translation seems ok
2407 (or com
2408 ;;(setq char quail-current-str))
2409 (setq char (viper-char-at-pos 'backward)))
2410 (setq viper-d-char char)
2411 (viper-loop (1- (if (> arg 0) arg (- arg)))
2412 (delete-char 1 t)
2413 (insert char))
2414 (viper-set-input-method nil))
2415 (delete-char arg t)
2416 (setq viper-d-char char)
2417 (viper-loop (if (> arg 0) arg (- arg))
2418 (insert char)))
2419 (viper-adjust-undo)
2420 (backward-char arg))))
2423 ;; basic cursor movement. j, k, l, h commands.
2425 (defun viper-forward-char (arg)
2426 "Move point right ARG characters (left if ARG negative).
2427 On reaching end of line, stop and signal error."
2428 (interactive "P")
2429 (viper-leave-region-active)
2430 (let ((val (viper-p-val arg))
2431 (com (viper-getcom arg)))
2432 (if com (viper-move-marker-locally 'viper-com-point (point)))
2433 (if viper-ex-style-motion
2434 (progn
2435 ;; the boundary condition check gets weird here because
2436 ;; forward-char may be the parameter of a delete, and 'dl' works
2437 ;; just like 'x' for the last char on a line, so we have to allow
2438 ;; the forward motion before the 'viper-execute-com', but, of
2439 ;; course, 'dl' doesn't work on an empty line, so we have to
2440 ;; catch that condition before 'viper-execute-com'
2441 (if (and (eolp) (bolp)) (error "") (forward-char val))
2442 (if com (viper-execute-com 'viper-forward-char val com))
2443 (if (eolp) (progn (backward-char 1) (error ""))))
2444 (forward-char val)
2445 (if com (viper-execute-com 'viper-forward-char val com)))))
2447 (defun viper-backward-char (arg)
2448 "Move point left ARG characters (right if ARG negative).
2449 On reaching beginning of line, stop and signal error."
2450 (interactive "P")
2451 (viper-leave-region-active)
2452 (let ((val (viper-p-val arg))
2453 (com (viper-getcom arg)))
2454 (if com (viper-move-marker-locally 'viper-com-point (point)))
2455 (if viper-ex-style-motion
2456 (progn
2457 (if (bolp) (error "") (backward-char val))
2458 (if com (viper-execute-com 'viper-backward-char val com)))
2459 (backward-char val)
2460 (if com (viper-execute-com 'viper-backward-char val com)))))
2462 ;; Like forward-char, but doesn't move at end of buffer.
2463 ;; Returns distance traveled
2464 ;; (positive or 0, if arg positive; negative if arg negative).
2465 (defun viper-forward-char-carefully (&optional arg)
2466 (setq arg (or arg 1))
2467 (let ((pt (point)))
2468 (condition-case nil
2469 (forward-char arg)
2470 (error))
2471 (if (< (point) pt) ; arg was negative
2472 (- (viper-chars-in-region pt (point)))
2473 (viper-chars-in-region pt (point)))))
2475 ;; Like backward-char, but doesn't move at beg of buffer.
2476 ;; Returns distance traveled
2477 ;; (negative or 0, if arg positive; positive if arg negative).
2478 (defun viper-backward-char-carefully (&optional arg)
2479 (setq arg (or arg 1))
2480 (let ((pt (point)))
2481 (condition-case nil
2482 (backward-char arg)
2483 (error))
2484 (if (> (point) pt) ; arg was negative
2485 (viper-chars-in-region pt (point))
2486 (- (viper-chars-in-region pt (point))))))
2488 (defun viper-next-line-carefully (arg)
2489 (condition-case nil
2490 (next-line arg)
2491 (error nil)))
2495 ;;; Word command
2497 ;; Words are formed from alpha's and nonalphas - <sp>,\t\n are separators for
2498 ;; word movement. When executed with a destructive command, \n is usually left
2499 ;; untouched for the last word. Viper uses syntax table to determine what is a
2500 ;; word and what is a separator. However, \n is always a separator. Also, if
2501 ;; viper-syntax-preference is 'vi, then `_' is part of the word.
2503 ;; skip only one \n
2504 (defun viper-skip-separators (forward)
2505 (if forward
2506 (progn
2507 (viper-skip-all-separators-forward 'within-line)
2508 (if (looking-at "\n")
2509 (progn
2510 (forward-char)
2511 (viper-skip-all-separators-forward 'within-line))))
2512 (viper-skip-all-separators-backward 'within-line)
2513 (viper-backward-char-carefully)
2514 (if (looking-at "\n")
2515 (viper-skip-all-separators-backward 'within-line)
2516 (forward-char))))
2518 (defun viper-forward-word-kernel (val)
2519 (while (> val 0)
2520 (cond ((viper-looking-at-alpha)
2521 (viper-skip-alpha-forward "_")
2522 (viper-skip-separators t))
2523 ((viper-looking-at-separator)
2524 (viper-skip-separators t))
2525 ((not (viper-looking-at-alphasep))
2526 (viper-skip-nonalphasep-forward)
2527 (viper-skip-separators t)))
2528 (setq val (1- val))))
2530 ;; first skip non-newline separators backward, then skip \n. Then, if TWICE is
2531 ;; non-nil, skip non-\n back again, but don't overshoot the limit LIM.
2532 (defun viper-separator-skipback-special (twice lim)
2533 (let ((prev-char (viper-char-at-pos 'backward))
2534 (saved-point (point)))
2535 ;; skip non-newline separators backward
2536 (while (and (not (memq prev-char '(nil \n)))
2537 (< lim (point))
2538 ;; must be non-newline separator
2539 (if (eq viper-syntax-preference 'strict-vi)
2540 (memq prev-char '(?\ ?\t))
2541 (memq (char-syntax prev-char) '(?\ ?-))))
2542 (viper-backward-char-carefully)
2543 (setq prev-char (viper-char-at-pos 'backward)))
2545 (if (and (< lim (point)) (eq prev-char ?\n))
2546 (backward-char)
2547 ;; If we skipped to the next word and the prefix of this line doesn't
2548 ;; consist of separators preceded by a newline, then don't skip backwards
2549 ;; at all.
2550 (goto-char saved-point))
2551 (setq prev-char (viper-char-at-pos 'backward))
2553 ;; skip again, but make sure we don't overshoot the limit
2554 (if twice
2555 (while (and (not (memq prev-char '(nil \n)))
2556 (< lim (point))
2557 ;; must be non-newline separator
2558 (if (eq viper-syntax-preference 'strict-vi)
2559 (memq prev-char '(?\ ?\t))
2560 (memq (char-syntax prev-char) '(?\ ?-))))
2561 (viper-backward-char-carefully)
2562 (setq prev-char (viper-char-at-pos 'backward))))
2564 (if (= (point) lim)
2565 (viper-forward-char-carefully))
2569 (defun viper-forward-word (arg)
2570 "Forward word."
2571 (interactive "P")
2572 (viper-leave-region-active)
2573 (let ((val (viper-p-val arg))
2574 (com (viper-getcom arg)))
2575 (if com (viper-move-marker-locally 'viper-com-point (point)))
2576 (viper-forward-word-kernel val)
2577 (if com (progn
2578 (cond ((memq com (list ?c (- ?c)))
2579 (viper-separator-skipback-special 'twice viper-com-point))
2580 ;; Yank words including the whitespace, but not newline
2581 ((memq com (list ?y (- ?y)))
2582 (viper-separator-skipback-special nil viper-com-point))
2583 ((viper-dotable-command-p com)
2584 (viper-separator-skipback-special nil viper-com-point)))
2585 (viper-execute-com 'viper-forward-word val com)))))
2588 (defun viper-forward-Word (arg)
2589 "Forward word delimited by white characters."
2590 (interactive "P")
2591 (viper-leave-region-active)
2592 (let ((val (viper-p-val arg))
2593 (com (viper-getcom arg)))
2594 (if com (viper-move-marker-locally 'viper-com-point (point)))
2595 (viper-loop val
2596 (viper-skip-nonseparators 'forward)
2597 (viper-skip-separators t))
2598 (if com (progn
2599 (cond ((memq com (list ?c (- ?c)))
2600 (viper-separator-skipback-special 'twice viper-com-point))
2601 ;; Yank words including the whitespace, but not newline
2602 ((memq com (list ?y (- ?y)))
2603 (viper-separator-skipback-special nil viper-com-point))
2604 ((viper-dotable-command-p com)
2605 (viper-separator-skipback-special nil viper-com-point)))
2606 (viper-execute-com 'viper-forward-Word val com)))))
2609 ;; this is a bit different from Vi, but Vi's end of word
2610 ;; makes no sense whatsoever
2611 (defun viper-end-of-word-kernel ()
2612 (if (viper-end-of-word-p) (forward-char))
2613 (if (viper-looking-at-separator)
2614 (viper-skip-all-separators-forward))
2616 (cond ((viper-looking-at-alpha) (viper-skip-alpha-forward "_"))
2617 ((not (viper-looking-at-alphasep)) (viper-skip-nonalphasep-forward)))
2618 (viper-backward-char-carefully))
2620 (defun viper-end-of-word-p ()
2621 (or (eobp)
2622 (save-excursion
2623 (cond ((viper-looking-at-alpha)
2624 (forward-char)
2625 (not (viper-looking-at-alpha)))
2626 ((not (viper-looking-at-alphasep))
2627 (forward-char)
2628 (viper-looking-at-alphasep))))))
2631 (defun viper-end-of-word (arg &optional careful)
2632 "Move point to end of current word."
2633 (interactive "P")
2634 (viper-leave-region-active)
2635 (let ((val (viper-p-val arg))
2636 (com (viper-getcom arg)))
2637 (if com (viper-move-marker-locally 'viper-com-point (point)))
2638 (viper-loop val (viper-end-of-word-kernel))
2639 (if com
2640 (progn
2641 (forward-char)
2642 (viper-execute-com 'viper-end-of-word val com)))))
2644 (defun viper-end-of-Word (arg)
2645 "Forward to end of word delimited by white character."
2646 (interactive "P")
2647 (viper-leave-region-active)
2648 (let ((val (viper-p-val arg))
2649 (com (viper-getcom arg)))
2650 (if com (viper-move-marker-locally 'viper-com-point (point)))
2651 (viper-loop val
2652 (viper-end-of-word-kernel)
2653 (viper-skip-nonseparators 'forward)
2654 (backward-char))
2655 (if com
2656 (progn
2657 (forward-char)
2658 (viper-execute-com 'viper-end-of-Word val com)))))
2660 (defun viper-backward-word-kernel (val)
2661 (while (> val 0)
2662 (viper-backward-char-carefully)
2663 (cond ((viper-looking-at-alpha)
2664 (viper-skip-alpha-backward "_"))
2665 ((viper-looking-at-separator)
2666 (forward-char)
2667 (viper-skip-separators nil)
2668 (viper-backward-char-carefully)
2669 (cond ((viper-looking-at-alpha)
2670 (viper-skip-alpha-backward "_"))
2671 ((not (viper-looking-at-alphasep))
2672 (viper-skip-nonalphasep-backward))
2673 ((bobp)) ; could still be at separator, but at beg of buffer
2674 (t (forward-char))))
2675 ((not (viper-looking-at-alphasep))
2676 (viper-skip-nonalphasep-backward)))
2677 (setq val (1- val))))
2679 (defun viper-backward-word (arg)
2680 "Backward word."
2681 (interactive "P")
2682 (viper-leave-region-active)
2683 (let ((val (viper-p-val arg))
2684 (com (viper-getcom arg)))
2685 (if com
2686 (let (i)
2687 (if (setq i (save-excursion (backward-char) (looking-at "\n")))
2688 (backward-char))
2689 (viper-move-marker-locally 'viper-com-point (point))
2690 (if i (forward-char))))
2691 (viper-backward-word-kernel val)
2692 (if com (viper-execute-com 'viper-backward-word val com))))
2694 (defun viper-backward-Word (arg)
2695 "Backward word delimited by white character."
2696 (interactive "P")
2697 (viper-leave-region-active)
2698 (let ((val (viper-p-val arg))
2699 (com (viper-getcom arg)))
2700 (if com
2701 (let (i)
2702 (if (setq i (save-excursion (backward-char) (looking-at "\n")))
2703 (backward-char))
2704 (viper-move-marker-locally 'viper-com-point (point))
2705 (if i (forward-char))))
2706 (viper-loop val
2707 (viper-skip-separators nil) ; nil means backward here
2708 (viper-skip-nonseparators 'backward))
2709 (if com (viper-execute-com 'viper-backward-Word val com))))
2713 ;; line commands
2715 (defun viper-beginning-of-line (arg)
2716 "Go to beginning of line."
2717 (interactive "P")
2718 (viper-leave-region-active)
2719 (let ((val (viper-p-val arg))
2720 (com (viper-getcom arg)))
2721 (if com (viper-move-marker-locally 'viper-com-point (point)))
2722 (beginning-of-line val)
2723 (if com (viper-execute-com 'viper-beginning-of-line val com))))
2725 (defun viper-bol-and-skip-white (arg)
2726 "Beginning of line at first non-white character."
2727 (interactive "P")
2728 (viper-leave-region-active)
2729 (let ((val (viper-p-val arg))
2730 (com (viper-getcom arg)))
2731 (if com (viper-move-marker-locally 'viper-com-point (point)))
2732 (forward-to-indentation (1- val))
2733 (if com (viper-execute-com 'viper-bol-and-skip-white val com))))
2735 (defun viper-goto-eol (arg)
2736 "Go to end of line."
2737 (interactive "P")
2738 (viper-leave-region-active)
2739 (let ((val (viper-p-val arg))
2740 (com (viper-getcom arg)))
2741 (if com (viper-move-marker-locally 'viper-com-point (point)))
2742 (end-of-line val)
2743 (if com (viper-execute-com 'viper-goto-eol val com))
2744 (if viper-ex-style-motion
2745 (if (and (eolp) (not (bolp))
2746 ;; a fix for viper-change-to-eol
2747 (not (equal viper-current-state 'insert-state)))
2748 (backward-char 1)
2749 ))))
2752 (defun viper-goto-col (arg)
2753 "Go to ARG's column."
2754 (interactive "P")
2755 (viper-leave-region-active)
2756 (let ((val (viper-p-val arg))
2757 (com (viper-getcom arg))
2758 line-len)
2759 (setq line-len
2760 (viper-chars-in-region
2761 (viper-line-pos 'start) (viper-line-pos 'end)))
2762 (if com (viper-move-marker-locally 'viper-com-point (point)))
2763 (beginning-of-line)
2764 (forward-char (1- (min line-len val)))
2765 (while (> (current-column) (1- val))
2766 (backward-char 1))
2767 (if com (viper-execute-com 'viper-goto-col val com))
2768 (save-excursion
2769 (end-of-line)
2770 (if (> val (current-column)) (error "")))
2774 (defun viper-next-line (arg)
2775 "Go to next line."
2776 (interactive "P")
2777 (viper-leave-region-active)
2778 (let ((val (viper-p-val arg))
2779 (com (viper-getCom arg)))
2780 (if com (viper-move-marker-locally 'viper-com-point (point)))
2781 (next-line val)
2782 (if viper-ex-style-motion
2783 (if (and (eolp) (not (bolp))) (backward-char 1)))
2784 (setq this-command 'next-line)
2785 (if com (viper-execute-com 'viper-next-line val com))))
2787 (defun viper-next-line-at-bol (arg)
2788 "Next line at beginning of line."
2789 (interactive "P")
2790 (viper-leave-region-active)
2791 (save-excursion
2792 (end-of-line)
2793 (if (eobp) (error "Last line in buffer")))
2794 (let ((val (viper-p-val arg))
2795 (com (viper-getCom arg)))
2796 (if com (viper-move-marker-locally 'viper-com-point (point)))
2797 (forward-line val)
2798 (back-to-indentation)
2799 (if com (viper-execute-com 'viper-next-line-at-bol val com))))
2801 (defun viper-previous-line (arg)
2802 "Go to previous line."
2803 (interactive "P")
2804 (viper-leave-region-active)
2805 (let ((val (viper-p-val arg))
2806 (com (viper-getCom arg)))
2807 (if com (viper-move-marker-locally 'viper-com-point (point)))
2808 (previous-line val)
2809 (if viper-ex-style-motion
2810 (if (and (eolp) (not (bolp))) (backward-char 1)))
2811 (setq this-command 'previous-line)
2812 (if com (viper-execute-com 'viper-previous-line val com))))
2815 (defun viper-previous-line-at-bol (arg)
2816 "Previous line at beginning of line."
2817 (interactive "P")
2818 (viper-leave-region-active)
2819 (save-excursion
2820 (beginning-of-line)
2821 (if (bobp) (error "First line in buffer")))
2822 (let ((val (viper-p-val arg))
2823 (com (viper-getCom arg)))
2824 (if com (viper-move-marker-locally 'viper-com-point (point)))
2825 (forward-line (- val))
2826 (back-to-indentation)
2827 (if com (viper-execute-com 'viper-previous-line val com))))
2829 (defun viper-change-to-eol (arg)
2830 "Change to end of line."
2831 (interactive "P")
2832 (viper-goto-eol (cons arg ?c)))
2834 (defun viper-kill-line (arg)
2835 "Delete line."
2836 (interactive "P")
2837 (viper-goto-eol (cons arg ?d)))
2839 (defun viper-erase-line (arg)
2840 "Erase line."
2841 (interactive "P")
2842 (viper-beginning-of-line (cons arg ?d)))
2845 ;;; Moving around
2847 (defun viper-goto-line (arg)
2848 "Go to ARG's line. Without ARG go to end of buffer."
2849 (interactive "P")
2850 (let ((val (viper-P-val arg))
2851 (com (viper-getCom arg)))
2852 (viper-move-marker-locally 'viper-com-point (point))
2853 (viper-deactivate-mark)
2854 (push-mark nil t)
2855 (if (null val)
2856 (goto-char (point-max))
2857 (goto-char (point-min))
2858 (forward-line (1- val)))
2860 ;; positioning is done twice: before and after command execution
2861 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2862 (back-to-indentation)
2864 (if com (viper-execute-com 'viper-goto-line val com))
2866 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2867 (back-to-indentation)
2870 ;; Find ARG's occurrence of CHAR on the current line.
2871 ;; If FORWARD then search is forward, otherwise backward. OFFSET is used to
2872 ;; adjust point after search.
2873 (defun viper-find-char (arg char forward offset)
2874 (or (char-or-string-p char) (error ""))
2875 (let ((arg (if forward arg (- arg)))
2876 (cmd (if (eq viper-intermediate-command 'viper-repeat)
2877 (nth 5 viper-d-com)
2878 (viper-array-to-string (this-command-keys))))
2879 point)
2880 (save-excursion
2881 (save-restriction
2882 (if (> arg 0)
2883 (narrow-to-region
2884 ;; forward search begins here
2885 (if (eolp) (error "Command `%s': At end of line" cmd) (point))
2886 ;; forward search ends here
2887 (progn (end-of-line) (point)))
2888 (narrow-to-region
2889 ;; backward search begins from here
2890 (if (bolp)
2891 (error "Command `%s': At beginning of line" cmd) (point))
2892 ;; backward search ends here
2893 (progn (beginning-of-line) (point))))
2894 ;; if arg > 0, point is forwarded before search.
2895 (if (> arg 0) (goto-char (1+ (point-min)))
2896 (goto-char (point-max)))
2897 (if (let ((case-fold-search nil))
2898 (search-forward (char-to-string char) nil 0 arg))
2899 (setq point (point))
2900 (error "Command `%s': `%c' not found" cmd char))))
2901 (goto-char point)
2902 (if (> arg 0)
2903 (backward-char (if offset 2 1))
2904 (forward-char (if offset 1 0)))))
2906 (defun viper-find-char-forward (arg)
2907 "Find char on the line.
2908 If called interactively read the char to find from the terminal, and if
2909 called from viper-repeat, the char last used is used. This behaviour is
2910 controlled by the sign of prefix numeric value."
2911 (interactive "P")
2912 (let ((val (viper-p-val arg))
2913 (com (viper-getcom arg))
2914 (cmd-representation (nth 5 viper-d-com)))
2915 (if (> val 0)
2916 ;; this means that the function was called interactively
2917 (setq viper-f-char (read-char)
2918 viper-f-forward t
2919 viper-f-offset nil)
2920 ;; viper-repeat --- set viper-F-char from command-keys
2921 (setq viper-F-char (if (stringp cmd-representation)
2922 (viper-seq-last-elt cmd-representation)
2923 viper-F-char)
2924 viper-f-char viper-F-char)
2925 (setq val (- val)))
2926 (if com (viper-move-marker-locally 'viper-com-point (point)))
2927 (viper-find-char
2928 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) t nil)
2929 (setq val (- val))
2930 (if com
2931 (progn
2932 (setq viper-F-char viper-f-char) ; set new viper-F-char
2933 (forward-char)
2934 (viper-execute-com 'viper-find-char-forward val com)))))
2936 (defun viper-goto-char-forward (arg)
2937 "Go up to char ARG forward on line."
2938 (interactive "P")
2939 (let ((val (viper-p-val arg))
2940 (com (viper-getcom arg))
2941 (cmd-representation (nth 5 viper-d-com)))
2942 (if (> val 0)
2943 ;; this means that the function was called interactively
2944 (setq viper-f-char (read-char)
2945 viper-f-forward t
2946 viper-f-offset t)
2947 ;; viper-repeat --- set viper-F-char from command-keys
2948 (setq viper-F-char (if (stringp cmd-representation)
2949 (viper-seq-last-elt cmd-representation)
2950 viper-F-char)
2951 viper-f-char viper-F-char)
2952 (setq val (- val)))
2953 (if com (viper-move-marker-locally 'viper-com-point (point)))
2954 (viper-find-char
2955 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) t t)
2956 (setq val (- val))
2957 (if com
2958 (progn
2959 (setq viper-F-char viper-f-char) ; set new viper-F-char
2960 (forward-char)
2961 (viper-execute-com 'viper-goto-char-forward val com)))))
2963 (defun viper-find-char-backward (arg)
2964 "Find char ARG on line backward."
2965 (interactive "P")
2966 (let ((val (viper-p-val arg))
2967 (com (viper-getcom arg))
2968 (cmd-representation (nth 5 viper-d-com)))
2969 (if (> val 0)
2970 ;; this means that the function was called interactively
2971 (setq viper-f-char (read-char)
2972 viper-f-forward nil
2973 viper-f-offset nil)
2974 ;; viper-repeat --- set viper-F-char from command-keys
2975 (setq viper-F-char (if (stringp cmd-representation)
2976 (viper-seq-last-elt cmd-representation)
2977 viper-F-char)
2978 viper-f-char viper-F-char)
2979 (setq val (- val)))
2980 (if com (viper-move-marker-locally 'viper-com-point (point)))
2981 (viper-find-char
2982 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) nil nil)
2983 (setq val (- val))
2984 (if com
2985 (progn
2986 (setq viper-F-char viper-f-char) ; set new viper-F-char
2987 (viper-execute-com 'viper-find-char-backward val com)))))
2989 (defun viper-goto-char-backward (arg)
2990 "Go up to char ARG backward on line."
2991 (interactive "P")
2992 (let ((val (viper-p-val arg))
2993 (com (viper-getcom arg))
2994 (cmd-representation (nth 5 viper-d-com)))
2995 (if (> val 0)
2996 ;; this means that the function was called interactively
2997 (setq viper-f-char (read-char)
2998 viper-f-forward nil
2999 viper-f-offset t)
3000 ;; viper-repeat --- set viper-F-char from command-keys
3001 (setq viper-F-char (if (stringp cmd-representation)
3002 (viper-seq-last-elt cmd-representation)
3003 viper-F-char)
3004 viper-f-char viper-F-char)
3005 (setq val (- val)))
3006 (if com (viper-move-marker-locally 'viper-com-point (point)))
3007 (viper-find-char
3008 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) nil t)
3009 (setq val (- val))
3010 (if com
3011 (progn
3012 (setq viper-F-char viper-f-char) ; set new viper-F-char
3013 (viper-execute-com 'viper-goto-char-backward val com)))))
3015 (defun viper-repeat-find (arg)
3016 "Repeat previous find command."
3017 (interactive "P")
3018 (let ((val (viper-p-val arg))
3019 (com (viper-getcom arg)))
3020 (viper-deactivate-mark)
3021 (if com (viper-move-marker-locally 'viper-com-point (point)))
3022 (viper-find-char val viper-f-char viper-f-forward viper-f-offset)
3023 (if com
3024 (progn
3025 (if viper-f-forward (forward-char))
3026 (viper-execute-com 'viper-repeat-find val com)))))
3028 (defun viper-repeat-find-opposite (arg)
3029 "Repeat previous find command in the opposite direction."
3030 (interactive "P")
3031 (let ((val (viper-p-val arg))
3032 (com (viper-getcom arg)))
3033 (viper-deactivate-mark)
3034 (if com (viper-move-marker-locally 'viper-com-point (point)))
3035 (viper-find-char val viper-f-char (not viper-f-forward) viper-f-offset)
3036 (if com
3037 (progn
3038 (if viper-f-forward (forward-char))
3039 (viper-execute-com 'viper-repeat-find-opposite val com)))))
3042 ;; window scrolling etc.
3044 (defun viper-window-top (arg)
3045 "Go to home window line."
3046 (interactive "P")
3047 (let ((val (viper-p-val arg))
3048 (com (viper-getCom arg)))
3049 (if com (viper-move-marker-locally 'viper-com-point (point)))
3050 (push-mark nil t)
3051 (move-to-window-line (1- val))
3053 ;; positioning is done twice: before and after command execution
3054 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3055 (back-to-indentation)
3057 (if com (viper-execute-com 'viper-window-top val com))
3059 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3060 (back-to-indentation)
3063 (defun viper-window-middle (arg)
3064 "Go to middle window line."
3065 (interactive "P")
3066 (let ((val (viper-p-val arg))
3067 (com (viper-getCom arg))
3068 lines)
3069 (if com (viper-move-marker-locally 'viper-com-point (point)))
3070 (push-mark nil t)
3071 (if (not (pos-visible-in-window-p (point-max)))
3072 (move-to-window-line (+ (/ (1- (window-height)) 2) (1- val)))
3073 (setq lines (count-lines (window-start) (point-max)))
3074 (move-to-window-line (+ (/ lines 2) (1- val))))
3076 ;; positioning is done twice: before and after command execution
3077 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3078 (back-to-indentation)
3080 (if com (viper-execute-com 'viper-window-middle val com))
3082 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3083 (back-to-indentation)
3086 (defun viper-window-bottom (arg)
3087 "Go to last window line."
3088 (interactive "P")
3089 (let ((val (viper-p-val arg))
3090 (com (viper-getCom arg)))
3091 (if com (viper-move-marker-locally 'viper-com-point (point)))
3092 (push-mark nil t)
3093 (move-to-window-line (- val))
3095 ;; positioning is done twice: before and after command execution
3096 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3097 (back-to-indentation)
3099 (if com (viper-execute-com 'viper-window-bottom val com))
3101 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3102 (back-to-indentation)
3105 (defun viper-line-to-top (arg)
3106 "Put current line on the home line."
3107 (interactive "p")
3108 (recenter (1- arg)))
3110 (defun viper-line-to-middle (arg)
3111 "Put current line on the middle line."
3112 (interactive "p")
3113 (recenter (+ (1- arg) (/ (1- (window-height)) 2))))
3115 (defun viper-line-to-bottom (arg)
3116 "Put current line on the last line."
3117 (interactive "p")
3118 (recenter (- (window-height) (1+ arg))))
3120 ;; If point is within viper-search-scroll-threshold of window top or bottom,
3121 ;; scroll up or down 1/7 of window height, depending on whether we are at the
3122 ;; bottom or at the top of the window. This function is called by viper-search
3123 ;; (which is called from viper-search-forward/backward/next). If the value of
3124 ;; viper-search-scroll-threshold is negative - don't scroll.
3125 (defun viper-adjust-window ()
3126 (let ((win-height (if viper-emacs-p
3127 (1- (window-height)) ; adjust for modeline
3128 (window-displayed-height)))
3129 (pt (point))
3130 at-top-p at-bottom-p
3131 min-scroll direction)
3132 (save-excursion
3133 (move-to-window-line 0) ; top
3134 (setq at-top-p
3135 (<= (count-lines pt (point))
3136 viper-search-scroll-threshold))
3137 (move-to-window-line -1) ; bottom
3138 (setq at-bottom-p
3139 (<= (count-lines pt (point)) viper-search-scroll-threshold))
3141 (cond (at-top-p (setq min-scroll (1- viper-search-scroll-threshold)
3142 direction 1))
3143 (at-bottom-p (setq min-scroll (1+ viper-search-scroll-threshold)
3144 direction -1)))
3145 (if min-scroll
3146 (recenter
3147 (* (max min-scroll (/ win-height 7)) direction)))
3151 ;; paren match
3152 ;; must correct this to only match ( to ) etc. On the other hand
3153 ;; it is good that paren match gets confused, because that way you
3154 ;; catch _all_ imbalances.
3156 (defun viper-paren-match (arg)
3157 "Go to the matching parenthesis."
3158 (interactive "P")
3159 (viper-leave-region-active)
3160 (let ((com (viper-getcom arg))
3161 (parse-sexp-ignore-comments viper-parse-sexp-ignore-comments)
3162 anchor-point)
3163 (if (integerp arg)
3164 (if (or (> arg 99) (< arg 1))
3165 (error "Prefix must be between 1 and 99")
3166 (goto-char
3167 (if (> (point-max) 80000)
3168 (* (/ (point-max) 100) arg)
3169 (/ (* (point-max) arg) 100)))
3170 (back-to-indentation))
3171 (let (beg-lim end-lim)
3172 (if (and (eolp) (not (bolp))) (forward-char -1))
3173 (if (not (looking-at "[][(){}]"))
3174 (setq anchor-point (point)))
3175 (save-excursion
3176 (beginning-of-line)
3177 (setq beg-lim (point))
3178 (end-of-line)
3179 (setq end-lim (point)))
3180 (cond ((re-search-forward "[][(){}]" end-lim t)
3181 (backward-char) )
3182 ((re-search-backward "[][(){}]" beg-lim t))
3184 (error "No matching character on line"))))
3185 (cond ((looking-at "[\(\[{]")
3186 (if com (viper-move-marker-locally 'viper-com-point (point)))
3187 (forward-sexp 1)
3188 (if com
3189 (viper-execute-com 'viper-paren-match nil com)
3190 (backward-char)))
3191 (anchor-point
3192 (if com
3193 (progn
3194 (viper-move-marker-locally 'viper-com-point anchor-point)
3195 (forward-char 1)
3196 (viper-execute-com 'viper-paren-match nil com)
3198 ((looking-at "[])}]")
3199 (forward-char)
3200 (if com (viper-move-marker-locally 'viper-com-point (point)))
3201 (backward-sexp 1)
3202 (if com (viper-execute-com 'viper-paren-match nil com)))
3203 (t (error ""))))))
3205 (defun viper-toggle-parse-sexp-ignore-comments ()
3206 (interactive)
3207 (setq viper-parse-sexp-ignore-comments
3208 (not viper-parse-sexp-ignore-comments))
3209 (princ (format
3210 "From now on, `%%' will %signore parentheses inside comment fields"
3211 (if viper-parse-sexp-ignore-comments "" "NOT "))))
3214 ;; sentence ,paragraph and heading
3216 (defun viper-forward-sentence (arg)
3217 "Forward sentence."
3218 (interactive "P")
3219 (push-mark nil t)
3220 (let ((val (viper-p-val arg))
3221 (com (viper-getcom arg)))
3222 (if com (viper-move-marker-locally 'viper-com-point (point)))
3223 (forward-sentence val)
3224 (if com (viper-execute-com 'viper-forward-sentence nil com))))
3226 (defun viper-backward-sentence (arg)
3227 "Backward sentence."
3228 (interactive "P")
3229 (push-mark nil t)
3230 (let ((val (viper-p-val arg))
3231 (com (viper-getcom arg)))
3232 (if com (viper-move-marker-locally 'viper-com-point (point)))
3233 (backward-sentence val)
3234 (if com (viper-execute-com 'viper-backward-sentence nil com))))
3236 (defun viper-forward-paragraph (arg)
3237 "Forward paragraph."
3238 (interactive "P")
3239 (push-mark nil t)
3240 (let ((val (viper-p-val arg))
3241 (com (viper-getCom arg)))
3242 (if com (viper-move-marker-locally 'viper-com-point (point)))
3243 (forward-paragraph val)
3244 (if com
3245 (progn
3246 (backward-char 1)
3247 (viper-execute-com 'viper-forward-paragraph nil com)))))
3249 (defun viper-backward-paragraph (arg)
3250 "Backward paragraph."
3251 (interactive "P")
3252 (push-mark nil t)
3253 (let ((val (viper-p-val arg))
3254 (com (viper-getCom arg)))
3255 (if com (viper-move-marker-locally 'viper-com-point (point)))
3256 (backward-paragraph val)
3257 (if com
3258 (progn
3259 (forward-char 1)
3260 (viper-execute-com 'viper-backward-paragraph nil com)
3261 (backward-char 1)))))
3263 ;; should be mode-specific etc.
3265 (defun viper-prev-heading (arg)
3266 (interactive "P")
3267 (let ((val (viper-p-val arg))
3268 (com (viper-getCom arg)))
3269 (if com (viper-move-marker-locally 'viper-com-point (point)))
3270 (re-search-backward viper-heading-start nil t val)
3271 (goto-char (match-beginning 0))
3272 (if com (viper-execute-com 'viper-prev-heading nil com))))
3274 (defun viper-heading-end (arg)
3275 (interactive "P")
3276 (let ((val (viper-p-val arg))
3277 (com (viper-getCom arg)))
3278 (if com (viper-move-marker-locally 'viper-com-point (point)))
3279 (re-search-forward viper-heading-end nil t val)
3280 (goto-char (match-beginning 0))
3281 (if com (viper-execute-com 'viper-heading-end nil com))))
3283 (defun viper-next-heading (arg)
3284 (interactive "P")
3285 (let ((val (viper-p-val arg))
3286 (com (viper-getCom arg)))
3287 (if com (viper-move-marker-locally 'viper-com-point (point)))
3288 (end-of-line)
3289 (re-search-forward viper-heading-start nil t val)
3290 (goto-char (match-beginning 0))
3291 (if com (viper-execute-com 'viper-next-heading nil com))))
3294 ;; scrolling
3296 (defun viper-scroll-screen (arg)
3297 "Scroll to next screen."
3298 (interactive "p")
3299 (condition-case nil
3300 (if (> arg 0)
3301 (while (> arg 0)
3302 (scroll-up)
3303 (setq arg (1- arg)))
3304 (while (> 0 arg)
3305 (scroll-down)
3306 (setq arg (1+ arg))))
3307 (error (beep 1)
3308 (if (> arg 0)
3309 (progn
3310 (message "End of buffer")
3311 (goto-char (point-max)))
3312 (message "Beginning of buffer")
3313 (goto-char (point-min))))
3316 (defun viper-scroll-screen-back (arg)
3317 "Scroll to previous screen."
3318 (interactive "p")
3319 (viper-scroll-screen (- arg)))
3321 (defun viper-scroll-down (arg)
3322 "Pull down half screen."
3323 (interactive "P")
3324 (condition-case nil
3325 (if (null arg)
3326 (scroll-down (/ (window-height) 2))
3327 (scroll-down arg))
3328 (error (beep 1)
3329 (message "Beginning of buffer")
3330 (goto-char (point-min)))))
3332 (defun viper-scroll-down-one (arg)
3333 "Scroll up one line."
3334 (interactive "p")
3335 (scroll-down arg))
3337 (defun viper-scroll-up (arg)
3338 "Pull up half screen."
3339 (interactive "P")
3340 (condition-case nil
3341 (if (null arg)
3342 (scroll-up (/ (window-height) 2))
3343 (scroll-up arg))
3344 (error (beep 1)
3345 (message "End of buffer")
3346 (goto-char (point-max)))))
3348 (defun viper-scroll-up-one (arg)
3349 "Scroll down one line."
3350 (interactive "p")
3351 (scroll-up arg))
3354 ;; searching
3356 (defun viper-if-string (prompt)
3357 (if (memq viper-intermediate-command
3358 '(viper-command-argument viper-digit-argument viper-repeat))
3359 (setq viper-this-command-keys (this-command-keys)))
3360 (let ((s (viper-read-string-with-history
3361 prompt
3362 nil ; no initial
3363 'viper-search-history
3364 (car viper-search-history))))
3365 (if (not (string= s ""))
3366 (setq viper-s-string s))))
3369 (defun viper-toggle-search-style (arg)
3370 "Toggle the value of viper-case-fold-search/viper-re-search.
3371 Without prefix argument, will ask which search style to toggle. With prefix
3372 arg 1,toggles viper-case-fold-search; with arg 2 toggles viper-re-search.
3374 Although this function is bound to \\[viper-toggle-search-style], the most
3375 convenient way to use it is to bind `//' to the macro
3376 `1 M-x viper-toggle-search-style' and `///' to
3377 `2 M-x viper-toggle-search-style'. In this way, hitting `//' quickly will
3378 toggle case-fold-search and hitting `/' three times witth toggle regexp
3379 search. Macros are more convenient in this case because they don't affect
3380 the Emacs binding of `/'."
3381 (interactive "P")
3382 (let (msg)
3383 (cond ((or (eq arg 1)
3384 (and (null arg)
3385 (y-or-n-p (format "Search style: '%s'. Want '%s'? "
3386 (if viper-case-fold-search
3387 "case-insensitive" "case-sensitive")
3388 (if viper-case-fold-search
3389 "case-sensitive"
3390 "case-insensitive")))))
3391 (setq viper-case-fold-search (null viper-case-fold-search))
3392 (if viper-case-fold-search
3393 (setq msg "Search becomes case-insensitive")
3394 (setq msg "Search becomes case-sensitive")))
3395 ((or (eq arg 2)
3396 (and (null arg)
3397 (y-or-n-p (format "Search style: '%s'. Want '%s'? "
3398 (if viper-re-search
3399 "regexp-search" "vanilla-search")
3400 (if viper-re-search
3401 "vanilla-search"
3402 "regexp-search")))))
3403 (setq viper-re-search (null viper-re-search))
3404 (if viper-re-search
3405 (setq msg "Search becomes regexp-style")
3406 (setq msg "Search becomes vanilla-style")))
3408 (setq msg "Search style remains unchanged")))
3409 (princ msg t)))
3411 (defun viper-set-searchstyle-toggling-macros (unset)
3412 "Set the macros for toggling the search style in Viper's vi-state.
3413 The macro that toggles case sensitivity is bound to `//', and the one that
3414 toggles regexp search is bound to `///'.
3415 With a prefix argument, this function unsets the macros. "
3416 (interactive "P")
3417 (or noninteractive
3418 (if (not unset)
3419 (progn
3420 ;; toggle case sensitivity in search
3421 (viper-record-kbd-macro
3422 "//" 'vi-state
3423 [1 (meta x) v i p e r - t o g g l e - s e a r c h - s t y l e return]
3425 ;; toggle regexp/vanila search
3426 (viper-record-kbd-macro
3427 "///" 'vi-state
3428 [2 (meta x) v i p e r - t o g g l e - s e a r c h - s t y l e return]
3430 (if (interactive-p)
3431 (message
3432 "// and /// now toggle case-sensitivity and regexp search")))
3433 (viper-unrecord-kbd-macro "//" 'vi-state)
3434 (sit-for 2)
3435 (viper-unrecord-kbd-macro "///" 'vi-state))))
3438 (defun viper-set-parsing-style-toggling-macro (unset)
3439 "Set `%%%' to be a macro that toggles whether comment fields should be parsed for matching parentheses.
3440 This is used in conjunction with the `%' command.
3442 With a prefix argument, unsets the macro."
3443 (interactive "P")
3444 (or noninteractive
3445 (if (not unset)
3446 (progn
3447 ;; Make %%% toggle parsing comments for matching parentheses
3448 (viper-record-kbd-macro
3449 "%%%" 'vi-state
3450 [(meta x) v i p e r - t o g g l e - p a r s e - s e x p - i g n o r e - c o m m e n t s return]
3452 (if (interactive-p)
3453 (message
3454 "%%%%%% now toggles whether comments should be parsed for matching parentheses")))
3455 (viper-unrecord-kbd-macro "%%%" 'vi-state))))
3458 (defun viper-set-emacs-state-searchstyle-macros (unset &optional arg-majormode)
3459 "Set the macros for toggling the search style in Viper's emacs-state.
3460 The macro that toggles case sensitivity is bound to `//', and the one that
3461 toggles regexp search is bound to `///'.
3462 With a prefix argument, this function unsets the macros.
3463 If the optional prefix argument is non-nil and specifies a valid major mode,
3464 this sets the macros only in the macros in that major mode. Otherwise,
3465 the macros are set in the current major mode.
3466 \(When unsetting the macros, the second argument has no effect.\)"
3467 (interactive "P")
3468 (or noninteractive
3469 (if (not unset)
3470 (progn
3471 ;; toggle case sensitivity in search
3472 (viper-record-kbd-macro
3473 "//" 'emacs-state
3474 [1 (meta x) v i p e r - t o g g l e - s e a r c h - s t y l e return]
3475 (or arg-majormode major-mode))
3476 ;; toggle regexp/vanila search
3477 (viper-record-kbd-macro
3478 "///" 'emacs-state
3479 [2 (meta x) v i p e r - t o g g l e - s e a r c h - s t y l e return]
3480 (or arg-majormode major-mode))
3481 (if (interactive-p)
3482 (message
3483 "// and /// now toggle case-sensitivity and regexp search.")))
3484 (viper-unrecord-kbd-macro "//" 'emacs-state)
3485 (sit-for 2)
3486 (viper-unrecord-kbd-macro "///" 'emacs-state))))
3489 (defun viper-search-forward (arg)
3490 "Search a string forward.
3491 ARG is used to find the ARG's occurrence of the string.
3492 Null string will repeat previous search."
3493 (interactive "P")
3494 (let ((val (viper-P-val arg))
3495 (com (viper-getcom arg))
3496 (old-str viper-s-string))
3497 (setq viper-s-forward t)
3498 (viper-if-string "/")
3499 ;; this is not used at present, but may be used later
3500 (if (or (not (equal old-str viper-s-string))
3501 (not (markerp viper-local-search-start-marker))
3502 (not (marker-buffer viper-local-search-start-marker)))
3503 (setq viper-local-search-start-marker (point-marker)))
3504 (viper-search viper-s-string t val)
3505 (if com
3506 (progn
3507 (viper-move-marker-locally 'viper-com-point (mark t))
3508 (viper-execute-com 'viper-search-next val com)))))
3510 (defun viper-search-backward (arg)
3511 "Search a string backward.
3512 ARG is used to find the ARG's occurrence of the string.
3513 Null string will repeat previous search."
3514 (interactive "P")
3515 (let ((val (viper-P-val arg))
3516 (com (viper-getcom arg))
3517 (old-str viper-s-string))
3518 (setq viper-s-forward nil)
3519 (viper-if-string "?")
3520 ;; this is not used at present, but may be used later
3521 (if (or (not (equal old-str viper-s-string))
3522 (not (markerp viper-local-search-start-marker))
3523 (not (marker-buffer viper-local-search-start-marker)))
3524 (setq viper-local-search-start-marker (point-marker)))
3525 (viper-search viper-s-string nil val)
3526 (if com
3527 (progn
3528 (viper-move-marker-locally 'viper-com-point (mark t))
3529 (viper-execute-com 'viper-search-next val com)))))
3532 ;; Search for COUNT's occurrence of STRING.
3533 ;; Search is forward if FORWARD is non-nil, otherwise backward.
3534 ;; INIT-POINT is the position where search is to start.
3535 ;; Arguments:
3536 ;; (STRING FORW COUNT &optional NO-OFFSET INIT-POINT LIMIT FAIL-IF-NOT-FOUND)
3537 (defun viper-search (string forward arg
3538 &optional no-offset init-point fail-if-not-found)
3539 (if (not (equal string ""))
3540 (let ((val (viper-p-val arg))
3541 (com (viper-getcom arg))
3542 (offset (not no-offset))
3543 (case-fold-search viper-case-fold-search)
3544 (start-point (or init-point (point))))
3545 (viper-deactivate-mark)
3546 (if forward
3547 (condition-case nil
3548 (progn
3549 (if offset (viper-forward-char-carefully))
3550 (if viper-re-search
3551 (progn
3552 (re-search-forward string nil nil val)
3553 (re-search-backward string))
3554 (search-forward string nil nil val)
3555 (search-backward string))
3556 (if (not (equal start-point (point)))
3557 (push-mark start-point t)))
3558 (search-failed
3559 (if (and (not fail-if-not-found) viper-search-wrap-around-t)
3560 (progn
3561 (message "Search wrapped around BOTTOM of buffer")
3562 (goto-char (point-min))
3563 (viper-search string forward (cons 1 com) t start-point 'fail)
3564 ;; don't wait in macros
3565 (or executing-kbd-macro
3566 (memq viper-intermediate-command
3567 '(viper-repeat
3568 viper-digit-argument
3569 viper-command-argument))
3570 (sit-for 2))
3571 ;; delete the wrap-around message
3572 (message "")
3574 (goto-char start-point)
3575 (error "`%s': %s not found"
3576 string
3577 (if viper-re-search "Pattern" "String"))
3579 ;; backward
3580 (condition-case nil
3581 (progn
3582 (if viper-re-search
3583 (re-search-backward string nil nil val)
3584 (search-backward string nil nil val))
3585 (if (not (equal start-point (point)))
3586 (push-mark start-point t)))
3587 (search-failed
3588 (if (and (not fail-if-not-found) viper-search-wrap-around-t)
3589 (progn
3590 (message "Search wrapped around TOP of buffer")
3591 (goto-char (point-max))
3592 (viper-search string forward (cons 1 com) t start-point 'fail)
3593 ;; don't wait in macros
3594 (or executing-kbd-macro
3595 (memq viper-intermediate-command
3596 '(viper-repeat
3597 viper-digit-argument
3598 viper-command-argument))
3599 (sit-for 2))
3600 ;; delete the wrap-around message
3601 (message "")
3603 (goto-char start-point)
3604 (error "`%s': %s not found"
3605 string
3606 (if viper-re-search "Pattern" "String"))
3607 ))))
3608 ;; pull up or down if at top/bottom of window
3609 (viper-adjust-window)
3610 ;; highlight the result of search
3611 ;; don't wait and don't highlight in macros
3612 (or executing-kbd-macro
3613 (memq viper-intermediate-command
3614 '(viper-repeat viper-digit-argument viper-command-argument))
3615 (viper-flash-search-pattern))
3618 (defun viper-search-next (arg)
3619 "Repeat previous search."
3620 (interactive "P")
3621 (let ((val (viper-p-val arg))
3622 (com (viper-getcom arg)))
3623 (if (null viper-s-string) (error viper-NoPrevSearch))
3624 (viper-search viper-s-string viper-s-forward arg)
3625 (if com
3626 (progn
3627 (viper-move-marker-locally 'viper-com-point (mark t))
3628 (viper-execute-com 'viper-search-next val com)))))
3630 (defun viper-search-Next (arg)
3631 "Repeat previous search in the reverse direction."
3632 (interactive "P")
3633 (let ((val (viper-p-val arg))
3634 (com (viper-getcom arg)))
3635 (if (null viper-s-string) (error viper-NoPrevSearch))
3636 (viper-search viper-s-string (not viper-s-forward) arg)
3637 (if com
3638 (progn
3639 (viper-move-marker-locally 'viper-com-point (mark t))
3640 (viper-execute-com 'viper-search-Next val com)))))
3643 ;; Search contents of buffer defined by one of Viper's motion commands.
3644 ;; Repeatable via `n' and `N'.
3645 (defun viper-buffer-search-enable (&optional c)
3646 (cond (c (setq viper-buffer-search-char c))
3647 ((null viper-buffer-search-char)
3648 (setq viper-buffer-search-char ?g)))
3649 (define-key viper-vi-basic-map
3650 (char-to-string viper-buffer-search-char) 'viper-command-argument)
3651 (aset viper-exec-array viper-buffer-search-char 'viper-exec-buffer-search)
3652 (setq viper-prefix-commands
3653 (cons viper-buffer-search-char viper-prefix-commands)))
3655 ;; This is a Viper wraper for isearch-forward.
3656 (defun viper-isearch-forward (arg)
3657 "Do incremental search forward."
3658 (interactive "P")
3659 ;; emacs bug workaround
3660 (if (listp arg) (setq arg (car arg)))
3661 (viper-exec-form-in-emacs (list 'isearch-forward arg)))
3663 ;; This is a Viper wraper for isearch-backward."
3664 (defun viper-isearch-backward (arg)
3665 "Do incremental search backward."
3666 (interactive "P")
3667 ;; emacs bug workaround
3668 (if (listp arg) (setq arg (car arg)))
3669 (viper-exec-form-in-emacs (list 'isearch-backward arg)))
3672 ;; visiting and killing files, buffers
3674 (defun viper-switch-to-buffer ()
3675 "Switch to buffer in the current window."
3676 (interactive)
3677 (let (buffer)
3678 (setq buffer
3679 (read-buffer
3680 (format "Switch to buffer in this window \(%s\): "
3681 (buffer-name (other-buffer (current-buffer))))))
3682 (switch-to-buffer buffer)
3685 (defun viper-switch-to-buffer-other-window ()
3686 "Switch to buffer in another window."
3687 (interactive)
3688 (let (buffer)
3689 (setq buffer
3690 (read-buffer
3691 (format "Switch to buffer in another window \(%s\): "
3692 (buffer-name (other-buffer (current-buffer))))))
3693 (switch-to-buffer-other-window buffer)
3696 (defun viper-kill-buffer ()
3697 "Kill a buffer."
3698 (interactive)
3699 (let (buffer buffer-name)
3700 (setq buffer-name
3701 (read-buffer
3702 (format "Kill buffer \(%s\): "
3703 (buffer-name (current-buffer)))))
3704 (setq buffer
3705 (if (null buffer-name)
3706 (current-buffer)
3707 (get-buffer buffer-name)))
3708 (if (null buffer) (error "`%s': No such buffer" buffer-name))
3709 (if (or (not (buffer-modified-p buffer))
3710 (y-or-n-p
3711 (format
3712 "Buffer `%s' is modified, are you sure you want to kill it? "
3713 buffer-name)))
3714 (kill-buffer buffer)
3715 (error "Buffer not killed"))))
3719 ;; yank and pop
3721 (defsubst viper-yank (text)
3722 "Yank TEXT silently. This works correctly with Emacs's yank-pop command."
3723 (insert text)
3724 (setq this-command 'yank))
3726 (defun viper-put-back (arg)
3727 "Put back after point/below line."
3728 (interactive "P")
3729 (let ((val (viper-p-val arg))
3730 (text (if viper-use-register
3731 (cond ((viper-valid-register viper-use-register '(digit))
3732 (current-kill
3733 (- viper-use-register ?1) 'do-not-rotate))
3734 ((viper-valid-register viper-use-register)
3735 (get-register (downcase viper-use-register)))
3736 (t (error viper-InvalidRegister viper-use-register)))
3737 (current-kill 0))))
3738 (if (null text)
3739 (if viper-use-register
3740 (let ((reg viper-use-register))
3741 (setq viper-use-register nil)
3742 (error viper-EmptyRegister reg))
3743 (error "")))
3744 (setq viper-use-register nil)
3745 (if (viper-end-with-a-newline-p text)
3746 (progn
3747 (end-of-line)
3748 (if (eobp)
3749 (insert "\n")
3750 (forward-line 1))
3751 (beginning-of-line))
3752 (if (not (eolp)) (viper-forward-char-carefully)))
3753 (set-marker (viper-mark-marker) (point) (current-buffer))
3754 (viper-set-destructive-command
3755 (list 'viper-put-back val nil viper-use-register nil nil))
3756 (viper-loop val (viper-yank text)))
3757 ;; Vi puts cursor on the last char when the yanked text doesn't contain a
3758 ;; newline; it leaves the cursor at the beginning when the text contains
3759 ;; a newline
3760 (if (viper-same-line (point) (mark))
3761 (or (= (point) (mark)) (viper-backward-char-carefully))
3762 (exchange-point-and-mark)
3763 (if (bolp)
3764 (back-to-indentation)))
3765 (viper-deactivate-mark))
3767 (defun viper-Put-back (arg)
3768 "Put back at point/above line."
3769 (interactive "P")
3770 (let ((val (viper-p-val arg))
3771 (text (if viper-use-register
3772 (cond ((viper-valid-register viper-use-register '(digit))
3773 (current-kill
3774 (- viper-use-register ?1) 'do-not-rotate))
3775 ((viper-valid-register viper-use-register)
3776 (get-register (downcase viper-use-register)))
3777 (t (error viper-InvalidRegister viper-use-register)))
3778 (current-kill 0))))
3779 (if (null text)
3780 (if viper-use-register
3781 (let ((reg viper-use-register))
3782 (setq viper-use-register nil)
3783 (error viper-EmptyRegister reg))
3784 (error "")))
3785 (setq viper-use-register nil)
3786 (if (viper-end-with-a-newline-p text) (beginning-of-line))
3787 (viper-set-destructive-command
3788 (list 'viper-Put-back val nil viper-use-register nil nil))
3789 (set-marker (viper-mark-marker) (point) (current-buffer))
3790 (viper-loop val (viper-yank text)))
3791 ;; Vi puts cursor on the last char when the yanked text doesn't contain a
3792 ;; newline; it leaves the cursor at the beginning when the text contains
3793 ;; a newline
3794 (if (viper-same-line (point) (mark))
3795 (or (= (point) (mark)) (viper-backward-char-carefully))
3796 (exchange-point-and-mark)
3797 (if (bolp)
3798 (back-to-indentation)))
3799 (viper-deactivate-mark))
3802 ;; Copy region to kill-ring.
3803 ;; If BEG and END do not belong to the same buffer, copy empty region.
3804 (defun viper-copy-region-as-kill (beg end)
3805 (condition-case nil
3806 (copy-region-as-kill beg end)
3807 (error (copy-region-as-kill beg beg))))
3810 (defun viper-delete-char (arg)
3811 "Delete next character."
3812 (interactive "P")
3813 (let ((val (viper-p-val arg))
3814 end-del-pos)
3815 (viper-set-destructive-command
3816 (list 'viper-delete-char val nil nil nil nil))
3817 (if (and viper-ex-style-editing
3818 (> val (viper-chars-in-region (point) (viper-line-pos 'end))))
3819 (setq val (viper-chars-in-region (point) (viper-line-pos 'end))))
3820 (if (and viper-ex-style-motion (eolp))
3821 (if (bolp) (error "") (setq val 0))) ; not bol---simply back 1 ch
3822 (save-excursion
3823 (viper-forward-char-carefully val)
3824 (setq end-del-pos (point)))
3825 (if viper-use-register
3826 (progn
3827 (cond ((viper-valid-register viper-use-register '((Letter)))
3828 (viper-append-to-register
3829 (downcase viper-use-register) (point) end-del-pos))
3830 ((viper-valid-register viper-use-register)
3831 (copy-to-register
3832 viper-use-register (point) end-del-pos nil))
3833 (t (error viper-InvalidRegister viper-use-register)))
3834 (setq viper-use-register nil)))
3836 (delete-char val t)
3837 (if viper-ex-style-motion
3838 (if (and (eolp) (not (bolp))) (backward-char 1)))
3841 (defun viper-delete-backward-char (arg)
3842 "Delete previous character. On reaching beginning of line, stop and beep."
3843 (interactive "P")
3844 (let ((val (viper-p-val arg))
3845 end-del-pos)
3846 (viper-set-destructive-command
3847 (list 'viper-delete-backward-char val nil nil nil nil))
3848 (if (and
3849 viper-ex-style-editing
3850 (> val (viper-chars-in-region (viper-line-pos 'start) (point))))
3851 (setq val (viper-chars-in-region (viper-line-pos 'start) (point))))
3852 (save-excursion
3853 (viper-backward-char-carefully val)
3854 (setq end-del-pos (point)))
3855 (if viper-use-register
3856 (progn
3857 (cond ((viper-valid-register viper-use-register '(Letter))
3858 (viper-append-to-register
3859 (downcase viper-use-register) end-del-pos (point)))
3860 ((viper-valid-register viper-use-register)
3861 (copy-to-register
3862 viper-use-register end-del-pos (point) nil))
3863 (t (error viper-InvalidRegister viper-use-register)))
3864 (setq viper-use-register nil)))
3865 (if (and (bolp) viper-ex-style-editing)
3866 (ding))
3867 (delete-backward-char val t)))
3869 (defun viper-del-backward-char-in-insert ()
3870 "Delete 1 char backwards while in insert mode."
3871 (interactive)
3872 (if (and viper-ex-style-editing (bolp))
3873 (beep 1)
3874 (delete-backward-char 1 t)))
3876 (defun viper-del-backward-char-in-replace ()
3877 "Delete one character in replace mode.
3878 If `viper-delete-backwards-in-replace' is t, then DEL key actually deletes
3879 charecters. If it is nil, then the cursor just moves backwards, similarly
3880 to Vi. The variable `viper-ex-style-editing', if t, doesn't let the
3881 cursor move past the beginning of line."
3882 (interactive)
3883 (cond (viper-delete-backwards-in-replace
3884 (cond ((not (bolp))
3885 (delete-backward-char 1 t))
3886 (viper-ex-style-editing
3887 (beep 1))
3888 ((bobp)
3889 (beep 1))
3891 (delete-backward-char 1 t))))
3892 (viper-ex-style-editing
3893 (if (bolp)
3894 (beep 1)
3895 (backward-char 1)))
3897 (backward-char 1))))
3901 ;; join lines.
3903 (defun viper-join-lines (arg)
3904 "Join this line to next, if ARG is nil. Otherwise, join ARG lines."
3905 (interactive "*P")
3906 (let ((val (viper-P-val arg)))
3907 (viper-set-destructive-command
3908 (list 'viper-join-lines val nil nil nil nil))
3909 (viper-loop (if (null val) 1 (1- val))
3910 (end-of-line)
3911 (if (not (eobp))
3912 (progn
3913 (forward-line 1)
3914 (delete-region (point) (1- (point)))
3915 (fixup-whitespace)
3916 ;; fixup-whitespace sometimes does not leave space
3917 ;; between objects, so we insert it as in Vi
3918 (or (looking-at " ")
3919 (insert " ")
3920 (backward-char 1))
3921 )))))
3924 ;; Replace state
3926 (defun viper-change (beg end)
3927 (if (markerp beg) (setq beg (marker-position beg)))
3928 (if (markerp end) (setq end (marker-position end)))
3929 ;; beg is sometimes (mark t), which may be nil
3930 (or beg (setq beg end))
3932 (viper-set-complex-command-for-undo)
3933 (if viper-use-register
3934 (progn
3935 (copy-to-register viper-use-register beg end nil)
3936 (setq viper-use-register nil)))
3937 (viper-set-replace-overlay beg end)
3938 (setq last-command nil) ; separate repl text from prev kills
3940 (if (= (viper-replace-start) (point-max))
3941 (error "End of buffer"))
3943 (setq viper-last-replace-region
3944 (buffer-substring (viper-replace-start)
3945 (viper-replace-end)))
3947 ;; protect against error while inserting "@" and other disasters
3948 ;; (e.g., read-only buff)
3949 (condition-case conds
3950 (if (or viper-allow-multiline-replace-regions
3951 (viper-same-line (viper-replace-start)
3952 (viper-replace-end)))
3953 (progn
3954 ;; tabs cause problems in replace, so untabify
3955 (goto-char (viper-replace-end))
3956 (insert-before-markers "@") ; put placeholder after the TAB
3957 (untabify (viper-replace-start) (point))
3958 ;; del @, don't put on kill ring
3959 (delete-backward-char 1)
3961 (viper-set-replace-overlay-glyphs
3962 viper-replace-region-start-delimiter
3963 viper-replace-region-end-delimiter)
3964 ;; this move takes care of the last posn in the overlay, which
3965 ;; has to be shifted because of insert. We can't simply insert
3966 ;; "$" before-markers because then overlay-start will shift the
3967 ;; beginning of the overlay in case we are replacing a single
3968 ;; character. This fixes the bug with `s' and `cl' commands.
3969 (viper-move-replace-overlay (viper-replace-start) (point))
3970 (goto-char (viper-replace-start))
3971 (viper-change-state-to-replace t))
3972 (kill-region (viper-replace-start)
3973 (viper-replace-end))
3974 (viper-hide-replace-overlay)
3975 (viper-change-state-to-insert))
3976 (error ;; make sure that the overlay doesn't stay.
3977 ;; go back to the original point
3978 (goto-char (viper-replace-start))
3979 (viper-hide-replace-overlay)
3980 (viper-message-conditions conds))))
3983 (defun viper-change-subr (beg end)
3984 ;; beg is sometimes (mark t), which may be nil
3985 (or beg (setq beg end))
3986 (if viper-use-register
3987 (progn
3988 (copy-to-register viper-use-register beg end nil)
3989 (setq viper-use-register nil)))
3990 (kill-region beg end)
3991 (setq this-command 'viper-change)
3992 (viper-yank-last-insertion))
3994 (defun viper-toggle-case (arg)
3995 "Toggle character case."
3996 (interactive "P")
3997 (let ((val (viper-p-val arg)) (c))
3998 (viper-set-destructive-command
3999 (list 'viper-toggle-case val nil nil nil nil))
4000 (while (> val 0)
4001 (setq c (following-char))
4002 (delete-char 1 nil)
4003 (if (eq c (upcase c))
4004 (insert-char (downcase c) 1)
4005 (insert-char (upcase c) 1))
4006 (if (eolp) (backward-char 1))
4007 (setq val (1- val)))))
4010 ;; query replace
4012 (defun viper-query-replace ()
4013 "Query replace.
4014 If a null string is suplied as the string to be replaced,
4015 the query replace mode will toggle between string replace
4016 and regexp replace."
4017 (interactive)
4018 (let (str)
4019 (setq str (viper-read-string-with-history
4020 (if viper-re-query-replace "Query replace regexp: "
4021 "Query replace: ")
4022 nil ; no initial
4023 'viper-replace1-history
4024 (car viper-replace1-history) ; default
4026 (if (string= str "")
4027 (progn
4028 (setq viper-re-query-replace (not viper-re-query-replace))
4029 (message "Query replace mode changed to %s"
4030 (if viper-re-query-replace "regexp replace"
4031 "string replace")))
4032 (if viper-re-query-replace
4033 (query-replace-regexp
4035 (viper-read-string-with-history
4036 (format "Query replace regexp `%s' with: " str)
4037 nil ; no initial
4038 'viper-replace1-history
4039 (car viper-replace1-history) ; default
4041 (query-replace
4043 (viper-read-string-with-history
4044 (format "Query replace `%s' with: " str)
4045 nil ; no initial
4046 'viper-replace1-history
4047 (car viper-replace1-history) ; default
4048 ))))))
4051 ;; marking
4053 (defun viper-mark-beginning-of-buffer ()
4054 "Mark beginning of buffer."
4055 (interactive)
4056 (push-mark (point))
4057 (goto-char (point-min))
4058 (exchange-point-and-mark)
4059 (message "Mark set at the beginning of buffer"))
4061 (defun viper-mark-end-of-buffer ()
4062 "Mark end of buffer."
4063 (interactive)
4064 (push-mark (point))
4065 (goto-char (point-max))
4066 (exchange-point-and-mark)
4067 (message "Mark set at the end of buffer"))
4069 (defun viper-mark-point ()
4070 "Set mark at point of buffer."
4071 (interactive)
4072 (let ((char (read-char)))
4073 (cond ((and (<= ?a char) (<= char ?z))
4074 (point-to-register (1+ (- char ?a))))
4075 ((= char ?<) (viper-mark-beginning-of-buffer))
4076 ((= char ?>) (viper-mark-end-of-buffer))
4077 ((= char ?.) (viper-set-mark-if-necessary))
4078 ((= char ?,) (viper-cycle-through-mark-ring))
4079 ((= char ?D) (mark-defun))
4080 (t (error ""))
4083 ;; Algorithm: If first invocation of this command save mark on ring, goto
4084 ;; mark, M0, and pop the most recent elt from the mark ring into mark,
4085 ;; making it into the new mark, M1.
4086 ;; Push this mark back and set mark to the original point position, p1.
4087 ;; So, if you hit '' or `` then you can return to p1.
4089 ;; If repeated command, pop top elt from the ring into mark and
4090 ;; jump there. This forgets the position, p1, and puts M1 back into mark.
4091 ;; Then we save the current pos, which is M0, jump to M1 and pop M2 from
4092 ;; the ring into mark. Push M2 back on the ring and set mark to M0.
4093 ;; etc.
4094 (defun viper-cycle-through-mark-ring ()
4095 "Visit previous locations on the mark ring.
4096 One can use `` and '' to temporarily jump 1 step back."
4097 (let* ((sv-pt (point)))
4098 ;; if repeated `m,' command, pop the previously saved mark.
4099 ;; Prev saved mark is actually prev saved point. It is used if the
4100 ;; user types `` or '' and is discarded
4101 ;; from the mark ring by the next `m,' command.
4102 ;; In any case, go to the previous or previously saved mark.
4103 ;; Then push the current mark (popped off the ring) and set current
4104 ;; point to be the mark. Current pt as mark is discarded by the next
4105 ;; m, command.
4106 (if (eq last-command 'viper-cycle-through-mark-ring)
4108 ;; save current mark if the first iteration
4109 (setq mark-ring (delete (viper-mark-marker) mark-ring))
4110 (if (mark t)
4111 (push-mark (mark t) t)) )
4112 (pop-mark)
4113 (set-mark-command 1)
4114 ;; don't duplicate mark on the ring
4115 (setq mark-ring (delete (viper-mark-marker) mark-ring))
4116 (push-mark sv-pt t)
4117 (viper-deactivate-mark)
4118 (setq this-command 'viper-cycle-through-mark-ring)
4122 (defun viper-goto-mark (arg)
4123 "Go to mark."
4124 (interactive "P")
4125 (let ((char (read-char))
4126 (com (viper-getcom arg)))
4127 (viper-goto-mark-subr char com nil)))
4129 (defun viper-goto-mark-and-skip-white (arg)
4130 "Go to mark and skip to first non-white character on line."
4131 (interactive "P")
4132 (let ((char (read-char))
4133 (com (viper-getCom arg)))
4134 (viper-goto-mark-subr char com t)))
4136 (defun viper-goto-mark-subr (char com skip-white)
4137 (if (eobp)
4138 (if (bobp)
4139 (error "Empty buffer")
4140 (backward-char 1)))
4141 (cond ((viper-valid-register char '(letter))
4142 (let* ((buff (current-buffer))
4143 (reg (1+ (- char ?a)))
4144 (text-marker (get-register reg)))
4145 (if com (viper-move-marker-locally 'viper-com-point (point)))
4146 (if (not (viper-valid-marker text-marker))
4147 (error viper-EmptyTextmarker char))
4148 (if (and (viper-same-line (point) viper-last-jump)
4149 (= (point) viper-last-jump-ignore))
4150 (push-mark viper-last-jump t)
4151 (push-mark nil t)) ; no msg
4152 (viper-register-to-point reg)
4153 (setq viper-last-jump (point-marker))
4154 (cond (skip-white
4155 (back-to-indentation)
4156 (setq viper-last-jump-ignore (point))))
4157 (if com
4158 (if (equal buff (current-buffer))
4159 (viper-execute-com (if skip-white
4160 'viper-goto-mark-and-skip-white
4161 'viper-goto-mark)
4162 nil com)
4163 (switch-to-buffer buff)
4164 (goto-char viper-com-point)
4165 (viper-change-state-to-vi)
4166 (error "")))))
4167 ((and (not skip-white) (= char ?`))
4168 (if com (viper-move-marker-locally 'viper-com-point (point)))
4169 (if (and (viper-same-line (point) viper-last-jump)
4170 (= (point) viper-last-jump-ignore))
4171 (goto-char viper-last-jump))
4172 (if (null (mark t)) (error "Mark is not set in this buffer"))
4173 (if (= (point) (mark t)) (pop-mark))
4174 (exchange-point-and-mark)
4175 (setq viper-last-jump (point-marker)
4176 viper-last-jump-ignore 0)
4177 (if com (viper-execute-com 'viper-goto-mark nil com)))
4178 ((and skip-white (= char ?'))
4179 (if com (viper-move-marker-locally 'viper-com-point (point)))
4180 (if (and (viper-same-line (point) viper-last-jump)
4181 (= (point) viper-last-jump-ignore))
4182 (goto-char viper-last-jump))
4183 (if (= (point) (mark t)) (pop-mark))
4184 (exchange-point-and-mark)
4185 (setq viper-last-jump (point))
4186 (back-to-indentation)
4187 (setq viper-last-jump-ignore (point))
4188 (if com (viper-execute-com 'viper-goto-mark-and-skip-white nil com)))
4189 (t (error viper-InvalidTextmarker char))))
4191 (defun viper-insert-tab ()
4192 (interactive)
4193 (insert-tab))
4195 (defun viper-exchange-point-and-mark ()
4196 (interactive)
4197 (exchange-point-and-mark)
4198 (back-to-indentation))
4200 ;; Input Mode Indentation
4202 ;; Returns t, if the string before point matches the regexp STR.
4203 (defsubst viper-looking-back (str)
4204 (and (save-excursion (re-search-backward str nil t))
4205 (= (point) (match-end 0))))
4208 (defun viper-forward-indent ()
4209 "Indent forward -- `C-t' in Vi."
4210 (interactive)
4211 (setq viper-cted t)
4212 (indent-to (+ (current-column) viper-shift-width)))
4214 (defun viper-backward-indent ()
4215 "Backtab, C-d in VI"
4216 (interactive)
4217 (if viper-cted
4218 (let ((p (point)) (c (current-column)) bol (indent t))
4219 (if (viper-looking-back "[0^]")
4220 (progn
4221 (if (eq ?^ (preceding-char))
4222 (setq viper-preserve-indent t))
4223 (delete-backward-char 1)
4224 (setq p (point))
4225 (setq indent nil)))
4226 (save-excursion
4227 (beginning-of-line)
4228 (setq bol (point)))
4229 (if (re-search-backward "[^ \t]" bol 1) (forward-char))
4230 (delete-region (point) p)
4231 (if indent
4232 (indent-to (- c viper-shift-width)))
4233 (if (or (bolp) (viper-looking-back "[^ \t]"))
4234 (setq viper-cted nil)))))
4236 (defun viper-autoindent ()
4237 "Auto Indentation, Vi-style."
4238 (interactive)
4239 (let ((col (current-indentation)))
4240 (if abbrev-mode (expand-abbrev))
4241 (if viper-preserve-indent
4242 (setq viper-preserve-indent nil)
4243 (setq viper-current-indent col))
4244 ;; don't leave whitespace lines around
4245 (if (memq last-command
4246 '(viper-autoindent
4247 viper-open-line viper-Open-line
4248 viper-replace-state-exit-cmd))
4249 (indent-to-left-margin))
4250 ;; use \n instead of newline, or else <Return> will move the insert point
4251 ;;(newline 1)
4252 (insert "\n")
4253 (if viper-auto-indent
4254 (progn
4255 (setq viper-cted t)
4256 (if (and viper-electric-mode
4257 (not
4258 (memq major-mode '(fundamental-mode
4259 text-mode
4260 paragraph-indent-text-mode ))))
4261 (indent-according-to-mode)
4262 (indent-to viper-current-indent))
4267 ;; Viewing registers
4269 (defun viper-ket-function (arg)
4270 "Function called by \], the ket. View registers and call \]\]."
4271 (interactive "P")
4272 (let ((reg (read-char)))
4273 (cond ((viper-valid-register reg '(letter Letter))
4274 (view-register (downcase reg)))
4275 ((viper-valid-register reg '(digit))
4276 (let ((text (current-kill (- reg ?1) 'do-not-rotate)))
4277 (save-excursion
4278 (set-buffer (get-buffer-create "*Output*"))
4279 (delete-region (point-min) (point-max))
4280 (insert (format "Register %c contains the string:\n" reg))
4281 (insert text)
4282 (goto-char (point-min)))
4283 (display-buffer "*Output*")))
4284 ((= ?\] reg)
4285 (viper-next-heading arg))
4286 (t (error
4287 viper-InvalidRegister reg)))))
4289 (defun viper-brac-function (arg)
4290 "Function called by \[, the brac. View textmarkers and call \[\["
4291 (interactive "P")
4292 (let ((reg (read-char)))
4293 (cond ((= ?\[ reg)
4294 (viper-prev-heading arg))
4295 ((= ?\] reg)
4296 (viper-heading-end arg))
4297 ((viper-valid-register reg '(letter))
4298 (let* ((val (get-register (1+ (- reg ?a))))
4299 (buf (if (not val)
4300 (error viper-EmptyTextmarker reg)
4301 (marker-buffer val)))
4302 (pos (marker-position val))
4303 line-no text (s pos) (e pos))
4304 (save-excursion
4305 (set-buffer (get-buffer-create "*Output*"))
4306 (delete-region (point-min) (point-max))
4307 (if (and buf pos)
4308 (progn
4309 (save-excursion
4310 (set-buffer buf)
4311 (setq line-no (1+ (count-lines (point-min) val)))
4312 (goto-char pos)
4313 (beginning-of-line)
4314 (if (re-search-backward "[^ \t]" nil t)
4315 (progn
4316 (beginning-of-line)
4317 (setq s (point))))
4318 (goto-char pos)
4319 (forward-line 1)
4320 (if (re-search-forward "[^ \t]" nil t)
4321 (progn
4322 (end-of-line)
4323 (setq e (point))))
4324 (setq text (buffer-substring s e))
4325 (setq text (format "%s<%c>%s"
4326 (substring text 0 (- pos s))
4327 reg (substring text (- pos s)))))
4328 (insert
4329 (format
4330 "Textmarker `%c' is in buffer `%s' at line %d.\n"
4331 reg (buffer-name buf) line-no))
4332 (insert (format "Here is some text around %c:\n\n %s"
4333 reg text)))
4334 (insert (format viper-EmptyTextmarker reg)))
4335 (goto-char (point-min)))
4336 (display-buffer "*Output*")))
4337 (t (error viper-InvalidTextmarker reg)))))
4341 ;; commands in insertion mode
4343 (defun viper-delete-backward-word (arg)
4344 "Delete previous word."
4345 (interactive "p")
4346 (save-excursion
4347 (push-mark nil t)
4348 (backward-word arg)
4349 (delete-region (point) (mark t))
4350 (pop-mark)))
4353 (defun viper-set-expert-level (&optional dont-change-unless)
4354 "Sets the expert level for a Viper user.
4355 Can be called interactively to change (temporarily or permanently) the
4356 current expert level.
4358 The optional argument DONT-CHANGE-UNLESS, if not nil, says that
4359 the level should not be changed, unless its current value is
4360 meaningless (i.e., not one of 1,2,3,4,5).
4362 User level determines the setting of Viper variables that are most
4363 sensitive for VI-style look-and-feel."
4365 (interactive)
4367 (if (not (natnump viper-expert-level)) (setq viper-expert-level 0))
4369 (save-window-excursion
4370 (delete-other-windows)
4371 ;; if 0 < viper-expert-level < viper-max-expert-level
4372 ;; & dont-change-unless = t -- use it; else ask
4373 (viper-ask-level dont-change-unless))
4375 (setq viper-always t
4376 viper-ex-style-motion t
4377 viper-ex-style-editing t
4378 viper-want-ctl-h-help nil)
4380 (cond ((eq viper-expert-level 1) ; novice or beginner
4381 (global-set-key ; in emacs-state
4382 viper-toggle-key
4383 (if (viper-window-display-p) 'viper-iconify 'suspend-emacs))
4384 (setq viper-no-multiple-ESC t
4385 viper-re-search t
4386 viper-vi-style-in-minibuffer t
4387 viper-search-wrap-around-t t
4388 viper-electric-mode nil
4389 viper-want-emacs-keys-in-vi nil
4390 viper-want-emacs-keys-in-insert nil))
4392 ((and (> viper-expert-level 1) (< viper-expert-level 5))
4393 ;; intermediate to guru
4394 (setq viper-no-multiple-ESC (if (viper-window-display-p)
4395 t 'twice)
4396 viper-electric-mode t
4397 viper-want-emacs-keys-in-vi t
4398 viper-want-emacs-keys-in-insert (> viper-expert-level 2))
4400 (if (eq viper-expert-level 4) ; respect user's ex-style motion
4401 ; and viper-no-multiple-ESC
4402 (progn
4403 (setq-default
4404 viper-ex-style-editing
4405 (viper-standard-value 'viper-ex-style-editing)
4406 viper-ex-style-motion
4407 (viper-standard-value 'viper-ex-style-motion))
4408 (setq viper-ex-style-motion
4409 (viper-standard-value 'viper-ex-style-motion)
4410 viper-ex-style-editing
4411 (viper-standard-value 'viper-ex-style-editing)
4412 viper-re-search
4413 (viper-standard-value 'viper-re-search)
4414 viper-no-multiple-ESC
4415 (viper-standard-value 'viper-no-multiple-ESC)))))
4417 ;; A wizard!!
4418 ;; Ideally, if 5 is selected, a buffer should pop up to let the
4419 ;; user toggle the values of variables.
4420 (t (setq-default viper-ex-style-editing
4421 (viper-standard-value 'viper-ex-style-editing)
4422 viper-ex-style-motion
4423 (viper-standard-value 'viper-ex-style-motion))
4424 (setq viper-want-ctl-h-help
4425 (viper-standard-value 'viper-want-ctl-h-help)
4426 viper-always
4427 (viper-standard-value 'viper-always)
4428 viper-no-multiple-ESC
4429 (viper-standard-value 'viper-no-multiple-ESC)
4430 viper-ex-style-motion
4431 (viper-standard-value 'viper-ex-style-motion)
4432 viper-ex-style-editing
4433 (viper-standard-value 'viper-ex-style-editing)
4434 viper-re-search
4435 (viper-standard-value 'viper-re-search)
4436 viper-electric-mode
4437 (viper-standard-value 'viper-electric-mode)
4438 viper-want-emacs-keys-in-vi
4439 (viper-standard-value 'viper-want-emacs-keys-in-vi)
4440 viper-want-emacs-keys-in-insert
4441 (viper-standard-value 'viper-want-emacs-keys-in-insert))))
4443 (viper-set-mode-vars-for viper-current-state)
4444 (if (or viper-always
4445 (and (> viper-expert-level 0) (> 5 viper-expert-level)))
4446 (viper-set-hooks)))
4448 ;; Ask user expert level.
4449 (defun viper-ask-level (dont-change-unless)
4450 (let ((ask-buffer " *viper-ask-level*")
4451 level-changed repeated)
4452 (save-window-excursion
4453 (switch-to-buffer ask-buffer)
4455 (while (or (> viper-expert-level viper-max-expert-level)
4456 (< viper-expert-level 1)
4457 (null dont-change-unless))
4458 (erase-buffer)
4459 (if repeated
4460 (progn
4461 (message "Invalid user level")
4462 (beep 1))
4463 (setq repeated t))
4464 (setq dont-change-unless t
4465 level-changed t)
4466 (insert "
4467 Please specify your level of familiarity with the venomous VI PERil
4468 (and the VI Plan for Emacs Rescue).
4469 You can change it at any time by typing `M-x viper-set-expert-level RET'
4471 1 -- BEGINNER: Almost all Emacs features are suppressed.
4472 Feels almost like straight Vi. File name completion and
4473 command history in the minibuffer are thrown in as a bonus.
4474 To use Emacs productively, you must reach level 3 or higher.
4475 2 -- MASTER: C-c now has its standard Emacs meaning in Vi command state,
4476 so most Emacs commands can be used when Viper is in Vi state.
4477 Good progress---you are well on the way to level 3!
4478 3 -- GRAND MASTER: Like 3, but most Emacs commands are available also
4479 in Viper's insert state.
4480 4 -- GURU: Like 3, but user settings are respected for viper-no-multiple-ESC,
4481 viper-ex-style-motion, viper-ex-style-editing, and
4482 viper-re-search variables. Adjust these settings to your taste.
4483 5 -- WIZARD: Like 4, but user settings are also respected for viper-always,
4484 viper-electric-mode, viper-want-ctl-h-help, viper-want-emacs-keys-in-vi,
4485 and viper-want-emacs-keys-in-insert. Adjust these to your taste.
4487 Please, specify your level now: ")
4489 (setq viper-expert-level (- (viper-read-char-exclusive) ?0))
4490 ) ; end while
4492 ;; tell the user if level was changed
4493 (and level-changed
4494 (progn
4495 (insert
4496 (format "\n\n\n\n\n\t\tYou have selected user level %d"
4497 viper-expert-level))
4498 (if (y-or-n-p "Do you wish to make this change permanent? ")
4499 ;; save the setting for viper-expert-level
4500 (viper-save-setting
4501 'viper-expert-level
4502 (format "Saving user level %d ..." viper-expert-level)
4503 viper-custom-file-name))
4505 (bury-buffer) ; remove ask-buffer from screen
4506 (message "")
4510 (defun viper-nil ()
4511 (interactive)
4512 (beep 1))
4515 ;; if ENFORCE-BUFFER is not nil, error if CHAR is a marker in another buffer
4516 (defun viper-register-to-point (char &optional enforce-buffer)
4517 "Like jump-to-register, but switches to another buffer in another window."
4518 (interactive "cViper register to point: ")
4519 (let ((val (get-register char)))
4520 (cond
4521 ((and (fboundp 'frame-configuration-p)
4522 (frame-configuration-p val))
4523 (set-frame-configuration val))
4524 ((window-configuration-p val)
4525 (set-window-configuration val))
4526 ((viper-valid-marker val)
4527 (if (and enforce-buffer
4528 (not (equal (current-buffer) (marker-buffer val))))
4529 (error (concat viper-EmptyTextmarker " in this buffer")
4530 (1- (+ char ?a))))
4531 (pop-to-buffer (marker-buffer val))
4532 (goto-char val))
4533 ((and (consp val) (eq (car val) 'file))
4534 (find-file (cdr val)))
4536 (error viper-EmptyTextmarker (1- (+ char ?a)))))))
4539 (defun viper-save-kill-buffer ()
4540 "Save then kill current buffer. "
4541 (interactive)
4542 (if (< viper-expert-level 2)
4543 (save-buffers-kill-emacs)
4544 (save-buffer)
4545 (kill-buffer (current-buffer))))
4549 ;;; Bug Report
4551 (defun viper-submit-report ()
4552 "Submit bug report on Viper."
4553 (interactive)
4554 (let ((reporter-prompt-for-summary-p t)
4555 (viper-device-type (viper-device-type))
4556 color-display-p frame-parameters
4557 minibuffer-emacs-face minibuffer-vi-face minibuffer-insert-face
4558 varlist salutation window-config)
4560 ;; If mode info is needed, add variable to `let' and then set it below,
4561 ;; like we did with color-display-p.
4562 (setq color-display-p (if (viper-window-display-p)
4563 (viper-color-display-p)
4564 'non-x)
4565 minibuffer-vi-face (if (viper-has-face-support-p)
4566 (viper-get-face viper-minibuffer-vi-face)
4567 'non-x)
4568 minibuffer-insert-face (if (viper-has-face-support-p)
4569 (viper-get-face
4570 viper-minibuffer-insert-face)
4571 'non-x)
4572 minibuffer-emacs-face (if (viper-has-face-support-p)
4573 (viper-get-face
4574 viper-minibuffer-emacs-face)
4575 'non-x)
4576 frame-parameters (if (fboundp 'frame-parameters)
4577 (frame-parameters (selected-frame))))
4579 (setq varlist (list 'viper-vi-minibuffer-minor-mode
4580 'viper-insert-minibuffer-minor-mode
4581 'viper-vi-intercept-minor-mode
4582 'viper-vi-local-user-minor-mode
4583 'viper-vi-kbd-minor-mode
4584 'viper-vi-global-user-minor-mode
4585 'viper-vi-state-modifier-minor-mode
4586 'viper-vi-diehard-minor-mode
4587 'viper-vi-basic-minor-mode
4588 'viper-replace-minor-mode
4589 'viper-insert-intercept-minor-mode
4590 'viper-insert-local-user-minor-mode
4591 'viper-insert-kbd-minor-mode
4592 'viper-insert-global-user-minor-mode
4593 'viper-insert-state-modifier-minor-mode
4594 'viper-insert-diehard-minor-mode
4595 'viper-insert-basic-minor-mode
4596 'viper-emacs-intercept-minor-mode
4597 'viper-emacs-local-user-minor-mode
4598 'viper-emacs-kbd-minor-mode
4599 'viper-emacs-global-user-minor-mode
4600 'viper-emacs-state-modifier-minor-mode
4601 'viper-automatic-iso-accents
4602 'viper-special-input-method
4603 'viper-want-emacs-keys-in-insert
4604 'viper-want-emacs-keys-in-vi
4605 'viper-keep-point-on-undo
4606 'viper-no-multiple-ESC
4607 'viper-electric-mode
4608 'viper-ESC-key
4609 'viper-want-ctl-h-help
4610 'viper-ex-style-editing
4611 'viper-delete-backwards-in-replace
4612 'viper-vi-style-in-minibuffer
4613 'viper-vi-state-hook
4614 'viper-insert-state-hook
4615 'viper-replace-state-hook
4616 'viper-emacs-state-hook
4617 'ex-cycle-other-window
4618 'ex-cycle-through-non-files
4619 'viper-expert-level
4620 'major-mode
4621 'viper-device-type
4622 'color-display-p
4623 'frame-parameters
4624 'minibuffer-vi-face
4625 'minibuffer-insert-face
4626 'minibuffer-emacs-face
4628 (setq salutation "
4629 Congratulations! You may have unearthed a bug in Viper!
4630 Please mail a concise, accurate summary of the problem to the address above.
4632 -------------------------------------------------------------------")
4633 (setq window-config (current-window-configuration))
4634 (with-output-to-temp-buffer " *viper-info*"
4635 (switch-to-buffer " *viper-info*")
4636 (delete-other-windows)
4637 (princ "
4638 PLEASE FOLLOW THESE PROCEDURES
4639 ------------------------------
4641 Before reporting a bug, please verify that it is related to Viper, and is
4642 not cause by other packages you are using.
4644 Don't report compilation warnings, unless you are certain that there is a
4645 problem. These warnings are normal and unavoidable.
4647 Please note that users should not modify variables and keymaps other than
4648 those advertised in the manual. Such `customization' is likely to crash
4649 Viper, as it would any other improperly customized Emacs package.
4651 If you are reporting an error message received while executing one of the
4652 Viper commands, type:
4654 M-x set-variable <Return> debug-on-error <Return> t <Return>
4656 Then reproduce the error. The above command will cause Emacs to produce a
4657 back trace of the execution that leads to the error. Please include this
4658 trace in your bug report.
4660 If you believe that one of Viper's commands goes into an infinite loop
4661 \(e.g., Emacs freezes\), type:
4663 M-x set-variable <Return> debug-on-quit <Return> t <Return>
4665 Then reproduce the problem. Wait for a few seconds, then type C-g to abort
4666 the current command. Include the resulting back trace in the bug report.
4668 Mail anyway (y or n)? ")
4669 (if (y-or-n-p "Mail anyway? ")
4671 (set-window-configuration window-config)
4672 (error "Bug report aborted")))
4674 (require 'reporter)
4675 (set-window-configuration window-config)
4677 (reporter-submit-bug-report "kifer@cs.sunysb.edu"
4678 (viper-version)
4679 varlist
4680 nil 'delete-other-windows
4681 salutation)
4687 ;; Smoothes out the difference between Emacs' unread-command-events
4688 ;; and XEmacs unread-command-event. Arg is a character, an event, a list of
4689 ;; events or a sequence of keys.
4691 ;; Due to the way unread-command-events in Emacs (not XEmacs), a non-event
4692 ;; symbol in unread-command-events list may cause Emacs to turn this symbol
4693 ;; into an event. Below, we delete nil from event lists, since nil is the most
4694 ;; common symbol that might appear in this wrong context.
4695 (defun viper-set-unread-command-events (arg)
4696 (if viper-emacs-p
4697 (setq
4698 unread-command-events
4699 (let ((new-events
4700 (cond ((eventp arg) (list arg))
4701 ((listp arg) arg)
4702 ((sequencep arg)
4703 (listify-key-sequence arg))
4704 (t (error
4705 "viper-set-unread-command-events: Invalid argument, %S"
4706 arg)))))
4707 (if (not (eventp nil))
4708 (setq new-events (delq nil new-events)))
4709 (append new-events unread-command-events)))
4710 ;; XEmacs
4711 (setq
4712 unread-command-events
4713 (append
4714 (cond ((viper-characterp arg) (list (character-to-event arg)))
4715 ((eventp arg) (list arg))
4716 ((stringp arg) (mapcar 'character-to-event arg))
4717 ((vectorp arg) (append arg nil)) ; turn into list
4718 ((listp arg) (viper-eventify-list-xemacs arg))
4719 (t (error
4720 "viper-set-unread-command-events: Invalid argument, %S" arg)))
4721 unread-command-events))))
4723 ;; list is assumed to be a list of events of characters
4724 (defun viper-eventify-list-xemacs (lis)
4725 (mapcar
4726 (function (lambda (elt)
4727 (cond ((viper-characterp elt) (character-to-event elt))
4728 ((eventp elt) elt)
4729 (t (error
4730 "viper-eventify-list-xemacs: can't convert to event, %S"
4731 elt)))))
4732 lis))
4736 ;;; viper-cmd.el ends here