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