(sendmail-send-it): Bind sendmail-coding-system
[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 1559 (concat "`" (viper-array-to-string keys) "'")
8626cfa2
MK
1560 (viper-abbreviate-string
1561 (if viper-xemacs-p
1562 (replace-in-string text "\n" "^J")
1563 text)
1564 max-text-len
1565 " inserting `" "'" " ......."))
d5e52f99
MK
1566 ))
1567
1568
2f3eb3b6 1569;; don't change viper-d-com if it was viper-repeat command invoked with `.'
d5e52f99 1570;; or in some other way (non-interactively).
2f3eb3b6
MK
1571(defun viper-set-destructive-command (list)
1572 (or (eq viper-intermediate-command 'viper-repeat)
d5e52f99 1573 (progn
2f3eb3b6
MK
1574 (setq viper-d-com list)
1575 (setcar (nthcdr 5 viper-d-com)
1576 (viper-array-to-string (if (arrayp viper-this-command-keys)
1577 viper-this-command-keys
1578 (this-command-keys))))
1579 (viper-push-onto-ring viper-d-com 'viper-command-ring)))
1580 (setq viper-this-command-keys nil))
d5e52f99 1581
2f3eb3b6 1582(defun viper-prev-destructive-command (next)
d5e52f99
MK
1583 "Find previous destructive command in the history of destructive commands.
1584With prefix argument, find next destructive command."
1585 (interactive "P")
2f3eb3b6
MK
1586 (let (cmd viper-intermediate-command)
1587 (if (eq last-command 'viper-display-current-destructive-command)
d5e52f99 1588 ;; repeated search through command history
2f3eb3b6
MK
1589 (setq viper-intermediate-command
1590 'repeating-display-destructive-command)
d5e52f99 1591 ;; first search through command history--set temp ring
2f3eb3b6 1592 (setq viper-temp-command-ring (copy-list viper-command-ring)))
d5e52f99 1593 (setq cmd (if next
2f3eb3b6
MK
1594 (viper-special-ring-rotate1 viper-temp-command-ring 1)
1595 (viper-special-ring-rotate1 viper-temp-command-ring -1)))
d5e52f99
MK
1596 (if (null cmd)
1597 ()
2f3eb3b6
MK
1598 (setq viper-d-com cmd))
1599 (viper-display-current-destructive-command)))
d5e52f99 1600
2f3eb3b6 1601(defun viper-next-destructive-command ()
d5e52f99
MK
1602 "Find next destructive command in the history of destructive commands."
1603 (interactive)
2f3eb3b6 1604 (viper-prev-destructive-command 'next))
d5e52f99 1605
2f3eb3b6 1606(defun viper-insert-prev-from-insertion-ring (arg)
d5e52f99
MK
1607 "Cycle through insertion ring in the direction of older insertions.
1608Undoes previous insertion and inserts new.
1609With prefix argument, cycles in the direction of newer elements.
1610In minibuffer, this command executes whatever the invocation key is bound
1611to in the global map, instead of cycling through the insertion ring."
1612 (interactive "P")
2f3eb3b6
MK
1613 (let (viper-intermediate-command)
1614 (if (eq last-command 'viper-insert-from-insertion-ring)
d5e52f99 1615 (progn ; repeated search through insertion history
2f3eb3b6
MK
1616 (setq viper-intermediate-command 'repeating-insertion-from-ring)
1617 (if (eq viper-current-state 'replace-state)
d5e52f99 1618 (undo 1)
2f3eb3b6 1619 (if viper-last-inserted-string-from-insertion-ring
d5e52f99 1620 (backward-delete-char
2f3eb3b6 1621 (length viper-last-inserted-string-from-insertion-ring))))
d5e52f99
MK
1622 )
1623 ;;first search through insertion history
2f3eb3b6
MK
1624 (setq viper-temp-insertion-ring (copy-list viper-insertion-ring)))
1625 (setq this-command 'viper-insert-from-insertion-ring)
d5e52f99
MK
1626 ;; so that things will be undone properly
1627 (setq buffer-undo-list (cons nil buffer-undo-list))
2f3eb3b6
MK
1628 (setq viper-last-inserted-string-from-insertion-ring
1629 (viper-special-ring-rotate1 viper-temp-insertion-ring (if arg 1 -1)))
d5e52f99 1630
2f3eb3b6
MK
1631 ;; this change of viper-intermediate-command must come after
1632 ;; viper-special-ring-rotate1, so that the ring will rotate, but before the
d5e52f99 1633 ;; insertion.
2f3eb3b6
MK
1634 (setq viper-intermediate-command nil)
1635 (if viper-last-inserted-string-from-insertion-ring
1636 (insert viper-last-inserted-string-from-insertion-ring))
d5e52f99
MK
1637 ))
1638
2f3eb3b6 1639(defun viper-insert-next-from-insertion-ring ()
d5e52f99
MK
1640 "Cycle through insertion ring in the direction of older insertions.
1641Undo previous insertion and inserts new."
1642 (interactive)
2f3eb3b6 1643 (viper-insert-prev-from-insertion-ring 'next))
d5e52f99
MK
1644
1645\f
1646;; some region utilities
1647
1648;; If at the last line of buffer, add \\n before eob, if newline is missing.
2f3eb3b6 1649(defun viper-add-newline-at-eob-if-necessary ()
d5e52f99
MK
1650 (save-excursion
1651 (end-of-line)
1652 ;; make sure all lines end with newline, unless in the minibuffer or
1653 ;; when requested otherwise (require-final-newline is nil)
1654 (if (and (eobp)
1655 (not (bolp))
1656 require-final-newline
2f3eb3b6 1657 (not (viper-is-in-minibuffer))
d5e52f99
MK
1658 (not buffer-read-only))
1659 (insert "\n"))))
1660
2f3eb3b6 1661(defun viper-yank-defun ()
d5e52f99
MK
1662 (mark-defun)
1663 (copy-region-as-kill (point) (mark t)))
1664
1665;; Enlarge region between BEG and END.
2f3eb3b6 1666(defun viper-enlarge-region (beg end)
d5e52f99
MK
1667 (or beg (setq beg end)) ; if beg is nil, set to end
1668 (or end (setq end beg)) ; if end is nil, set to beg
1669
1670 (if (< beg end)
1671 (progn (goto-char beg) (set-mark end))
1672 (goto-char end)
1673 (set-mark beg))
1674 (beginning-of-line)
1675 (exchange-point-and-mark)
1676 (if (or (not (eobp)) (not (bolp))) (forward-line 1))
1677 (if (not (eobp)) (beginning-of-line))
1678 (if (> beg end) (exchange-point-and-mark)))
1679
1680
1681;; Quote region by each line with a user supplied string.
2f3eb3b6
MK
1682(defun viper-quote-region ()
1683 (setq viper-quote-string
1684 (viper-read-string-with-history
d5e52f99
MK
1685 "Quote string: "
1686 nil
2f3eb3b6
MK
1687 'viper-quote-region-history
1688 viper-quote-string))
1689 (viper-enlarge-region (point) (mark t))
d5e52f99 1690 (if (> (point) (mark t)) (exchange-point-and-mark))
2f3eb3b6 1691 (insert viper-quote-string)
d5e52f99
MK
1692 (beginning-of-line)
1693 (forward-line 1)
1694 (while (and (< (point) (mark t)) (bolp))
2f3eb3b6 1695 (insert viper-quote-string)
d5e52f99
MK
1696 (beginning-of-line)
1697 (forward-line 1)))
1698
1699;; Tells whether BEG is on the same line as END.
1700;; If one of the args is nil, it'll return nil.
2f3eb3b6 1701(defun viper-same-line (beg end)
d5e52f99
MK
1702 (let ((selective-display nil)
1703 (incr 0)
1704 temp)
1705 (if (and beg end (> beg end))
1706 (setq temp beg
1707 beg end
1708 end temp))
1709 (if (and beg end)
1710 (cond ((or (> beg (point-max)) (> end (point-max))) ; out of range
1711 nil)
1712 (t
1713 ;; This 'if' is needed because Emacs treats the next empty line
1714 ;; as part of the previous line.
2f3eb3b6 1715 (if (= (viper-line-pos 'start) end)
d5e52f99
MK
1716 (setq incr 1))
1717 (<= (+ incr (count-lines beg end)) 1))))
1718 ))
1719
1720
1721;; Check if the string ends with a newline.
2f3eb3b6 1722(defun viper-end-with-a-newline-p (string)
d5e52f99 1723 (or (string= string "")
2f3eb3b6 1724 (= (viper-seq-last-elt string) ?\n)))
d5e52f99 1725
2f3eb3b6 1726(defun viper-tmp-insert-at-eob (msg)
d5e52f99
MK
1727 (let ((savemax (point-max)))
1728 (goto-char savemax)
1729 (insert msg)
1730 (sit-for 2)
1731 (goto-char savemax) (delete-region (point) (point-max))
1732 ))
1733
1734
1735\f
1736;;; Minibuffer business
1737
2f3eb3b6
MK
1738(defsubst viper-set-minibuffer-style ()
1739 (add-hook 'minibuffer-setup-hook 'viper-minibuffer-setup-sentinel))
d5e52f99
MK
1740
1741
2f3eb3b6
MK
1742(defun viper-minibuffer-setup-sentinel ()
1743 (let ((hook (if viper-vi-style-in-minibuffer
1744 'viper-change-state-to-insert
1745 'viper-change-state-to-emacs)))
d5e52f99
MK
1746 (funcall hook)
1747 ))
1748
1749;; Interpret last event in the local map
2f3eb3b6 1750(defun viper-exit-minibuffer ()
d5e52f99
MK
1751 (interactive)
1752 (let (command)
1753 (setq command (local-key-binding (char-to-string last-command-char)))
1754 (if command
1755 (command-execute command)
1756 (exit-minibuffer))))
1757
1758\f
1759;;; Reading string with history
1760
2f3eb3b6 1761(defun viper-read-string-with-history (prompt &optional initial
d5e52f99
MK
1762 history-var default keymap)
1763 ;; Read string, prompting with PROMPT and inserting the INITIAL
1764 ;; value. Uses HISTORY-VAR. DEFAULT is the default value to accept if the
1765 ;; input is an empty string. Use KEYMAP, if given, or the
1766 ;; minibuffer-local-map.
1767 ;; Default value is displayed until the user types something in the
1768 ;; minibuffer.
1769 (let ((minibuffer-setup-hook
1770 '(lambda ()
1771 (if (stringp initial)
1772 (progn
1773 ;; don't wait if we have unread events or in kbd macro
1774 (or unread-command-events
1775 executing-kbd-macro
1776 (sit-for 840))
1777 (erase-buffer)
1778 (insert initial)))
2f3eb3b6 1779 (viper-minibuffer-setup-sentinel)))
d5e52f99
MK
1780 (val "")
1781 (padding "")
1782 temp-msg)
1783
1784 (setq keymap (or keymap minibuffer-local-map)
1785 initial (or initial "")
1786 temp-msg (if default
1787 (format "(default: %s) " default)
1788 ""))
1789
2f3eb3b6 1790 (setq viper-incomplete-ex-cmd nil)
d5e52f99
MK
1791 (setq val (read-from-minibuffer prompt
1792 (concat temp-msg initial val padding)
1793 keymap nil history-var))
1794 (setq minibuffer-setup-hook nil
2f3eb3b6 1795 padding (viper-array-to-string (this-command-keys))
d5e52f99
MK
1796 temp-msg "")
1797 ;; the following tries to be smart about what to put in history
1798 (if (not (string= val (car (eval history-var))))
1799 (set history-var (cons val (eval history-var))))
1800 (if (or (string= (nth 0 (eval history-var)) (nth 1 (eval history-var)))
1801 (string= (nth 0 (eval history-var)) ""))
1802 (set history-var (cdr (eval history-var))))
2f3eb3b6
MK
1803 ;; If the user enters nothing but the prev cmd wasn't viper-ex,
1804 ;; viper-command-argument, or `! shell-command', this probably means
d5e52f99
MK
1805 ;; that the user typed something then erased. Return "" in this case, not
1806 ;; the default---the default is too confusing in this case.
1807 (cond ((and (string= val "")
1808 (not (string= prompt "!")) ; was a `! shell-command'
1809 (not (memq last-command
2f3eb3b6
MK
1810 '(viper-ex
1811 viper-command-argument
d5e52f99
MK
1812 t)
1813 )))
1814 "")
1815 ((string= val "") (or default ""))
1816 (t val))
1817 ))
1818
1819
1820\f
1821;; insertion commands
1822
1823;; Called when state changes from Insert Vi command mode.
1824;; Repeats the insertion command if Insert state was entered with prefix
1825;; argument > 1.
2f3eb3b6
MK
1826(defun viper-repeat-insert-command ()
1827 (let ((i-com (car viper-d-com))
1828 (val (nth 1 viper-d-com))
1829 (char (nth 2 viper-d-com)))
d5e52f99
MK
1830 (if (and val (> val 1)) ; first check that val is non-nil
1831 (progn
2f3eb3b6
MK
1832 (setq viper-d-com (list i-com (1- val) ?r nil nil nil))
1833 (viper-repeat nil)
1834 (setq viper-d-com (list i-com val char nil nil nil))
d5e52f99
MK
1835 ))))
1836
2f3eb3b6 1837(defun viper-insert (arg)
d5e52f99
MK
1838 "Insert before point."
1839 (interactive "P")
2f3eb3b6
MK
1840 (viper-set-complex-command-for-undo)
1841 (let ((val (viper-p-val arg))
1842 (com (viper-getcom arg)))
1843 (viper-set-destructive-command (list 'viper-insert val ?r nil nil nil))
d5e52f99 1844 (if com
2f3eb3b6
MK
1845 (viper-loop val (viper-yank-last-insertion))
1846 (viper-change-state-to-insert))))
d5e52f99 1847
2f3eb3b6 1848(defun viper-append (arg)
d5e52f99
MK
1849 "Append after point."
1850 (interactive "P")
2f3eb3b6
MK
1851 (viper-set-complex-command-for-undo)
1852 (let ((val (viper-p-val arg))
1853 (com (viper-getcom arg)))
1854 (viper-set-destructive-command (list 'viper-append val ?r nil nil nil))
d5e52f99
MK
1855 (if (not (eolp)) (forward-char))
1856 (if (equal com ?r)
2f3eb3b6
MK
1857 (viper-loop val (viper-yank-last-insertion))
1858 (viper-change-state-to-insert))))
d5e52f99 1859
2f3eb3b6 1860(defun viper-Append (arg)
d5e52f99
MK
1861 "Append at end of line."
1862 (interactive "P")
2f3eb3b6
MK
1863 (viper-set-complex-command-for-undo)
1864 (let ((val (viper-p-val arg))
1865 (com (viper-getcom arg)))
1866 (viper-set-destructive-command (list 'viper-Append val ?r nil nil nil))
d5e52f99
MK
1867 (end-of-line)
1868 (if (equal com ?r)
2f3eb3b6
MK
1869 (viper-loop val (viper-yank-last-insertion))
1870 (viper-change-state-to-insert))))
d5e52f99 1871
2f3eb3b6 1872(defun viper-Insert (arg)
d5e52f99
MK
1873 "Insert before first non-white."
1874 (interactive "P")
2f3eb3b6
MK
1875 (viper-set-complex-command-for-undo)
1876 (let ((val (viper-p-val arg))
1877 (com (viper-getcom arg)))
1878 (viper-set-destructive-command (list 'viper-Insert val ?r nil nil nil))
d5e52f99
MK
1879 (back-to-indentation)
1880 (if (equal com ?r)
2f3eb3b6
MK
1881 (viper-loop val (viper-yank-last-insertion))
1882 (viper-change-state-to-insert))))
d5e52f99 1883
2f3eb3b6 1884(defun viper-open-line (arg)
d5e52f99
MK
1885 "Open line below."
1886 (interactive "P")
2f3eb3b6
MK
1887 (viper-set-complex-command-for-undo)
1888 (let ((val (viper-p-val arg))
1889 (com (viper-getcom arg)))
1890 (viper-set-destructive-command (list 'viper-open-line val ?r nil nil nil))
d5e52f99
MK
1891 (let ((col (current-indentation)))
1892 (if (equal com ?r)
2f3eb3b6 1893 (viper-loop val
d5e52f99
MK
1894 (progn
1895 (end-of-line)
1896 (newline 1)
2f3eb3b6 1897 (if viper-auto-indent
d5e52f99 1898 (progn
2f3eb3b6
MK
1899 (setq viper-cted t)
1900 (if viper-electric-mode
d5e52f99
MK
1901 (indent-according-to-mode)
1902 (indent-to col))
1903 ))
2f3eb3b6 1904 (viper-yank-last-insertion)))
d5e52f99
MK
1905 (end-of-line)
1906 (newline 1)
2f3eb3b6 1907 (if viper-auto-indent
d5e52f99 1908 (progn
2f3eb3b6
MK
1909 (setq viper-cted t)
1910 (if viper-electric-mode
d5e52f99
MK
1911 (indent-according-to-mode)
1912 (indent-to col))))
2f3eb3b6 1913 (viper-change-state-to-insert)))))
d5e52f99 1914
2f3eb3b6 1915(defun viper-Open-line (arg)
d5e52f99
MK
1916 "Open line above."
1917 (interactive "P")
2f3eb3b6
MK
1918 (viper-set-complex-command-for-undo)
1919 (let ((val (viper-p-val arg))
1920 (com (viper-getcom arg)))
1921 (viper-set-destructive-command (list 'viper-Open-line val ?r nil nil nil))
d5e52f99
MK
1922 (let ((col (current-indentation)))
1923 (if (equal com ?r)
2f3eb3b6 1924 (viper-loop val
d5e52f99
MK
1925 (progn
1926 (beginning-of-line)
1927 (open-line 1)
2f3eb3b6 1928 (if viper-auto-indent
d5e52f99 1929 (progn
2f3eb3b6
MK
1930 (setq viper-cted t)
1931 (if viper-electric-mode
d5e52f99
MK
1932 (indent-according-to-mode)
1933 (indent-to col))
1934 ))
2f3eb3b6 1935 (viper-yank-last-insertion)))
d5e52f99
MK
1936 (beginning-of-line)
1937 (open-line 1)
2f3eb3b6 1938 (if viper-auto-indent
d5e52f99 1939 (progn
2f3eb3b6
MK
1940 (setq viper-cted t)
1941 (if viper-electric-mode
d5e52f99
MK
1942 (indent-according-to-mode)
1943 (indent-to col))
1944 ))
2f3eb3b6 1945 (viper-change-state-to-insert)))))
d5e52f99 1946
2f3eb3b6 1947(defun viper-open-line-at-point (arg)
d5e52f99
MK
1948 "Open line at point."
1949 (interactive "P")
2f3eb3b6
MK
1950 (viper-set-complex-command-for-undo)
1951 (let ((val (viper-p-val arg))
1952 (com (viper-getcom arg)))
1953 (viper-set-destructive-command
1954 (list 'viper-open-line-at-point val ?r nil nil nil))
d5e52f99 1955 (if (equal com ?r)
2f3eb3b6 1956 (viper-loop val
d5e52f99
MK
1957 (progn
1958 (open-line 1)
2f3eb3b6 1959 (viper-yank-last-insertion)))
d5e52f99 1960 (open-line 1)
2f3eb3b6 1961 (viper-change-state-to-insert))))
d5e52f99 1962
2f3eb3b6 1963(defun viper-substitute (arg)
d5e52f99
MK
1964 "Substitute characters."
1965 (interactive "P")
2f3eb3b6
MK
1966 (let ((val (viper-p-val arg))
1967 (com (viper-getcom arg)))
d5e52f99
MK
1968 (push-mark nil t)
1969 (forward-char val)
1970 (if (equal com ?r)
2f3eb3b6
MK
1971 (viper-change-subr (mark t) (point))
1972 (viper-change (mark t) (point)))
1973 (viper-set-destructive-command (list 'viper-substitute val ?r nil nil nil))
d5e52f99
MK
1974 ))
1975
2f3eb3b6
MK
1976;; Command bound to S
1977(defun viper-substitute-line (arg)
d5e52f99
MK
1978 "Substitute lines."
1979 (interactive "p")
2f3eb3b6
MK
1980 (viper-set-complex-command-for-undo)
1981 (viper-line (cons arg ?C)))
d5e52f99
MK
1982
1983;; Prepare for replace
2f3eb3b6
MK
1984(defun viper-start-replace ()
1985 (setq viper-began-as-replace t
1986 viper-sitting-in-replace t
1987 viper-replace-chars-to-delete 0
1988 viper-replace-chars-deleted 0)
1989 (viper-add-hook
1990 'viper-after-change-functions 'viper-replace-mode-spy-after t)
1991 (viper-add-hook
1992 'viper-before-change-functions 'viper-replace-mode-spy-before t)
d5e52f99 1993 ;; this will get added repeatedly, but no harm
2f3eb3b6
MK
1994 (add-hook 'after-change-functions 'viper-after-change-sentinel t)
1995 (add-hook 'before-change-functions 'viper-before-change-sentinel t)
1996 (viper-move-marker-locally 'viper-last-posn-in-replace-region
1997 (viper-replace-start))
1998 (viper-add-hook
1999 'viper-post-command-hooks 'viper-replace-state-post-command-sentinel t)
2000 (viper-add-hook
2001 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel t)
d5e52f99 2002 ;; guard against a smartie who switched from R-replace to normal replace
2f3eb3b6
MK
2003 (viper-remove-hook
2004 'viper-post-command-hooks 'viper-R-state-post-command-sentinel)
d5e52f99
MK
2005 (if overwrite-mode (overwrite-mode nil))
2006 )
2007
2008
2009;; checks how many chars were deleted by the last change
2f3eb3b6
MK
2010(defun viper-replace-mode-spy-before (beg end)
2011 (setq viper-replace-chars-deleted
d5e52f99 2012 (- end beg
2f3eb3b6
MK
2013 (max 0 (- end (viper-replace-end)))
2014 (max 0 (- (viper-replace-start) beg))
d5e52f99
MK
2015 )))
2016
2017;; Invoked as an after-change-function to set up parameters of the last change
2f3eb3b6
MK
2018(defun viper-replace-mode-spy-after (beg end length)
2019 (if (memq viper-intermediate-command '(repeating-insertion-from-ring))
d5e52f99 2020 (progn
2f3eb3b6
MK
2021 (setq viper-replace-chars-to-delete 0)
2022 (viper-move-marker-locally
2023 'viper-last-posn-in-replace-region (point)))
d5e52f99
MK
2024
2025 (let (beg-col end-col real-end chars-to-delete)
2f3eb3b6 2026 (setq real-end (min end (viper-replace-end)))
d5e52f99
MK
2027 (save-excursion
2028 (goto-char beg)
2029 (setq beg-col (current-column))
2030 (goto-char real-end)
2031 (setq end-col (current-column)))
2032
2033 ;; If beg of change is outside the replacement region, then don't
2034 ;; delete anything in the repl region (set chars-to-delete to 0).
2035 ;;
2036 ;; This works fine except that we have to take special care of
2037 ;; dabbrev-expand. The problem stems from new-dabbrev.el, which
2038 ;; sometimes simply shifts the repl region rightwards, without
2039 ;; deleting an equal amount of characters.
2040 ;;
2041 ;; The reason why new-dabbrev.el causes this are this:
2042 ;; if one dinamically completes a partial word that starts before the
2043 ;; replacement region (but ends inside) then new-dabbrev.el first
2044 ;; moves cursor backwards, to the beginning of the word to be
2045 ;; completed (say, pt A). Then it inserts the
2046 ;; completed word and then deletes the old, incomplete part.
2047 ;; Since the complete word is inserted at position before the repl
2048 ;; region, the next If-statement would have set chars-to-delete to 0
2049 ;; unless we check for the current command, which must be
2050 ;; dabbrev-expand.
2051 ;;
2052 ;; In fact, it might be also useful to have overlays for insert
2053 ;; regions as well, since this will let us capture the situation when
2054 ;; dabbrev-expand goes back past the insertion point to find the
2055 ;; beginning of the word to be expanded.
2f3eb3b6
MK
2056 (if (or (and (<= (viper-replace-start) beg)
2057 (<= beg (viper-replace-end)))
d5e52f99
MK
2058 (and (= length 0) (eq this-command 'dabbrev-expand)))
2059 (setq chars-to-delete
2060 (max (- end-col beg-col) (- real-end beg) 0))
2061 (setq chars-to-delete 0))
2062
2063 ;; if beg = last change position, it means that we are within the
2064 ;; same command that does multiple changes. Moreover, it means
2065 ;; that we have two subsequent changes (insert/delete) that
2066 ;; complement each other.
2f3eb3b6
MK
2067 (if (= beg (marker-position viper-last-posn-in-replace-region))
2068 (setq viper-replace-chars-to-delete
2069 (- (+ chars-to-delete viper-replace-chars-to-delete)
2070 viper-replace-chars-deleted))
2071 (setq viper-replace-chars-to-delete chars-to-delete))
d5e52f99 2072
2f3eb3b6
MK
2073 (viper-move-marker-locally
2074 'viper-last-posn-in-replace-region
2075 (max (if (> end (viper-replace-end)) (viper-replace-start) end)
2076 (or (marker-position viper-last-posn-in-replace-region)
2077 (viper-replace-start))
d5e52f99
MK
2078 ))
2079
2f3eb3b6 2080 (setq viper-replace-chars-to-delete
d5e52f99 2081 (max 0
2f3eb3b6
MK
2082 (min viper-replace-chars-to-delete
2083 (- (viper-replace-end) viper-last-posn-in-replace-region)
2084 (- (viper-line-pos 'end)
2085 viper-last-posn-in-replace-region)
d5e52f99
MK
2086 )))
2087 )))
2088
2089
2f3eb3b6 2090;; Delete stuff between posn and the end of viper-replace-overlay-marker, if
d5e52f99 2091;; posn is within the overlay.
2f3eb3b6
MK
2092(defun viper-finish-change (posn)
2093 (viper-remove-hook
2094 'viper-after-change-functions 'viper-replace-mode-spy-after)
2095 (viper-remove-hook
2096 'viper-before-change-functions 'viper-replace-mode-spy-before)
2097 (viper-remove-hook
2098 'viper-post-command-hooks 'viper-replace-state-post-command-sentinel)
2099 (viper-remove-hook
2100 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel)
2101 (viper-restore-cursor-color-after-replace)
2102 (setq viper-sitting-in-replace nil) ; just in case we'll need to know it
d5e52f99
MK
2103 (save-excursion
2104 (if (and
2f3eb3b6
MK
2105 viper-replace-overlay
2106 (>= posn (viper-replace-start))
2107 (< posn (viper-replace-end)))
2108 (delete-region posn (viper-replace-end)))
d5e52f99
MK
2109 )
2110
2f3eb3b6
MK
2111 (if (eq viper-current-state 'replace-state)
2112 (viper-downgrade-to-insert))
2113 ;; replace mode ended => nullify viper-last-posn-in-replace-region
2114 (viper-move-marker-locally 'viper-last-posn-in-replace-region nil)
2115 (viper-hide-replace-overlay)
2116 (viper-refresh-mode-line)
2117 (viper-put-string-on-kill-ring viper-last-replace-region)
d5e52f99
MK
2118 )
2119
2120;; Make STRING be the first element of the kill ring.
2f3eb3b6 2121(defun viper-put-string-on-kill-ring (string)
d5e52f99
MK
2122 (setq kill-ring (cons string kill-ring))
2123 (if (> (length kill-ring) kill-ring-max)
2124 (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))
2125 (setq kill-ring-yank-pointer kill-ring))
2126
2f3eb3b6
MK
2127(defun viper-finish-R-mode ()
2128 (viper-remove-hook
2129 'viper-post-command-hooks 'viper-R-state-post-command-sentinel)
2130 (viper-remove-hook
2131 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel)
2132 (viper-downgrade-to-insert))
d5e52f99 2133
2f3eb3b6 2134(defun viper-start-R-mode ()
d5e52f99
MK
2135 ;; Leave arg as 1, not t: XEmacs insists that it must be a pos number
2136 (overwrite-mode 1)
2f3eb3b6
MK
2137 (viper-add-hook
2138 'viper-post-command-hooks 'viper-R-state-post-command-sentinel t)
2139 (viper-add-hook
2140 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel t)
d5e52f99 2141 ;; guard against a smartie who switched from R-replace to normal replace
2f3eb3b6
MK
2142 (viper-remove-hook
2143 'viper-post-command-hooks 'viper-replace-state-post-command-sentinel)
d5e52f99
MK
2144 )
2145
2146
2147
2f3eb3b6 2148(defun viper-replace-state-exit-cmd ()
d5e52f99
MK
2149 "Binding for keys that cause Replace state to switch to Vi or to Insert.
2150These keys are ESC, RET, and LineFeed"
2151 (interactive)
2152 (if overwrite-mode ;; If you are in replace mode invoked via 'R'
2f3eb3b6
MK
2153 (viper-finish-R-mode)
2154 (viper-finish-change viper-last-posn-in-replace-region))
d5e52f99 2155 (let (com)
2f3eb3b6
MK
2156 (if (eq this-command 'viper-intercept-ESC-key)
2157 (setq com 'viper-exit-insert-state)
2158 (viper-set-unread-command-events last-input-char)
d5e52f99
MK
2159 (setq com (key-binding (read-key-sequence nil))))
2160
2161 (condition-case conds
2162 (command-execute com)
2163 (error
2f3eb3b6 2164 (viper-message-conditions conds)))
d5e52f99 2165 )
2f3eb3b6
MK
2166 (viper-hide-replace-overlay))
2167
d5e52f99 2168
2f3eb3b6
MK
2169(defun viper-replace-state-carriage-return ()
2170 "Carriage return in Viper replace state."
d5e52f99
MK
2171 (interactive)
2172 ;; If Emacs start supporting overlay maps, as it currently supports
2f3eb3b6 2173 ;; text-property maps, we could do away with viper-replace-minor-mode and
d5e52f99
MK
2174 ;; just have keymap attached to replace overlay. Then the "if part" of this
2175 ;; statement can be deleted.
2f3eb3b6
MK
2176 (if (or (< (point) (viper-replace-start))
2177 (> (point) (viper-replace-end)))
2178 (let (viper-replace-minor-mode com)
2179 (viper-set-unread-command-events last-input-char)
d5e52f99
MK
2180 (setq com (key-binding (read-key-sequence nil)))
2181 (condition-case conds
2182 (command-execute com)
2183 (error
2f3eb3b6
MK
2184 (viper-message-conditions conds))))
2185 (if (not viper-allow-multiline-replace-regions)
2186 (viper-replace-state-exit-cmd)
2187 (if (viper-same-line (point) (viper-replace-end))
2188 (viper-replace-state-exit-cmd)
2189 ;; delete the rest of line
2190 (delete-region (point) (viper-line-pos 'end))
2191 (save-excursion
2192 (end-of-line)
2193 (if (eobp) (error "Last line in buffer")))
2194 ;; skip to the next line
2195 (forward-line 1)
2196 (back-to-indentation)
2197 ))))
d5e52f99
MK
2198
2199
2200;; This is the function bound to 'R'---unlimited replace.
2201;; Similar to Emacs's own overwrite-mode.
2f3eb3b6 2202(defun viper-overwrite (arg)
d5e52f99
MK
2203 "Begin overwrite mode."
2204 (interactive "P")
2f3eb3b6
MK
2205 (let ((val (viper-p-val arg))
2206 (com (viper-getcom arg)) (len))
2207 (viper-set-destructive-command (list 'viper-overwrite val ?r nil nil nil))
d5e52f99
MK
2208 (if com
2209 (progn
2f3eb3b6
MK
2210 ;; Viper saves inserted text in viper-last-insertion
2211 (setq len (length viper-last-insertion))
d5e52f99 2212 (delete-char len)
2f3eb3b6
MK
2213 (viper-loop val (viper-yank-last-insertion)))
2214 (setq last-command 'viper-overwrite)
2215 (viper-set-complex-command-for-undo)
2216 (viper-set-replace-overlay (point) (viper-line-pos 'end))
2217 (viper-change-state-to-replace)
d5e52f99
MK
2218 )))
2219
2220\f
2221;; line commands
2222
2f3eb3b6 2223(defun viper-line (arg)
d5e52f99
MK
2224 (let ((val (car arg))
2225 (com (cdr arg)))
2f3eb3b6 2226 (viper-move-marker-locally 'viper-com-point (point))
d5e52f99 2227 (if (not (eobp))
2f3eb3b6 2228 (viper-next-line-carefully (1- val)))
d5e52f99
MK
2229 ;; this ensures that dd, cc, D, yy will do the right thing on the last
2230 ;; line of buffer when this line has no \n.
2f3eb3b6
MK
2231 (viper-add-newline-at-eob-if-necessary)
2232 (viper-execute-com 'viper-line val com))
d5e52f99
MK
2233 (if (and (eobp) (not (bobp))) (forward-line -1))
2234 )
2235
2f3eb3b6 2236(defun viper-yank-line (arg)
d5e52f99
MK
2237 "Yank ARG lines (in Vi's sense)."
2238 (interactive "P")
2f3eb3b6
MK
2239 (let ((val (viper-p-val arg)))
2240 (viper-line (cons val ?Y))))
d5e52f99
MK
2241
2242\f
2243;; region commands
2244
2f3eb3b6 2245(defun viper-region (arg)
d5e52f99
MK
2246 "Execute command on a region."
2247 (interactive "P")
2f3eb3b6
MK
2248 (let ((val (viper-P-val arg))
2249 (com (viper-getcom arg)))
2250 (viper-move-marker-locally 'viper-com-point (point))
d5e52f99 2251 (exchange-point-and-mark)
2f3eb3b6 2252 (viper-execute-com 'viper-region val com)))
d5e52f99 2253
2f3eb3b6 2254(defun viper-Region (arg)
d5e52f99
MK
2255 "Execute command on a Region."
2256 (interactive "P")
2f3eb3b6
MK
2257 (let ((val (viper-P-val arg))
2258 (com (viper-getCom arg)))
2259 (viper-move-marker-locally 'viper-com-point (point))
d5e52f99 2260 (exchange-point-and-mark)
2f3eb3b6 2261 (viper-execute-com 'viper-Region val com)))
d5e52f99 2262
2f3eb3b6 2263(defun viper-replace-char (arg)
d5e52f99
MK
2264 "Replace the following ARG chars by the character read."
2265 (interactive "P")
2266 (if (and (eolp) (bolp)) (error "No character to replace here"))
2f3eb3b6
MK
2267 (let ((val (viper-p-val arg))
2268 (com (viper-getcom arg)))
2269 (viper-replace-char-subr com val)
d5e52f99 2270 (if (and (eolp) (not (bolp))) (forward-char 1))
2f3eb3b6
MK
2271 (viper-set-destructive-command
2272 (list 'viper-replace-char val ?r nil viper-d-char nil))
d5e52f99
MK
2273 ))
2274
2f3eb3b6 2275(defun viper-replace-char-subr (com arg)
d5e52f99 2276 (let ((take-care-of-iso-accents
2f3eb3b6 2277 (and (boundp 'iso-accents-mode) viper-automatic-iso-accents))
d5e52f99
MK
2278 char)
2279 (setq char (if (equal com ?r)
2f3eb3b6 2280 viper-d-char
d5e52f99
MK
2281 (read-char)))
2282 (if (and take-care-of-iso-accents (memq char '(?' ?\" ?^ ?~)))
2283 ;; get European characters
2284 (progn
2285 (iso-accents-mode 1)
2f3eb3b6 2286 (viper-set-unread-command-events char)
d5e52f99
MK
2287 (setq char (aref (read-key-sequence nil) 0))
2288 (iso-accents-mode -1)))
2289 (delete-char arg t)
2f3eb3b6
MK
2290 (setq viper-d-char char)
2291 (viper-loop (if (> arg 0) arg (- arg))
d5e52f99
MK
2292 (if (eq char ?\C-m) (insert "\n") (insert char)))
2293 (backward-char arg)))
2294
2295\f
2296;; basic cursor movement. j, k, l, h commands.
2297
2f3eb3b6 2298(defun viper-forward-char (arg)
d5e52f99
MK
2299 "Move point right ARG characters (left if ARG negative).
2300On reaching end of line, stop and signal error."
2301 (interactive "P")
2f3eb3b6
MK
2302 (viper-leave-region-active)
2303 (let ((val (viper-p-val arg))
2304 (com (viper-getcom arg)))
2305 (if com (viper-move-marker-locally 'viper-com-point (point)))
2306 (if viper-ex-style-motion
d5e52f99
MK
2307 (progn
2308 ;; the boundary condition check gets weird here because
2309 ;; forward-char may be the parameter of a delete, and 'dl' works
2310 ;; just like 'x' for the last char on a line, so we have to allow
2f3eb3b6 2311 ;; the forward motion before the 'viper-execute-com', but, of
d5e52f99 2312 ;; course, 'dl' doesn't work on an empty line, so we have to
2f3eb3b6 2313 ;; catch that condition before 'viper-execute-com'
d5e52f99 2314 (if (and (eolp) (bolp)) (error "") (forward-char val))
2f3eb3b6 2315 (if com (viper-execute-com 'viper-forward-char val com))
d5e52f99
MK
2316 (if (eolp) (progn (backward-char 1) (error ""))))
2317 (forward-char val)
2f3eb3b6 2318 (if com (viper-execute-com 'viper-forward-char val com)))))
d5e52f99 2319
2f3eb3b6 2320(defun viper-backward-char (arg)
d5e52f99
MK
2321 "Move point left ARG characters (right if ARG negative).
2322On reaching beginning of line, stop and signal error."
2323 (interactive "P")
2f3eb3b6
MK
2324 (viper-leave-region-active)
2325 (let ((val (viper-p-val arg))
2326 (com (viper-getcom arg)))
2327 (if com (viper-move-marker-locally 'viper-com-point (point)))
2328 (if viper-ex-style-motion
d5e52f99
MK
2329 (progn
2330 (if (bolp) (error "") (backward-char val))
2f3eb3b6 2331 (if com (viper-execute-com 'viper-backward-char val com)))
d5e52f99 2332 (backward-char val)
2f3eb3b6 2333 (if com (viper-execute-com 'viper-backward-char val com)))))
d5e52f99
MK
2334
2335;; Like forward-char, but doesn't move at end of buffer.
2f3eb3b6 2336(defun viper-forward-char-carefully (&optional arg)
d5e52f99
MK
2337 (setq arg (or arg 1))
2338 (if (>= (point-max) (+ (point) arg))
2339 (forward-char arg)
2340 (goto-char (point-max))))
2341
2342;; Like backward-char, but doesn't move at end of buffer.
2f3eb3b6 2343(defun viper-backward-char-carefully (&optional arg)
d5e52f99
MK
2344 (setq arg (or arg 1))
2345 (if (<= (point-min) (- (point) arg))
2346 (backward-char arg)
2347 (goto-char (point-min))))
2348
2f3eb3b6 2349(defun viper-next-line-carefully (arg)
d5e52f99
MK
2350 (condition-case nil
2351 (next-line arg)
2352 (error nil)))
2353
2354
2355\f
2356;;; Word command
2357
2f3eb3b6
MK
2358;; Words are formed from alpha's and nonalphas - <sp>,\t\n are separators for
2359;; word movement. When executed with a destructive command, \n is usually left
2360;; untouched for the last word. Viper uses syntax table to determine what is a
2361;; word and what is a separator. However, \n is always a separator. Also, if
2362;; viper-syntax-preference is 'vi, then `_' is part of the word.
d5e52f99
MK
2363
2364;; skip only one \n
2f3eb3b6 2365(defun viper-skip-separators (forward)
d5e52f99
MK
2366 (if forward
2367 (progn
2f3eb3b6 2368 (viper-skip-all-separators-forward 'within-line)
d5e52f99
MK
2369 (if (looking-at "\n")
2370 (progn
2371 (forward-char)
2f3eb3b6
MK
2372 (viper-skip-all-separators-forward 'within-line))))
2373 (viper-skip-all-separators-backward 'within-line)
d5e52f99
MK
2374 (backward-char)
2375 (if (looking-at "\n")
2f3eb3b6 2376 (viper-skip-all-separators-backward 'within-line)
d5e52f99
MK
2377 (forward-char))))
2378
2f3eb3b6 2379(defun viper-forward-word-kernel (val)
d5e52f99 2380 (while (> val 0)
2f3eb3b6
MK
2381 (cond ((viper-looking-at-alpha)
2382 (viper-skip-alpha-forward "_")
2383 (viper-skip-separators t))
2384 ((viper-looking-at-separator)
2385 (viper-skip-separators t))
2386 ((not (viper-looking-at-alphasep))
2387 (viper-skip-nonalphasep-forward)
2388 (viper-skip-separators t)))
d5e52f99
MK
2389 (setq val (1- val))))
2390
2391;; first search backward for pat. Then skip chars backwards using aux-pat
2f3eb3b6 2392(defun viper-fwd-skip (pat aux-pat lim)
d5e52f99
MK
2393 (if (and (save-excursion
2394 (re-search-backward pat lim t))
2395 (= (point) (match-end 0)))
2396 (goto-char (match-beginning 0)))
2397 (skip-chars-backward aux-pat lim)
2398 (if (= (point) lim)
2f3eb3b6 2399 (viper-forward-char-carefully))
d5e52f99
MK
2400 )
2401
2402
2f3eb3b6 2403(defun viper-forward-word (arg)
d5e52f99
MK
2404 "Forward word."
2405 (interactive "P")
2f3eb3b6
MK
2406 (viper-leave-region-active)
2407 (let ((val (viper-p-val arg))
2408 (com (viper-getcom arg)))
2409 (if com (viper-move-marker-locally 'viper-com-point (point)))
2410 (viper-forward-word-kernel val)
d5e52f99
MK
2411 (if com (progn
2412 (cond ((memq com (list ?c (- ?c)))
2f3eb3b6 2413 (viper-fwd-skip "\n[ \t]*" " \t" viper-com-point))
d5e52f99
MK
2414 ;; Yank words including the whitespace, but not newline
2415 ((memq com (list ?y (- ?y)))
2f3eb3b6
MK
2416 (viper-fwd-skip "\n[ \t]*" "" viper-com-point))
2417 ((viper-dotable-command-p com)
2418 (viper-fwd-skip "\n[ \t]*" "" viper-com-point)))
2419 (viper-execute-com 'viper-forward-word val com)))))
d5e52f99
MK
2420
2421
2f3eb3b6 2422(defun viper-forward-Word (arg)
d5e52f99
MK
2423 "Forward word delimited by white characters."
2424 (interactive "P")
2f3eb3b6
MK
2425 (viper-leave-region-active)
2426 (let ((val (viper-p-val arg))
2427 (com (viper-getcom arg)))
2428 (if com (viper-move-marker-locally 'viper-com-point (point)))
2429 (viper-loop val
d5e52f99 2430 (progn
2f3eb3b6
MK
2431 (viper-skip-nonseparators 'forward)
2432 (viper-skip-separators t)))
d5e52f99
MK
2433 (if com (progn
2434 (cond ((memq com (list ?c (- ?c)))
2f3eb3b6 2435 (viper-fwd-skip "\n[ \t]*" " \t" viper-com-point))
d5e52f99
MK
2436 ;; Yank words including the whitespace, but not newline
2437 ((memq com (list ?y (- ?y)))
2f3eb3b6
MK
2438 (viper-fwd-skip "\n[ \t]*" "" viper-com-point))
2439 ((viper-dotable-command-p com)
2440 (viper-fwd-skip "\n[ \t]*" "" viper-com-point)))
2441 (viper-execute-com 'viper-forward-Word val com)))))
d5e52f99
MK
2442
2443
2444;; this is a bit different from Vi, but Vi's end of word
2445;; makes no sense whatsoever
2f3eb3b6
MK
2446(defun viper-end-of-word-kernel ()
2447 (if (viper-end-of-word-p) (forward-char))
2448 (if (viper-looking-at-separator)
2449 (viper-skip-all-separators-forward))
d5e52f99 2450
2f3eb3b6
MK
2451 (cond ((viper-looking-at-alpha) (viper-skip-alpha-forward "_"))
2452 ((not (viper-looking-at-alphasep)) (viper-skip-nonalphasep-forward)))
2453 (viper-backward-char-carefully))
d5e52f99 2454
2f3eb3b6 2455(defun viper-end-of-word-p ()
d5e52f99
MK
2456 (or (eobp)
2457 (save-excursion
2f3eb3b6 2458 (cond ((viper-looking-at-alpha)
d5e52f99 2459 (forward-char)
2f3eb3b6
MK
2460 (not (viper-looking-at-alpha)))
2461 ((not (viper-looking-at-alphasep))
d5e52f99 2462 (forward-char)
2f3eb3b6 2463 (viper-looking-at-alphasep))))))
d5e52f99
MK
2464
2465
2f3eb3b6 2466(defun viper-end-of-word (arg &optional careful)
d5e52f99
MK
2467 "Move point to end of current word."
2468 (interactive "P")
2f3eb3b6
MK
2469 (viper-leave-region-active)
2470 (let ((val (viper-p-val arg))
2471 (com (viper-getcom arg)))
2472 (if com (viper-move-marker-locally 'viper-com-point (point)))
2473 (viper-loop val (viper-end-of-word-kernel))
d5e52f99
MK
2474 (if com
2475 (progn
2476 (forward-char)
2f3eb3b6 2477 (viper-execute-com 'viper-end-of-word val com)))))
d5e52f99 2478
2f3eb3b6 2479(defun viper-end-of-Word (arg)
d5e52f99
MK
2480 "Forward to end of word delimited by white character."
2481 (interactive "P")
2f3eb3b6
MK
2482 (viper-leave-region-active)
2483 (let ((val (viper-p-val arg))
2484 (com (viper-getcom arg)))
2485 (if com (viper-move-marker-locally 'viper-com-point (point)))
2486 (viper-loop val
d5e52f99 2487 (progn
2f3eb3b6
MK
2488 (viper-end-of-word-kernel)
2489 (viper-skip-nonseparators 'forward)
d5e52f99
MK
2490 (backward-char)))
2491 (if com
2492 (progn
2493 (forward-char)
2f3eb3b6 2494 (viper-execute-com 'viper-end-of-Word val com)))))
d5e52f99 2495
2f3eb3b6 2496(defun viper-backward-word-kernel (val)
d5e52f99
MK
2497 (while (> val 0)
2498 (backward-char)
2f3eb3b6
MK
2499 (cond ((viper-looking-at-alpha)
2500 (viper-skip-alpha-backward "_"))
2501 ((viper-looking-at-separator)
d5e52f99 2502 (forward-char)
2f3eb3b6 2503 (viper-skip-separators nil)
d5e52f99 2504 (backward-char)
2f3eb3b6
MK
2505 (cond ((viper-looking-at-alpha)
2506 (viper-skip-alpha-backward "_"))
2507 ((not (viper-looking-at-alphasep))
2508 (viper-skip-nonalphasep-backward))
d5e52f99 2509 (t (forward-char))))
2f3eb3b6
MK
2510 ((not (viper-looking-at-alphasep))
2511 (viper-skip-nonalphasep-backward)))
d5e52f99
MK
2512 (setq val (1- val))))
2513
2f3eb3b6 2514(defun viper-backward-word (arg)
d5e52f99
MK
2515 "Backward word."
2516 (interactive "P")
2f3eb3b6
MK
2517 (viper-leave-region-active)
2518 (let ((val (viper-p-val arg))
2519 (com (viper-getcom arg)))
d5e52f99
MK
2520 (if com
2521 (let (i)
2522 (if (setq i (save-excursion (backward-char) (looking-at "\n")))
2523 (backward-char))
2f3eb3b6 2524 (viper-move-marker-locally 'viper-com-point (point))
d5e52f99 2525 (if i (forward-char))))
2f3eb3b6
MK
2526 (viper-backward-word-kernel val)
2527 (if com (viper-execute-com 'viper-backward-word val com))))
d5e52f99 2528
2f3eb3b6 2529(defun viper-backward-Word (arg)
d5e52f99
MK
2530 "Backward word delimited by white character."
2531 (interactive "P")
2f3eb3b6
MK
2532 (viper-leave-region-active)
2533 (let ((val (viper-p-val arg))
2534 (com (viper-getcom arg)))
d5e52f99
MK
2535 (if com
2536 (let (i)
2537 (if (setq i (save-excursion (backward-char) (looking-at "\n")))
2538 (backward-char))
2f3eb3b6 2539 (viper-move-marker-locally 'viper-com-point (point))
d5e52f99 2540 (if i (forward-char))))
2f3eb3b6 2541 (viper-loop val
d5e52f99 2542 (progn
2f3eb3b6
MK
2543 (viper-skip-separators nil)
2544 (viper-skip-nonseparators 'backward)))
2545 (if com (viper-execute-com 'viper-backward-Word val com))))
d5e52f99
MK
2546
2547
2548\f
2549;; line commands
2550
2f3eb3b6 2551(defun viper-beginning-of-line (arg)
d5e52f99
MK
2552 "Go to beginning of line."
2553 (interactive "P")
2f3eb3b6
MK
2554 (viper-leave-region-active)
2555 (let ((val (viper-p-val arg))
2556 (com (viper-getcom arg)))
2557 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2558 (beginning-of-line val)
2f3eb3b6 2559 (if com (viper-execute-com 'viper-beginning-of-line val com))))
d5e52f99 2560
2f3eb3b6 2561(defun viper-bol-and-skip-white (arg)
d5e52f99
MK
2562 "Beginning of line at first non-white character."
2563 (interactive "P")
2f3eb3b6
MK
2564 (viper-leave-region-active)
2565 (let ((val (viper-p-val arg))
2566 (com (viper-getcom arg)))
2567 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2568 (forward-to-indentation (1- val))
2f3eb3b6 2569 (if com (viper-execute-com 'viper-bol-and-skip-white val com))))
d5e52f99 2570
2f3eb3b6 2571(defun viper-goto-eol (arg)
d5e52f99
MK
2572 "Go to end of line."
2573 (interactive "P")
2f3eb3b6
MK
2574 (viper-leave-region-active)
2575 (let ((val (viper-p-val arg))
2576 (com (viper-getcom arg)))
2577 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2578 (end-of-line val)
2f3eb3b6
MK
2579 (if com (viper-execute-com 'viper-goto-eol val com))
2580 (if viper-ex-style-motion
d5e52f99 2581 (if (and (eolp) (not (bolp))
2f3eb3b6
MK
2582 ;; a fix for viper-change-to-eol
2583 (not (equal viper-current-state 'insert-state)))
d5e52f99
MK
2584 (backward-char 1)
2585 ))))
2586
2587
2f3eb3b6 2588(defun viper-goto-col (arg)
d5e52f99
MK
2589 "Go to ARG's column."
2590 (interactive "P")
2f3eb3b6
MK
2591 (viper-leave-region-active)
2592 (let ((val (viper-p-val arg))
2593 (com (viper-getcom arg))
d5e52f99 2594 line-len)
2f3eb3b6
MK
2595 (setq line-len (- (viper-line-pos 'end) (viper-line-pos 'start)))
2596 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2597 (beginning-of-line)
2598 (forward-char (1- (min line-len val)))
2599 (while (> (current-column) (1- val))
2600 (backward-char 1))
2f3eb3b6 2601 (if com (viper-execute-com 'viper-goto-col val com))
d5e52f99
MK
2602 (save-excursion
2603 (end-of-line)
2604 (if (> val (current-column)) (error "")))
2605 ))
2606
2607
2f3eb3b6 2608(defun viper-next-line (arg)
d5e52f99
MK
2609 "Go to next line."
2610 (interactive "P")
2f3eb3b6
MK
2611 (viper-leave-region-active)
2612 (let ((val (viper-p-val arg))
2613 (com (viper-getCom arg)))
2614 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2615 (next-line val)
2f3eb3b6 2616 (if viper-ex-style-motion
d5e52f99
MK
2617 (if (and (eolp) (not (bolp))) (backward-char 1)))
2618 (setq this-command 'next-line)
2f3eb3b6 2619 (if com (viper-execute-com 'viper-next-line val com))))
d5e52f99 2620
2f3eb3b6 2621(defun viper-next-line-at-bol (arg)
d5e52f99
MK
2622 "Next line at beginning of line."
2623 (interactive "P")
2f3eb3b6 2624 (viper-leave-region-active)
d5e52f99
MK
2625 (save-excursion
2626 (end-of-line)
2627 (if (eobp) (error "Last line in buffer")))
2f3eb3b6
MK
2628 (let ((val (viper-p-val arg))
2629 (com (viper-getCom arg)))
2630 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2631 (forward-line val)
2632 (back-to-indentation)
2f3eb3b6 2633 (if com (viper-execute-com 'viper-next-line-at-bol val com))))
d5e52f99 2634
2f3eb3b6 2635(defun viper-previous-line (arg)
d5e52f99
MK
2636 "Go to previous line."
2637 (interactive "P")
2f3eb3b6
MK
2638 (viper-leave-region-active)
2639 (let ((val (viper-p-val arg))
2640 (com (viper-getCom arg)))
2641 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2642 (previous-line val)
2f3eb3b6 2643 (if viper-ex-style-motion
d5e52f99
MK
2644 (if (and (eolp) (not (bolp))) (backward-char 1)))
2645 (setq this-command 'previous-line)
2f3eb3b6 2646 (if com (viper-execute-com 'viper-previous-line val com))))
d5e52f99
MK
2647
2648
2f3eb3b6 2649(defun viper-previous-line-at-bol (arg)
d5e52f99
MK
2650 "Previous line at beginning of line."
2651 (interactive "P")
2f3eb3b6 2652 (viper-leave-region-active)
d5e52f99
MK
2653 (save-excursion
2654 (beginning-of-line)
2655 (if (bobp) (error "First line in buffer")))
2f3eb3b6
MK
2656 (let ((val (viper-p-val arg))
2657 (com (viper-getCom arg)))
2658 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2659 (forward-line (- val))
2660 (back-to-indentation)
2f3eb3b6 2661 (if com (viper-execute-com 'viper-previous-line val com))))
d5e52f99 2662
2f3eb3b6 2663(defun viper-change-to-eol (arg)
d5e52f99
MK
2664 "Change to end of line."
2665 (interactive "P")
2f3eb3b6 2666 (viper-goto-eol (cons arg ?c)))
d5e52f99 2667
2f3eb3b6 2668(defun viper-kill-line (arg)
d5e52f99
MK
2669 "Delete line."
2670 (interactive "P")
2f3eb3b6 2671 (viper-goto-eol (cons arg ?d)))
d5e52f99 2672
2f3eb3b6 2673(defun viper-erase-line (arg)
d5e52f99
MK
2674 "Erase line."
2675 (interactive "P")
2f3eb3b6 2676 (viper-beginning-of-line (cons arg ?d)))
d5e52f99
MK
2677
2678\f
2679;;; Moving around
2680
2f3eb3b6 2681(defun viper-goto-line (arg)
d5e52f99
MK
2682 "Go to ARG's line. Without ARG go to end of buffer."
2683 (interactive "P")
2f3eb3b6
MK
2684 (let ((val (viper-P-val arg))
2685 (com (viper-getCom arg)))
2686 (viper-move-marker-locally 'viper-com-point (point))
2687 (viper-deactivate-mark)
d5e52f99
MK
2688 (push-mark nil t)
2689 (if (null val)
2690 (goto-char (point-max))
2691 (goto-char (point-min))
2692 (forward-line (1- val)))
2693
2694 ;; positioning is done twice: before and after command execution
2695 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2696 (back-to-indentation)
2697
2f3eb3b6 2698 (if com (viper-execute-com 'viper-goto-line val com))
d5e52f99
MK
2699
2700 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2701 (back-to-indentation)
2702 ))
2703
2704;; Find ARG's occurrence of CHAR on the current line.
2705;; If FORWARD then search is forward, otherwise backward. OFFSET is used to
2706;; adjust point after search.
2f3eb3b6 2707(defun viper-find-char (arg char forward offset)
d5e52f99
MK
2708 (or (char-or-string-p char) (error ""))
2709 (let ((arg (if forward arg (- arg)))
2f3eb3b6
MK
2710 (cmd (if (eq viper-intermediate-command 'viper-repeat)
2711 (nth 5 viper-d-com)
2712 (viper-array-to-string (this-command-keys))))
d5e52f99
MK
2713 point)
2714 (save-excursion
2715 (save-restriction
2716 (if (> arg 0)
2717 (narrow-to-region
2718 ;; forward search begins here
2719 (if (eolp) (error "Command `%s': At end of line" cmd) (point))
2720 ;; forward search ends here
2721 (progn (end-of-line) (point)))
2722 (narrow-to-region
2723 ;; backward search begins from here
2724 (if (bolp)
2725 (error "Command `%s': At beginning of line" cmd) (point))
2726 ;; backward search ends here
2727 (progn (beginning-of-line) (point))))
2728 ;; if arg > 0, point is forwarded before search.
2729 (if (> arg 0) (goto-char (1+ (point-min)))
2730 (goto-char (point-max)))
2731 (if (let ((case-fold-search nil))
2732 (search-forward (char-to-string char) nil 0 arg))
2733 (setq point (point))
2734 (error "Command `%s': `%c' not found" cmd char))))
2735 (goto-char (+ point (if (> arg 0) (if offset -2 -1) (if offset 1 0))))))
2736
2f3eb3b6 2737(defun viper-find-char-forward (arg)
d5e52f99
MK
2738 "Find char on the line.
2739If called interactively read the char to find from the terminal, and if
2f3eb3b6 2740called from viper-repeat, the char last used is used. This behaviour is
d5e52f99
MK
2741controlled by the sign of prefix numeric value."
2742 (interactive "P")
2f3eb3b6
MK
2743 (let ((val (viper-p-val arg))
2744 (com (viper-getcom arg))
2745 (cmd-representation (nth 5 viper-d-com)))
d5e52f99
MK
2746 (if (> val 0)
2747 ;; this means that the function was called interactively
2f3eb3b6
MK
2748 (setq viper-f-char (read-char)
2749 viper-f-forward t
2750 viper-f-offset nil)
2751 ;; viper-repeat --- set viper-F-char from command-keys
2752 (setq viper-F-char (if (stringp cmd-representation)
2753 (viper-seq-last-elt cmd-representation)
2754 viper-F-char)
2755 viper-f-char viper-F-char)
d5e52f99 2756 (setq val (- val)))
2f3eb3b6
MK
2757 (if com (viper-move-marker-locally 'viper-com-point (point)))
2758 (viper-find-char
2759 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) t nil)
d5e52f99
MK
2760 (setq val (- val))
2761 (if com
2762 (progn
2f3eb3b6 2763 (setq viper-F-char viper-f-char) ; set new viper-F-char
d5e52f99 2764 (forward-char)
2f3eb3b6 2765 (viper-execute-com 'viper-find-char-forward val com)))))
d5e52f99 2766
2f3eb3b6 2767(defun viper-goto-char-forward (arg)
d5e52f99
MK
2768 "Go up to char ARG forward on line."
2769 (interactive "P")
2f3eb3b6
MK
2770 (let ((val (viper-p-val arg))
2771 (com (viper-getcom arg))
2772 (cmd-representation (nth 5 viper-d-com)))
d5e52f99
MK
2773 (if (> val 0)
2774 ;; this means that the function was called interactively
2f3eb3b6
MK
2775 (setq viper-f-char (read-char)
2776 viper-f-forward t
2777 viper-f-offset t)
2778 ;; viper-repeat --- set viper-F-char from command-keys
2779 (setq viper-F-char (if (stringp cmd-representation)
2780 (viper-seq-last-elt cmd-representation)
2781 viper-F-char)
2782 viper-f-char viper-F-char)
d5e52f99 2783 (setq val (- val)))
2f3eb3b6
MK
2784 (if com (viper-move-marker-locally 'viper-com-point (point)))
2785 (viper-find-char
2786 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) t t)
d5e52f99
MK
2787 (setq val (- val))
2788 (if com
2789 (progn
2f3eb3b6 2790 (setq viper-F-char viper-f-char) ; set new viper-F-char
d5e52f99 2791 (forward-char)
2f3eb3b6 2792 (viper-execute-com 'viper-goto-char-forward val com)))))
d5e52f99 2793
2f3eb3b6 2794(defun viper-find-char-backward (arg)
d5e52f99
MK
2795 "Find char ARG on line backward."
2796 (interactive "P")
2f3eb3b6
MK
2797 (let ((val (viper-p-val arg))
2798 (com (viper-getcom arg))
2799 (cmd-representation (nth 5 viper-d-com)))
d5e52f99
MK
2800 (if (> val 0)
2801 ;; this means that the function was called interactively
2f3eb3b6
MK
2802 (setq viper-f-char (read-char)
2803 viper-f-forward nil
2804 viper-f-offset nil)
2805 ;; viper-repeat --- set viper-F-char from command-keys
2806 (setq viper-F-char (if (stringp cmd-representation)
2807 (viper-seq-last-elt cmd-representation)
2808 viper-F-char)
2809 viper-f-char viper-F-char)
d5e52f99 2810 (setq val (- val)))
2f3eb3b6
MK
2811 (if com (viper-move-marker-locally 'viper-com-point (point)))
2812 (viper-find-char
2813 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) nil nil)
d5e52f99
MK
2814 (setq val (- val))
2815 (if com
2816 (progn
2f3eb3b6
MK
2817 (setq viper-F-char viper-f-char) ; set new viper-F-char
2818 (viper-execute-com 'viper-find-char-backward val com)))))
d5e52f99 2819
2f3eb3b6 2820(defun viper-goto-char-backward (arg)
d5e52f99
MK
2821 "Go up to char ARG backward on line."
2822 (interactive "P")
2f3eb3b6
MK
2823 (let ((val (viper-p-val arg))
2824 (com (viper-getcom arg))
2825 (cmd-representation (nth 5 viper-d-com)))
d5e52f99
MK
2826 (if (> val 0)
2827 ;; this means that the function was called interactively
2f3eb3b6
MK
2828 (setq viper-f-char (read-char)
2829 viper-f-forward nil
2830 viper-f-offset t)
2831 ;; viper-repeat --- set viper-F-char from command-keys
2832 (setq viper-F-char (if (stringp cmd-representation)
2833 (viper-seq-last-elt cmd-representation)
2834 viper-F-char)
2835 viper-f-char viper-F-char)
d5e52f99 2836 (setq val (- val)))
2f3eb3b6
MK
2837 (if com (viper-move-marker-locally 'viper-com-point (point)))
2838 (viper-find-char
2839 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) nil t)
d5e52f99
MK
2840 (setq val (- val))
2841 (if com
2842 (progn
2f3eb3b6
MK
2843 (setq viper-F-char viper-f-char) ; set new viper-F-char
2844 (viper-execute-com 'viper-goto-char-backward val com)))))
d5e52f99 2845
2f3eb3b6 2846(defun viper-repeat-find (arg)
d5e52f99
MK
2847 "Repeat previous find command."
2848 (interactive "P")
2f3eb3b6
MK
2849 (let ((val (viper-p-val arg))
2850 (com (viper-getcom arg)))
2851 (viper-deactivate-mark)
2852 (if com (viper-move-marker-locally 'viper-com-point (point)))
2853 (viper-find-char val viper-f-char viper-f-forward viper-f-offset)
d5e52f99
MK
2854 (if com
2855 (progn
2f3eb3b6
MK
2856 (if viper-f-forward (forward-char))
2857 (viper-execute-com 'viper-repeat-find val com)))))
d5e52f99 2858
2f3eb3b6 2859(defun viper-repeat-find-opposite (arg)
d5e52f99
MK
2860 "Repeat previous find command in the opposite direction."
2861 (interactive "P")
2f3eb3b6
MK
2862 (let ((val (viper-p-val arg))
2863 (com (viper-getcom arg)))
2864 (viper-deactivate-mark)
2865 (if com (viper-move-marker-locally 'viper-com-point (point)))
2866 (viper-find-char val viper-f-char (not viper-f-forward) viper-f-offset)
d5e52f99
MK
2867 (if com
2868 (progn
2f3eb3b6
MK
2869 (if viper-f-forward (forward-char))
2870 (viper-execute-com 'viper-repeat-find-opposite val com)))))
d5e52f99
MK
2871
2872\f
2873;; window scrolling etc.
2874
2f3eb3b6 2875(defun viper-window-top (arg)
d5e52f99
MK
2876 "Go to home window line."
2877 (interactive "P")
2f3eb3b6
MK
2878 (let ((val (viper-p-val arg))
2879 (com (viper-getCom arg)))
2880 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2881 (push-mark nil t)
2882 (move-to-window-line (1- val))
2883
2884 ;; positioning is done twice: before and after command execution
2885 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2886 (back-to-indentation)
2887
2f3eb3b6 2888 (if com (viper-execute-com 'viper-window-top val com))
d5e52f99
MK
2889
2890 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2891 (back-to-indentation)
2892 ))
2893
2f3eb3b6 2894(defun viper-window-middle (arg)
d5e52f99
MK
2895 "Go to middle window line."
2896 (interactive "P")
2f3eb3b6
MK
2897 (let ((val (viper-p-val arg))
2898 (com (viper-getCom arg))
d5e52f99 2899 lines)
2f3eb3b6 2900 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2901 (push-mark nil t)
2902 (if (not (pos-visible-in-window-p (point-max)))
2903 (move-to-window-line (+ (/ (1- (window-height)) 2) (1- val)))
2904 (setq lines (count-lines (window-start) (point-max)))
2905 (move-to-window-line (+ (/ lines 2) (1- val))))
2906
2907 ;; positioning is done twice: before and after command execution
2908 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2909 (back-to-indentation)
2910
2f3eb3b6 2911 (if com (viper-execute-com 'viper-window-middle val com))
d5e52f99
MK
2912
2913 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2914 (back-to-indentation)
2915 ))
2916
2f3eb3b6 2917(defun viper-window-bottom (arg)
d5e52f99
MK
2918 "Go to last window line."
2919 (interactive "P")
2f3eb3b6
MK
2920 (let ((val (viper-p-val arg))
2921 (com (viper-getCom arg)))
2922 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2923 (push-mark nil t)
2924 (move-to-window-line (- val))
2925
2926 ;; positioning is done twice: before and after command execution
2927 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2928 (back-to-indentation)
2929
2f3eb3b6 2930 (if com (viper-execute-com 'viper-window-bottom val com))
d5e52f99
MK
2931
2932 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
2933 (back-to-indentation)
2934 ))
2935
2f3eb3b6 2936(defun viper-line-to-top (arg)
d5e52f99
MK
2937 "Put current line on the home line."
2938 (interactive "p")
2939 (recenter (1- arg)))
2940
2f3eb3b6 2941(defun viper-line-to-middle (arg)
d5e52f99
MK
2942 "Put current line on the middle line."
2943 (interactive "p")
2944 (recenter (+ (1- arg) (/ (1- (window-height)) 2))))
2945
2f3eb3b6 2946(defun viper-line-to-bottom (arg)
d5e52f99
MK
2947 "Put current line on the last line."
2948 (interactive "p")
2949 (recenter (- (window-height) (1+ arg))))
2950
2f3eb3b6 2951;; If point is within viper-search-scroll-threshold of window top or bottom,
d5e52f99 2952;; scroll up or down 1/7 of window height, depending on whether we are at the
2f3eb3b6
MK
2953;; bottom or at the top of the window. This function is called by viper-search
2954;; (which is called from viper-search-forward/backward/next). If the value of
2955;; viper-search-scroll-threshold is negative - don't scroll.
2956(defun viper-adjust-window ()
2957 (let ((win-height (if viper-emacs-p
d5e52f99
MK
2958 (1- (window-height)) ; adjust for modeline
2959 (window-displayed-height)))
2960 (pt (point))
2961 at-top-p at-bottom-p
2962 min-scroll direction)
2963 (save-excursion
2964 (move-to-window-line 0) ; top
2965 (setq at-top-p
2966 (<= (count-lines pt (point))
2f3eb3b6 2967 viper-search-scroll-threshold))
d5e52f99
MK
2968 (move-to-window-line -1) ; bottom
2969 (setq at-bottom-p
2f3eb3b6 2970 (<= (count-lines pt (point)) viper-search-scroll-threshold))
d5e52f99 2971 )
2f3eb3b6 2972 (cond (at-top-p (setq min-scroll (1- viper-search-scroll-threshold)
d5e52f99 2973 direction 1))
2f3eb3b6 2974 (at-bottom-p (setq min-scroll (1+ viper-search-scroll-threshold)
d5e52f99
MK
2975 direction -1)))
2976 (if min-scroll
2977 (recenter
2978 (* (max min-scroll (/ win-height 7)) direction)))
2979 ))
2980
2981\f
2982;; paren match
2983;; must correct this to only match ( to ) etc. On the other hand
2984;; it is good that paren match gets confused, because that way you
2985;; catch _all_ imbalances.
2986
2f3eb3b6 2987(defun viper-paren-match (arg)
d5e52f99
MK
2988 "Go to the matching parenthesis."
2989 (interactive "P")
2f3eb3b6
MK
2990 (viper-leave-region-active)
2991 (let ((com (viper-getcom arg))
2992 (parse-sexp-ignore-comments viper-parse-sexp-ignore-comments)
d5e52f99
MK
2993 anchor-point)
2994 (if (integerp arg)
2995 (if (or (> arg 99) (< arg 1))
2996 (error "Prefix must be between 1 and 99")
2997 (goto-char
2998 (if (> (point-max) 80000)
2999 (* (/ (point-max) 100) arg)
3000 (/ (* (point-max) arg) 100)))
3001 (back-to-indentation))
3002 (let (beg-lim end-lim)
3003 (if (and (eolp) (not (bolp))) (forward-char -1))
3004 (if (not (looking-at "[][(){}]"))
3005 (setq anchor-point (point)))
3006 (save-excursion
3007 (beginning-of-line)
3008 (setq beg-lim (point))
3009 (end-of-line)
3010 (setq end-lim (point)))
3011 (cond ((re-search-forward "[][(){}]" end-lim t)
3012 (backward-char) )
3013 ((re-search-backward "[][(){}]" beg-lim t))
3014 (t
3015 (error "No matching character on line"))))
3016 (cond ((looking-at "[\(\[{]")
2f3eb3b6 3017 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
3018 (forward-sexp 1)
3019 (if com
2f3eb3b6 3020 (viper-execute-com 'viper-paren-match nil com)
d5e52f99
MK
3021 (backward-char)))
3022 (anchor-point
3023 (if com
3024 (progn
2f3eb3b6 3025 (viper-move-marker-locally 'viper-com-point anchor-point)
d5e52f99 3026 (forward-char 1)
2f3eb3b6 3027 (viper-execute-com 'viper-paren-match nil com)
d5e52f99
MK
3028 )))
3029 ((looking-at "[])}]")
3030 (forward-char)
2f3eb3b6 3031 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 3032 (backward-sexp 1)
2f3eb3b6 3033 (if com (viper-execute-com 'viper-paren-match nil com)))
d5e52f99
MK
3034 (t (error ""))))))
3035
2f3eb3b6 3036(defun viper-toggle-parse-sexp-ignore-comments ()
d5e52f99 3037 (interactive)
2f3eb3b6
MK
3038 (setq viper-parse-sexp-ignore-comments
3039 (not viper-parse-sexp-ignore-comments))
1e70790f
MK
3040 (princ (format
3041 "From now on, `%%' will %signore parentheses inside comment fields"
2f3eb3b6 3042 (if viper-parse-sexp-ignore-comments "" "NOT "))))
d5e52f99
MK
3043
3044\f
3045;; sentence ,paragraph and heading
3046
2f3eb3b6 3047(defun viper-forward-sentence (arg)
d5e52f99
MK
3048 "Forward sentence."
3049 (interactive "P")
3050 (push-mark nil t)
2f3eb3b6
MK
3051 (let ((val (viper-p-val arg))
3052 (com (viper-getcom arg)))
3053 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 3054 (forward-sentence val)
2f3eb3b6 3055 (if com (viper-execute-com 'viper-forward-sentence nil com))))
d5e52f99 3056
2f3eb3b6 3057(defun viper-backward-sentence (arg)
d5e52f99
MK
3058 "Backward sentence."
3059 (interactive "P")
3060 (push-mark nil t)
2f3eb3b6
MK
3061 (let ((val (viper-p-val arg))
3062 (com (viper-getcom arg)))
3063 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 3064 (backward-sentence val)
2f3eb3b6 3065 (if com (viper-execute-com 'viper-backward-sentence nil com))))
d5e52f99 3066
2f3eb3b6 3067(defun viper-forward-paragraph (arg)
d5e52f99
MK
3068 "Forward paragraph."
3069 (interactive "P")
3070 (push-mark nil t)
2f3eb3b6
MK
3071 (let ((val (viper-p-val arg))
3072 (com (viper-getCom arg)))
3073 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
3074 (forward-paragraph val)
3075 (if com
3076 (progn
3077 (backward-char 1)
2f3eb3b6 3078 (viper-execute-com 'viper-forward-paragraph nil com)))))
d5e52f99 3079
2f3eb3b6 3080(defun viper-backward-paragraph (arg)
d5e52f99
MK
3081 "Backward paragraph."
3082 (interactive "P")
3083 (push-mark nil t)
2f3eb3b6
MK
3084 (let ((val (viper-p-val arg))
3085 (com (viper-getCom arg)))
3086 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
3087 (backward-paragraph val)
3088 (if com
3089 (progn
3090 (forward-char 1)
2f3eb3b6 3091 (viper-execute-com 'viper-backward-paragraph nil com)
d5e52f99
MK
3092 (backward-char 1)))))
3093
3094;; should be mode-specific etc.
3095
2f3eb3b6 3096(defun viper-prev-heading (arg)
d5e52f99 3097 (interactive "P")
2f3eb3b6
MK
3098 (let ((val (viper-p-val arg))
3099 (com (viper-getCom arg)))
3100 (if com (viper-move-marker-locally 'viper-com-point (point)))
3101 (re-search-backward viper-heading-start nil t val)
d5e52f99 3102 (goto-char (match-beginning 0))
2f3eb3b6 3103 (if com (viper-execute-com 'viper-prev-heading nil com))))
d5e52f99 3104
2f3eb3b6 3105(defun viper-heading-end (arg)
d5e52f99 3106 (interactive "P")
2f3eb3b6
MK
3107 (let ((val (viper-p-val arg))
3108 (com (viper-getCom arg)))
3109 (if com (viper-move-marker-locally 'viper-com-point (point)))
3110 (re-search-forward viper-heading-end nil t val)
d5e52f99 3111 (goto-char (match-beginning 0))
2f3eb3b6 3112 (if com (viper-execute-com 'viper-heading-end nil com))))
d5e52f99 3113
2f3eb3b6 3114(defun viper-next-heading (arg)
d5e52f99 3115 (interactive "P")
2f3eb3b6
MK
3116 (let ((val (viper-p-val arg))
3117 (com (viper-getCom arg)))
3118 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 3119 (end-of-line)
2f3eb3b6 3120 (re-search-forward viper-heading-start nil t val)
d5e52f99 3121 (goto-char (match-beginning 0))
2f3eb3b6 3122 (if com (viper-execute-com 'viper-next-heading nil com))))
d5e52f99
MK
3123
3124\f
3125;; scrolling
3126
2f3eb3b6 3127(defun viper-scroll-screen (arg)
d5e52f99
MK
3128 "Scroll to next screen."
3129 (interactive "p")
3130 (condition-case nil
3131 (if (> arg 0)
3132 (while (> arg 0)
3133 (scroll-up)
3134 (setq arg (1- arg)))
3135 (while (> 0 arg)
3136 (scroll-down)
3137 (setq arg (1+ arg))))
3138 (error (beep 1)
3139 (if (> arg 0)
3140 (progn
3141 (message "End of buffer")
3142 (goto-char (point-max)))
3143 (message "Beginning of buffer")
3144 (goto-char (point-min))))
3145 ))
3146
2f3eb3b6 3147(defun viper-scroll-screen-back (arg)
d5e52f99
MK
3148 "Scroll to previous screen."
3149 (interactive "p")
2f3eb3b6 3150 (viper-scroll-screen (- arg)))
d5e52f99 3151
2f3eb3b6 3152(defun viper-scroll-down (arg)
d5e52f99
MK
3153 "Pull down half screen."
3154 (interactive "P")
3155 (condition-case nil
3156 (if (null arg)
3157 (scroll-down (/ (window-height) 2))
3158 (scroll-down arg))
3159 (error (beep 1)
3160 (message "Beginning of buffer")
3161 (goto-char (point-min)))))
3162
2f3eb3b6 3163(defun viper-scroll-down-one (arg)
d5e52f99
MK
3164 "Scroll up one line."
3165 (interactive "p")
3166 (scroll-down arg))
3167
2f3eb3b6 3168(defun viper-scroll-up (arg)
d5e52f99
MK
3169 "Pull up half screen."
3170 (interactive "P")
3171 (condition-case nil
3172 (if (null arg)
3173 (scroll-up (/ (window-height) 2))
3174 (scroll-up arg))
3175 (error (beep 1)
3176 (message "End of buffer")
3177 (goto-char (point-max)))))
3178
2f3eb3b6 3179(defun viper-scroll-up-one (arg)
d5e52f99
MK
3180 "Scroll down one line."
3181 (interactive "p")
3182 (scroll-up arg))
3183
3184\f
3185;; searching
3186
2f3eb3b6
MK
3187(defun viper-if-string (prompt)
3188 (if (memq viper-intermediate-command
3189 '(viper-command-argument viper-digit-argument viper-repeat))
3190 (setq viper-this-command-keys (this-command-keys)))
3191 (let ((s (viper-read-string-with-history
d5e52f99
MK
3192 prompt
3193 nil ; no initial
2f3eb3b6
MK
3194 'viper-search-history
3195 (car viper-search-history))))
d5e52f99 3196 (if (not (string= s ""))
2f3eb3b6 3197 (setq viper-s-string s))))
d5e52f99
MK
3198
3199
2f3eb3b6
MK
3200(defun viper-toggle-search-style (arg)
3201 "Toggle the value of viper-case-fold-search/viper-re-search.
d5e52f99 3202Without prefix argument, will ask which search style to toggle. With prefix
2f3eb3b6 3203arg 1,toggles viper-case-fold-search; with arg 2 toggles viper-re-search.
d5e52f99 3204
2f3eb3b6 3205Although this function is bound to \\[viper-toggle-search-style], the most
d5e52f99 3206convenient way to use it is to bind `//' to the macro
2f3eb3b6
MK
3207`1 M-x viper-toggle-search-style' and `///' to
3208`2 M-x viper-toggle-search-style'. In this way, hitting `//' quickly will
d5e52f99
MK
3209toggle case-fold-search and hitting `/' three times witth toggle regexp
3210search. Macros are more convenient in this case because they don't affect
3211the Emacs binding of `/'."
3212 (interactive "P")
3213 (let (msg)
3214 (cond ((or (eq arg 1)
3215 (and (null arg)
3216 (y-or-n-p (format "Search style: '%s'. Want '%s'? "
2f3eb3b6 3217 (if viper-case-fold-search
d5e52f99 3218 "case-insensitive" "case-sensitive")
2f3eb3b6 3219 (if viper-case-fold-search
d5e52f99
MK
3220 "case-sensitive"
3221 "case-insensitive")))))
2f3eb3b6
MK
3222 (setq viper-case-fold-search (null viper-case-fold-search))
3223 (if viper-case-fold-search
d5e52f99
MK
3224 (setq msg "Search becomes case-insensitive")
3225 (setq msg "Search becomes case-sensitive")))
3226 ((or (eq arg 2)
3227 (and (null arg)
3228 (y-or-n-p (format "Search style: '%s'. Want '%s'? "
2f3eb3b6 3229 (if viper-re-search
d5e52f99 3230 "regexp-search" "vanilla-search")
2f3eb3b6 3231 (if viper-re-search
d5e52f99
MK
3232 "vanilla-search"
3233 "regexp-search")))))
2f3eb3b6
MK
3234 (setq viper-re-search (null viper-re-search))
3235 (if viper-re-search
d5e52f99
MK
3236 (setq msg "Search becomes regexp-style")
3237 (setq msg "Search becomes vanilla-style")))
3238 (t
3239 (setq msg "Search style remains unchanged")))
1e70790f 3240 (princ msg t)))
d5e52f99 3241
2f3eb3b6 3242(defun viper-set-searchstyle-toggling-macros (unset)
d5e52f99
MK
3243 "Set the macros for toggling the search style in Viper's vi-state.
3244The macro that toggles case sensitivity is bound to `//', and the one that
3245toggles regexp search is bound to `///'.
3246With a prefix argument, this function unsets the macros. "
3247 (interactive "P")
3248 (or noninteractive
3249 (if (not unset)
3250 (progn
3251 ;; toggle case sensitivity in search
2f3eb3b6 3252 (viper-record-kbd-macro
d5e52f99 3253 "//" 'vi-state
2f3eb3b6 3254 [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
3255 't)
3256 ;; toggle regexp/vanila search
2f3eb3b6 3257 (viper-record-kbd-macro
d5e52f99 3258 "///" 'vi-state
2f3eb3b6 3259 [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
3260 't)
3261 (if (interactive-p)
3262 (message
1e70790f 3263 "// and /// now toggle case-sensitivity and regexp search")))
2f3eb3b6 3264 (viper-unrecord-kbd-macro "//" 'vi-state)
d5e52f99 3265 (sit-for 2)
2f3eb3b6 3266 (viper-unrecord-kbd-macro "///" 'vi-state))))
d5e52f99 3267
1e70790f 3268
2f3eb3b6 3269(defun viper-set-parsing-style-toggling-macro (unset)
1e70790f
MK
3270 "Set `%%%' to be a macro that toggles whether comment fields should be parsed for matching parentheses.
3271This is used in conjunction with the `%' command.
3272
3273With a prefix argument, unsets the macro."
3274 (interactive "P")
3275 (or noninteractive
3276 (if (not unset)
3277 (progn
3278 ;; Make %%% toggle parsing comments for matching parentheses
2f3eb3b6 3279 (viper-record-kbd-macro
1e70790f 3280 "%%%" 'vi-state
2f3eb3b6 3281 [(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
3282 't)
3283 (if (interactive-p)
3284 (message
3285 "%%%%%% now toggles whether comments should be parsed for matching parentheses")))
2f3eb3b6 3286 (viper-unrecord-kbd-macro "%%%" 'vi-state))))
1e70790f
MK
3287
3288
2f3eb3b6 3289(defun viper-set-emacs-state-searchstyle-macros (unset &optional arg-majormode)
d5e52f99
MK
3290 "Set the macros for toggling the search style in Viper's emacs-state.
3291The macro that toggles case sensitivity is bound to `//', and the one that
3292toggles regexp search is bound to `///'.
3293With a prefix argument, this function unsets the macros.
3294If the optional prefix argument is non-nil and specifies a valid major mode,
3295this sets the macros only in the macros in that major mode. Otherwise,
3296the macros are set in the current major mode.
3297\(When unsetting the macros, the second argument has no effect.\)"
3298 (interactive "P")
3299 (or noninteractive
3300 (if (not unset)
3301 (progn
3302 ;; toggle case sensitivity in search
2f3eb3b6 3303 (viper-record-kbd-macro
d5e52f99 3304 "//" 'emacs-state
2f3eb3b6 3305 [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
3306 (or arg-majormode major-mode))
3307 ;; toggle regexp/vanila search
2f3eb3b6 3308 (viper-record-kbd-macro
d5e52f99 3309 "///" 'emacs-state
2f3eb3b6 3310 [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
3311 (or arg-majormode major-mode))
3312 (if (interactive-p)
3313 (message
3314 "// and /// now toggle case-sensitivity and regexp search.")))
2f3eb3b6 3315 (viper-unrecord-kbd-macro "//" 'emacs-state)
d5e52f99 3316 (sit-for 2)
2f3eb3b6 3317 (viper-unrecord-kbd-macro "///" 'emacs-state))))
d5e52f99
MK
3318
3319
2f3eb3b6 3320(defun viper-search-forward (arg)
d5e52f99
MK
3321 "Search a string forward.
3322ARG is used to find the ARG's occurrence of the string.
3323Null string will repeat previous search."
3324 (interactive "P")
2f3eb3b6
MK
3325 (let ((val (viper-P-val arg))
3326 (com (viper-getcom arg))
3327 (old-str viper-s-string))
3328 (setq viper-s-forward t)
3329 (viper-if-string "/")
d5e52f99 3330 ;; this is not used at present, but may be used later
2f3eb3b6
MK
3331 (if (or (not (equal old-str viper-s-string))
3332 (not (markerp viper-local-search-start-marker))
3333 (not (marker-buffer viper-local-search-start-marker)))
3334 (setq viper-local-search-start-marker (point-marker)))
3335 (viper-search viper-s-string t val)
d5e52f99
MK
3336 (if com
3337 (progn
2f3eb3b6
MK
3338 (viper-move-marker-locally 'viper-com-point (mark t))
3339 (viper-execute-com 'viper-search-next val com)))))
d5e52f99 3340
2f3eb3b6 3341(defun viper-search-backward (arg)
d5e52f99
MK
3342 "Search a string backward.
3343ARG is used to find the ARG's occurrence of the string.
3344Null string will repeat previous search."
3345 (interactive "P")
2f3eb3b6
MK
3346 (let ((val (viper-P-val arg))
3347 (com (viper-getcom arg))
3348 (old-str viper-s-string))
3349 (setq viper-s-forward nil)
3350 (viper-if-string "?")
d5e52f99 3351 ;; this is not used at present, but may be used later
2f3eb3b6
MK
3352 (if (or (not (equal old-str viper-s-string))
3353 (not (markerp viper-local-search-start-marker))
3354 (not (marker-buffer viper-local-search-start-marker)))
3355 (setq viper-local-search-start-marker (point-marker)))
3356 (viper-search viper-s-string nil val)
d5e52f99
MK
3357 (if com
3358 (progn
2f3eb3b6
MK
3359 (viper-move-marker-locally 'viper-com-point (mark t))
3360 (viper-execute-com 'viper-search-next val com)))))
d5e52f99
MK
3361
3362
3363;; Search for COUNT's occurrence of STRING.
3364;; Search is forward if FORWARD is non-nil, otherwise backward.
3365;; INIT-POINT is the position where search is to start.
3366;; Arguments:
3367;; (STRING FORW COUNT &optional NO-OFFSET INIT-POINT LIMIT FAIL-IF-NOT-FOUND)
2f3eb3b6
MK
3368(defun viper-search (string forward arg
3369 &optional no-offset init-point fail-if-not-found)
d5e52f99 3370 (if (not (equal string ""))
2f3eb3b6
MK
3371 (let ((val (viper-p-val arg))
3372 (com (viper-getcom arg))
d5e52f99 3373 (offset (not no-offset))
2f3eb3b6 3374 (case-fold-search viper-case-fold-search)
d5e52f99 3375 (start-point (or init-point (point))))
2f3eb3b6 3376 (viper-deactivate-mark)
d5e52f99
MK
3377 (if forward
3378 (condition-case nil
3379 (progn
2f3eb3b6
MK
3380 (if offset (viper-forward-char-carefully))
3381 (if viper-re-search
d5e52f99
MK
3382 (progn
3383 (re-search-forward string nil nil val)
3384 (re-search-backward string))
3385 (search-forward string nil nil val)
3386 (search-backward string))
3387 (if (not (equal start-point (point)))
3388 (push-mark start-point t)))
3389 (search-failed
2f3eb3b6 3390 (if (and (not fail-if-not-found) viper-search-wrap-around-t)
d5e52f99
MK
3391 (progn
3392 (message "Search wrapped around BOTTOM of buffer")
3393 (goto-char (point-min))
2f3eb3b6 3394 (viper-search string forward (cons 1 com) t start-point 'fail)
d5e52f99 3395 ;; don't wait in macros
2f3eb3b6
MK
3396 (or executing-kbd-macro
3397 (memq viper-intermediate-command
3398 '(viper-repeat
3399 viper-digit-argument
3400 viper-command-argument))
3401 (sit-for 2))
d5e52f99
MK
3402 ;; delete the wrap-around message
3403 (message "")
3404 )
3405 (goto-char start-point)
3406 (error "`%s': %s not found"
3407 string
2f3eb3b6 3408 (if viper-re-search "Pattern" "String"))
d5e52f99
MK
3409 )))
3410 ;; backward
3411 (condition-case nil
3412 (progn
2f3eb3b6 3413 (if viper-re-search
d5e52f99
MK
3414 (re-search-backward string nil nil val)
3415 (search-backward string nil nil val))
3416 (if (not (equal start-point (point)))
3417 (push-mark start-point t)))
3418 (search-failed
2f3eb3b6 3419 (if (and (not fail-if-not-found) viper-search-wrap-around-t)
d5e52f99
MK
3420 (progn
3421 (message "Search wrapped around TOP of buffer")
3422 (goto-char (point-max))
2f3eb3b6 3423 (viper-search string forward (cons 1 com) t start-point 'fail)
d5e52f99 3424 ;; don't wait in macros
2f3eb3b6
MK
3425 (or executing-kbd-macro
3426 (memq viper-intermediate-command
3427 '(viper-repeat
3428 viper-digit-argument
3429 viper-command-argument))
3430 (sit-for 2))
d5e52f99
MK
3431 ;; delete the wrap-around message
3432 (message "")
3433 )
3434 (goto-char start-point)
3435 (error "`%s': %s not found"
3436 string
2f3eb3b6 3437 (if viper-re-search "Pattern" "String"))
d5e52f99
MK
3438 ))))
3439 ;; pull up or down if at top/bottom of window
2f3eb3b6 3440 (viper-adjust-window)
d5e52f99
MK
3441 ;; highlight the result of search
3442 ;; don't wait and don't highlight in macros
3443 (or executing-kbd-macro
2f3eb3b6
MK
3444 (memq viper-intermediate-command
3445 '(viper-repeat viper-digit-argument viper-command-argument))
3446 (viper-flash-search-pattern))
d5e52f99
MK
3447 )))
3448
2f3eb3b6 3449(defun viper-search-next (arg)
d5e52f99
MK
3450 "Repeat previous search."
3451 (interactive "P")
2f3eb3b6
MK
3452 (let ((val (viper-p-val arg))
3453 (com (viper-getcom arg)))
3454 (if (null viper-s-string) (error viper-NoPrevSearch))
3455 (viper-search viper-s-string viper-s-forward arg)
d5e52f99
MK
3456 (if com
3457 (progn
2f3eb3b6
MK
3458 (viper-move-marker-locally 'viper-com-point (mark t))
3459 (viper-execute-com 'viper-search-next val com)))))
d5e52f99 3460
2f3eb3b6 3461(defun viper-search-Next (arg)
d5e52f99
MK
3462 "Repeat previous search in the reverse direction."
3463 (interactive "P")
2f3eb3b6
MK
3464 (let ((val (viper-p-val arg))
3465 (com (viper-getcom arg)))
3466 (if (null viper-s-string) (error viper-NoPrevSearch))
3467 (viper-search viper-s-string (not viper-s-forward) arg)
d5e52f99
MK
3468 (if com
3469 (progn
2f3eb3b6
MK
3470 (viper-move-marker-locally 'viper-com-point (mark t))
3471 (viper-execute-com 'viper-search-Next val com)))))
d5e52f99
MK
3472
3473
3474;; Search contents of buffer defined by one of Viper's motion commands.
3475;; Repeatable via `n' and `N'.
2f3eb3b6
MK
3476(defun viper-buffer-search-enable (&optional c)
3477 (cond (c (setq viper-buffer-search-char c))
3478 ((null viper-buffer-search-char)
3479 (setq viper-buffer-search-char ?g)))
3480 (define-key viper-vi-basic-map
3481 (char-to-string viper-buffer-search-char) 'viper-command-argument)
3482 (aset viper-exec-array viper-buffer-search-char 'viper-exec-buffer-search)
3483 (setq viper-prefix-commands
3484 (cons viper-buffer-search-char viper-prefix-commands)))
d5e52f99
MK
3485
3486;; This is a Viper wraper for isearch-forward.
2f3eb3b6 3487(defun viper-isearch-forward (arg)
d5e52f99
MK
3488 "Do incremental search forward."
3489 (interactive "P")
3490 ;; emacs bug workaround
3491 (if (listp arg) (setq arg (car arg)))
2f3eb3b6 3492 (viper-exec-form-in-emacs (list 'isearch-forward arg)))
d5e52f99
MK
3493
3494;; This is a Viper wraper for isearch-backward."
2f3eb3b6 3495(defun viper-isearch-backward (arg)
d5e52f99
MK
3496 "Do incremental search backward."
3497 (interactive "P")
3498 ;; emacs bug workaround
3499 (if (listp arg) (setq arg (car arg)))
2f3eb3b6 3500 (viper-exec-form-in-emacs (list 'isearch-backward arg)))
d5e52f99
MK
3501
3502\f
3503;; visiting and killing files, buffers
3504
2f3eb3b6 3505(defun viper-switch-to-buffer ()
d5e52f99
MK
3506 "Switch to buffer in the current window."
3507 (interactive)
3508 (let (buffer)
3509 (setq buffer
3510 (read-buffer
3511 (format "Switch to buffer in this window \(%s\): "
3512 (buffer-name (other-buffer (current-buffer))))))
3513 (switch-to-buffer buffer)
3514 ))
3515
2f3eb3b6 3516(defun viper-switch-to-buffer-other-window ()
d5e52f99
MK
3517 "Switch to buffer in another window."
3518 (interactive)
3519 (let (buffer)
3520 (setq buffer
3521 (read-buffer
3522 (format "Switch to buffer in another window \(%s\): "
3523 (buffer-name (other-buffer (current-buffer))))))
3524 (switch-to-buffer-other-window buffer)
3525 ))
3526
2f3eb3b6 3527(defun viper-kill-buffer ()
d5e52f99
MK
3528 "Kill a buffer."
3529 (interactive)
3530 (let (buffer buffer-name)
3531 (setq buffer-name
3532 (read-buffer
3533 (format "Kill buffer \(%s\): "
3534 (buffer-name (current-buffer)))))
3535 (setq buffer
3536 (if (null buffer-name)
3537 (current-buffer)
3538 (get-buffer buffer-name)))
3539 (if (null buffer) (error "`%s': No such buffer" buffer-name))
3540 (if (or (not (buffer-modified-p buffer))
3541 (y-or-n-p
3542 (format
3543 "Buffer `%s' is modified, are you sure you want to kill it? "
3544 buffer-name)))
3545 (kill-buffer buffer)
3546 (error "Buffer not killed"))))
3547
3548
2f3eb3b6 3549(defcustom viper-smart-suffix-list
d5e52f99
MK
3550 '("" "tex" "c" "cc" "C" "el" "java" "html" "htm" "pl" "P" "p")
3551 "*List of suffixes that Viper automatically tries to append to filenames ending with a `.'.
3552This is useful when you the current directory contains files with the same
3553prefix and many different suffixes. Usually, only one of the suffixes
3554represents an editable file. However, file completion will stop at the `.'
3555The smart suffix feature lets you hit RET in such a case, and Viper will
3556select the appropriate suffix.
3557
3558Suffixes are tried in the order given and the first suffix for which a
3559corresponding file exists is selected. If no file exists for any of the
3560suffixes, the user is asked to confirm.
3561
1e70790f
MK
3562To turn this feature off, set this variable to nil."
3563 :type '(set string)
3564 :group 'viper)
d5e52f99
MK
3565
3566;; Try to add suffix to files ending with a `.'
3567;; Useful when the user hits RET on a non-completed file name.
2f3eb3b6 3568(defun viper-file-add-suffix ()
d5e52f99 3569 (let ((count 0)
2f3eb3b6 3570 (len (length viper-smart-suffix-list))
d5e52f99
MK
3571 (file (buffer-string))
3572 found key cmd suff)
3573 (goto-char (point-max))
2f3eb3b6 3574 (if (and viper-smart-suffix-list (string-match "\\.$" file))
d5e52f99
MK
3575 (progn
3576 (while (and (not found) (< count len))
2f3eb3b6 3577 (setq suff (nth count viper-smart-suffix-list)
d5e52f99
MK
3578 count (1+ count))
3579 (if (file-exists-p (format "%s%s" file suff))
3580 (progn
3581 (setq found t)
3582 (insert suff))))
2f3eb3b6 3583
d5e52f99
MK
3584 (if found
3585 ()
2f3eb3b6 3586 (viper-tmp-insert-at-eob " [Please complete file name]")
d5e52f99 3587 (unwind-protect
2f3eb3b6
MK
3588 (while (not (memq cmd
3589 '(exit-minibuffer viper-exit-minibuffer)))
d5e52f99
MK
3590 (setq cmd
3591 (key-binding (setq key (read-key-sequence nil))))
3592 (cond ((eq cmd 'self-insert-command)
2f3eb3b6 3593 (if viper-xemacs-p
d5e52f99
MK
3594 (insert (events-to-keys key))
3595 (insert key)))
2f3eb3b6 3596 ((memq cmd '(exit-minibuffer viper-exit-minibuffer))
d5e52f99
MK
3597 nil)
3598 (t (command-execute cmd)))
3599 )))
2f3eb3b6 3600 ))))
d5e52f99
MK
3601
3602
d5e52f99
MK
3603
3604\f
3605;; yank and pop
3606
2f3eb3b6 3607(defsubst viper-yank (text)
d5e52f99
MK
3608 "Yank TEXT silently. This works correctly with Emacs's yank-pop command."
3609 (insert text)
3610 (setq this-command 'yank))
3611
2f3eb3b6 3612(defun viper-put-back (arg)
d5e52f99
MK
3613 "Put back after point/below line."
3614 (interactive "P")
2f3eb3b6
MK
3615 (let ((val (viper-p-val arg))
3616 (text (if viper-use-register
3617 (cond ((viper-valid-register viper-use-register '(digit))
3618 (current-kill
3619 (- viper-use-register ?1) 'do-not-rotate))
3620 ((viper-valid-register viper-use-register)
3621 (get-register (downcase viper-use-register)))
3622 (t (error viper-InvalidRegister viper-use-register)))
d5e52f99
MK
3623 (current-kill 0))))
3624 (if (null text)
2f3eb3b6
MK
3625 (if viper-use-register
3626 (let ((reg viper-use-register))
3627 (setq viper-use-register nil)
3628 (error viper-EmptyRegister reg))
d5e52f99 3629 (error "")))
2f3eb3b6
MK
3630 (setq viper-use-register nil)
3631 (if (viper-end-with-a-newline-p text)
d5e52f99
MK
3632 (progn
3633 (end-of-line)
3634 (if (eobp)
3635 (insert "\n")
3636 (forward-line 1))
3637 (beginning-of-line))
2f3eb3b6
MK
3638 (if (not (eolp)) (viper-forward-char-carefully)))
3639 (set-marker (viper-mark-marker) (point) (current-buffer))
3640 (viper-set-destructive-command
3641 (list 'viper-put-back val nil viper-use-register nil nil))
3642 (viper-loop val (viper-yank text)))
d5e52f99
MK
3643 ;; Vi puts cursor on the last char when the yanked text doesn't contain a
3644 ;; newline; it leaves the cursor at the beginning when the text contains
3645 ;; a newline
2f3eb3b6
MK
3646 (if (viper-same-line (point) (mark))
3647 (or (= (point) (mark)) (viper-backward-char-carefully))
d5e52f99
MK
3648 (exchange-point-and-mark)
3649 (if (bolp)
3650 (back-to-indentation)))
2f3eb3b6 3651 (viper-deactivate-mark))
d5e52f99 3652
2f3eb3b6 3653(defun viper-Put-back (arg)
d5e52f99
MK
3654 "Put back at point/above line."
3655 (interactive "P")
2f3eb3b6
MK
3656 (let ((val (viper-p-val arg))
3657 (text (if viper-use-register
3658 (cond ((viper-valid-register viper-use-register '(digit))
3659 (current-kill
3660 (- viper-use-register ?1) 'do-not-rotate))
3661 ((viper-valid-register viper-use-register)
3662 (get-register (downcase viper-use-register)))
3663 (t (error viper-InvalidRegister viper-use-register)))
d5e52f99
MK
3664 (current-kill 0))))
3665 (if (null text)
2f3eb3b6
MK
3666 (if viper-use-register
3667 (let ((reg viper-use-register))
3668 (setq viper-use-register nil)
3669 (error viper-EmptyRegister reg))
d5e52f99 3670 (error "")))
2f3eb3b6
MK
3671 (setq viper-use-register nil)
3672 (if (viper-end-with-a-newline-p text) (beginning-of-line))
3673 (viper-set-destructive-command
3674 (list 'viper-Put-back val nil viper-use-register nil nil))
3675 (set-marker (viper-mark-marker) (point) (current-buffer))
3676 (viper-loop val (viper-yank text)))
d5e52f99
MK
3677 ;; Vi puts cursor on the last char when the yanked text doesn't contain a
3678 ;; newline; it leaves the cursor at the beginning when the text contains
3679 ;; a newline
2f3eb3b6
MK
3680 (if (viper-same-line (point) (mark))
3681 (or (= (point) (mark)) (viper-backward-char-carefully))
d5e52f99
MK
3682 (exchange-point-and-mark)
3683 (if (bolp)
3684 (back-to-indentation)))
2f3eb3b6 3685 (viper-deactivate-mark))
d5e52f99
MK
3686
3687
3688;; Copy region to kill-ring.
3689;; If BEG and END do not belong to the same buffer, copy empty region.
2f3eb3b6 3690(defun viper-copy-region-as-kill (beg end)
d5e52f99
MK
3691 (condition-case nil
3692 (copy-region-as-kill beg end)
3693 (error (copy-region-as-kill beg beg))))
3694
3695
2f3eb3b6 3696(defun viper-delete-char (arg)
d5e52f99
MK
3697 "Delete character."
3698 (interactive "P")
2f3eb3b6
MK
3699 (let ((val (viper-p-val arg)))
3700 (viper-set-destructive-command
3701 (list 'viper-delete-char val nil nil nil nil))
d5e52f99
MK
3702 (if (> val 1)
3703 (save-excursion
3704 (let ((here (point)))
3705 (end-of-line)
3706 (if (> val (- (point) here))
3707 (setq val (- (point) here))))))
2f3eb3b6
MK
3708 (if (and (eq val 0) (not viper-ex-style-motion)) (setq val 1))
3709 (if (and viper-ex-style-motion (eolp))
d5e52f99 3710 (if (bolp) (error "") (setq val 0))) ; not bol---simply back 1 ch
2f3eb3b6 3711 (if viper-use-register
d5e52f99 3712 (progn
2f3eb3b6
MK
3713 (cond ((viper-valid-register viper-use-register '((Letter)))
3714 (viper-append-to-register
3715 (downcase viper-use-register) (point) (- (point) val)))
3716 ((viper-valid-register viper-use-register)
d5e52f99 3717 (copy-to-register
2f3eb3b6
MK
3718 viper-use-register (point) (- (point) val) nil))
3719 (t (error viper-InvalidRegister viper-use-register)))
3720 (setq viper-use-register nil)))
3721 (if viper-ex-style-motion
d5e52f99
MK
3722 (progn
3723 (delete-char val t)
3724 (if (and (eolp) (not (bolp))) (backward-char 1)))
3725 (if (eolp)
3726 (delete-backward-char val t)
3727 (delete-char val t)))))
3728
2f3eb3b6 3729(defun viper-delete-backward-char (arg)
d5e52f99
MK
3730 "Delete previous character. On reaching beginning of line, stop and beep."
3731 (interactive "P")
2f3eb3b6
MK
3732 (let ((val (viper-p-val arg)))
3733 (viper-set-destructive-command
3734 (list 'viper-delete-backward-char val nil nil nil nil))
d5e52f99
MK
3735 (if (> val 1)
3736 (save-excursion
3737 (let ((here (point)))
3738 (beginning-of-line)
3739 (if (> val (- here (point)))
3740 (setq val (- here (point)))))))
2f3eb3b6 3741 (if viper-use-register
d5e52f99 3742 (progn
2f3eb3b6
MK
3743 (cond ((viper-valid-register viper-use-register '(Letter))
3744 (viper-append-to-register
3745 (downcase viper-use-register) (point) (+ (point) val)))
3746 ((viper-valid-register viper-use-register)
d5e52f99 3747 (copy-to-register
2f3eb3b6
MK
3748 viper-use-register (point) (+ (point) val) nil))
3749 (t (error viper-InvalidRegister viper-use-register)))
3750 (setq viper-use-register nil)))
d5e52f99
MK
3751 (if (bolp) (ding)
3752 (delete-backward-char val t))))
3753
2f3eb3b6 3754(defun viper-del-backward-char-in-insert ()
d5e52f99
MK
3755 "Delete 1 char backwards while in insert mode."
3756 (interactive)
2f3eb3b6 3757 (if (and viper-ex-style-editing-in-insert (bolp))
d5e52f99
MK
3758 (beep 1)
3759 (delete-backward-char 1 t)))
3760
2f3eb3b6 3761(defun viper-del-backward-char-in-replace ()
d5e52f99 3762 "Delete one character in replace mode.
2f3eb3b6 3763If `viper-delete-backwards-in-replace' is t, then DEL key actually deletes
d5e52f99 3764charecters. If it is nil, then the cursor just moves backwards, similarly
2f3eb3b6 3765to Vi. The variable `viper-ex-style-editing-in-insert', if t, doesn't let the
d5e52f99
MK
3766cursor move past the beginning of line."
3767 (interactive)
2f3eb3b6 3768 (cond (viper-delete-backwards-in-replace
d5e52f99
MK
3769 (cond ((not (bolp))
3770 (delete-backward-char 1 t))
2f3eb3b6 3771 (viper-ex-style-editing-in-insert
d5e52f99
MK
3772 (beep 1))
3773 ((bobp)
3774 (beep 1))
3775 (t
3776 (delete-backward-char 1 t))))
2f3eb3b6 3777 (viper-ex-style-editing-in-insert
d5e52f99
MK
3778 (if (bolp)
3779 (beep 1)
3780 (backward-char 1)))
3781 (t
3782 (backward-char 1))))
3783
3784
3785\f
3786;; join lines.
3787
2f3eb3b6 3788(defun viper-join-lines (arg)
d5e52f99
MK
3789 "Join this line to next, if ARG is nil. Otherwise, join ARG lines."
3790 (interactive "*P")
2f3eb3b6
MK
3791 (let ((val (viper-P-val arg)))
3792 (viper-set-destructive-command
3793 (list 'viper-join-lines val nil nil nil nil))
3794 (viper-loop (if (null val) 1 (1- val))
d5e52f99
MK
3795 (progn
3796 (end-of-line)
3797 (if (not (eobp))
3798 (progn
3799 (forward-line 1)
3800 (delete-region (point) (1- (point)))
1e70790f
MK
3801 (fixup-whitespace)
3802 ;; fixup-whitespace sometimes does not leave space
3803 ;; between objects, so we insert it as in Vi
3804 (or (looking-at " ")
3805 (insert " ")
3806 (backward-char 1))
3807 ))))))
d5e52f99
MK
3808
3809\f
3810;; Replace state
3811
2f3eb3b6 3812(defun viper-change (beg end)
d5e52f99
MK
3813 (if (markerp beg) (setq beg (marker-position beg)))
3814 (if (markerp end) (setq end (marker-position end)))
3815 ;; beg is sometimes (mark t), which may be nil
3816 (or beg (setq beg end))
3817
2f3eb3b6
MK
3818 (viper-set-complex-command-for-undo)
3819 (if viper-use-register
d5e52f99 3820 (progn
2f3eb3b6
MK
3821 (copy-to-register viper-use-register beg end nil)
3822 (setq viper-use-register nil)))
3823 (viper-set-replace-overlay beg end)
d5e52f99
MK
3824 (setq last-command nil) ; separate repl text from prev kills
3825
2f3eb3b6 3826 (if (= (viper-replace-start) (point-max))
d5e52f99
MK
3827 (error "End of buffer"))
3828
2f3eb3b6
MK
3829 (setq viper-last-replace-region
3830 (buffer-substring (viper-replace-start)
3831 (viper-replace-end)))
d5e52f99
MK
3832
3833 ;; protect against error while inserting "@" and other disasters
3834 ;; (e.g., read-only buff)
3835 (condition-case conds
2f3eb3b6
MK
3836 (if (or viper-allow-multiline-replace-regions
3837 (viper-same-line (viper-replace-start)
3838 (viper-replace-end)))
d5e52f99
MK
3839 (progn
3840 ;; tabs cause problems in replace, so untabify
2f3eb3b6 3841 (goto-char (viper-replace-end))
d5e52f99 3842 (insert-before-markers "@") ; put placeholder after the TAB
2f3eb3b6 3843 (untabify (viper-replace-start) (point))
d5e52f99
MK
3844 ;; del @, don't put on kill ring
3845 (delete-backward-char 1)
3846
2f3eb3b6
MK
3847 (viper-set-replace-overlay-glyphs
3848 viper-replace-region-start-delimiter
3849 viper-replace-region-end-delimiter)
d5e52f99
MK
3850 ;; this move takes care of the last posn in the overlay, which
3851 ;; has to be shifted because of insert. We can't simply insert
3852 ;; "$" before-markers because then overlay-start will shift the
3853 ;; beginning of the overlay in case we are replacing a single
3854 ;; character. This fixes the bug with `s' and `cl' commands.
2f3eb3b6
MK
3855 (viper-move-replace-overlay (viper-replace-start) (point))
3856 (goto-char (viper-replace-start))
3857 (viper-change-state-to-replace t))
3858 (kill-region (viper-replace-start)
3859 (viper-replace-end))
3860 (viper-hide-replace-overlay)
3861 (viper-change-state-to-insert))
d5e52f99
MK
3862 (error ;; make sure that the overlay doesn't stay.
3863 ;; go back to the original point
2f3eb3b6
MK
3864 (goto-char (viper-replace-start))
3865 (viper-hide-replace-overlay)
3866 (viper-message-conditions conds))))
d5e52f99
MK
3867
3868
2f3eb3b6 3869(defun viper-change-subr (beg end)
d5e52f99
MK
3870 ;; beg is sometimes (mark t), which may be nil
3871 (or beg (setq beg end))
2f3eb3b6 3872 (if viper-use-register
d5e52f99 3873 (progn
2f3eb3b6
MK
3874 (copy-to-register viper-use-register beg end nil)
3875 (setq viper-use-register nil)))
d5e52f99 3876 (kill-region beg end)
2f3eb3b6
MK
3877 (setq this-command 'viper-change)
3878 (viper-yank-last-insertion))
d5e52f99 3879
2f3eb3b6 3880(defun viper-toggle-case (arg)
d5e52f99
MK
3881 "Toggle character case."
3882 (interactive "P")
2f3eb3b6
MK
3883 (let ((val (viper-p-val arg)) (c))
3884 (viper-set-destructive-command
3885 (list 'viper-toggle-case val nil nil nil nil))
d5e52f99
MK
3886 (while (> val 0)
3887 (setq c (following-char))
3888 (delete-char 1 nil)
3889 (if (eq c (upcase c))
3890 (insert-char (downcase c) 1)
3891 (insert-char (upcase c) 1))
3892 (if (eolp) (backward-char 1))
3893 (setq val (1- val)))))
3894
3895\f
3896;; query replace
3897
2f3eb3b6 3898(defun viper-query-replace ()
d5e52f99
MK
3899 "Query replace.
3900If a null string is suplied as the string to be replaced,
3901the query replace mode will toggle between string replace
3902and regexp replace."
3903 (interactive)
3904 (let (str)
2f3eb3b6
MK
3905 (setq str (viper-read-string-with-history
3906 (if viper-re-query-replace "Query replace regexp: "
d5e52f99
MK
3907 "Query replace: ")
3908 nil ; no initial
2f3eb3b6
MK
3909 'viper-replace1-history
3910 (car viper-replace1-history) ; default
d5e52f99
MK
3911 ))
3912 (if (string= str "")
3913 (progn
2f3eb3b6 3914 (setq viper-re-query-replace (not viper-re-query-replace))
d5e52f99 3915 (message "Query replace mode changed to %s"
2f3eb3b6 3916 (if viper-re-query-replace "regexp replace"
d5e52f99 3917 "string replace")))
2f3eb3b6 3918 (if viper-re-query-replace
d5e52f99
MK
3919 (query-replace-regexp
3920 str
2f3eb3b6 3921 (viper-read-string-with-history
d5e52f99
MK
3922 (format "Query replace regexp `%s' with: " str)
3923 nil ; no initial
2f3eb3b6
MK
3924 'viper-replace1-history
3925 (car viper-replace1-history) ; default
d5e52f99
MK
3926 ))
3927 (query-replace
3928 str
2f3eb3b6 3929 (viper-read-string-with-history
d5e52f99
MK
3930 (format "Query replace `%s' with: " str)
3931 nil ; no initial
2f3eb3b6
MK
3932 'viper-replace1-history
3933 (car viper-replace1-history) ; default
d5e52f99
MK
3934 ))))))
3935
3936\f
3937;; marking
3938
2f3eb3b6 3939(defun viper-mark-beginning-of-buffer ()
d5e52f99
MK
3940 "Mark beginning of buffer."
3941 (interactive)
3942 (push-mark (point))
3943 (goto-char (point-min))
3944 (exchange-point-and-mark)
3945 (message "Mark set at the beginning of buffer"))
3946
2f3eb3b6 3947(defun viper-mark-end-of-buffer ()
d5e52f99
MK
3948 "Mark end of buffer."
3949 (interactive)
3950 (push-mark (point))
3951 (goto-char (point-max))
3952 (exchange-point-and-mark)
3953 (message "Mark set at the end of buffer"))
3954
2f3eb3b6 3955(defun viper-mark-point ()
d5e52f99
MK
3956 "Set mark at point of buffer."
3957 (interactive)
3958 (let ((char (read-char)))
3959 (cond ((and (<= ?a char) (<= char ?z))
3960 (point-to-register (1+ (- char ?a))))
2f3eb3b6
MK
3961 ((= char ?<) (viper-mark-beginning-of-buffer))
3962 ((= char ?>) (viper-mark-end-of-buffer))
3963 ((= char ?.) (viper-set-mark-if-necessary))
3964 ((= char ?,) (viper-cycle-through-mark-ring))
d5e52f99
MK
3965 ((= char ?D) (mark-defun))
3966 (t (error ""))
3967 )))
3968
3969;; Algorithm: If first invocation of this command save mark on ring, goto
3970;; mark, M0, and pop the most recent elt from the mark ring into mark,
3971;; making it into the new mark, M1.
3972;; Push this mark back and set mark to the original point position, p1.
3973;; So, if you hit '' or `` then you can return to p1.
3974;;
3975;; If repeated command, pop top elt from the ring into mark and
3976;; jump there. This forgets the position, p1, and puts M1 back into mark.
3977;; Then we save the current pos, which is M0, jump to M1 and pop M2 from
3978;; the ring into mark. Push M2 back on the ring and set mark to M0.
3979;; etc.
2f3eb3b6 3980(defun viper-cycle-through-mark-ring ()
d5e52f99
MK
3981 "Visit previous locations on the mark ring.
3982One can use `` and '' to temporarily jump 1 step back."
3983 (let* ((sv-pt (point)))
3984 ;; if repeated `m,' command, pop the previously saved mark.
3985 ;; Prev saved mark is actually prev saved point. It is used if the
3986 ;; user types `` or '' and is discarded
3987 ;; from the mark ring by the next `m,' command.
3988 ;; In any case, go to the previous or previously saved mark.
3989 ;; Then push the current mark (popped off the ring) and set current
3990 ;; point to be the mark. Current pt as mark is discarded by the next
3991 ;; m, command.
2f3eb3b6 3992 (if (eq last-command 'viper-cycle-through-mark-ring)
d5e52f99
MK
3993 ()
3994 ;; save current mark if the first iteration
2f3eb3b6 3995 (setq mark-ring (delete (viper-mark-marker) mark-ring))
d5e52f99
MK
3996 (if (mark t)
3997 (push-mark (mark t) t)) )
3998 (pop-mark)
3999 (set-mark-command 1)
4000 ;; don't duplicate mark on the ring
2f3eb3b6 4001 (setq mark-ring (delete (viper-mark-marker) mark-ring))
d5e52f99 4002 (push-mark sv-pt t)
2f3eb3b6
MK
4003 (viper-deactivate-mark)
4004 (setq this-command 'viper-cycle-through-mark-ring)
d5e52f99
MK
4005 ))
4006
4007
2f3eb3b6 4008(defun viper-goto-mark (arg)
d5e52f99
MK
4009 "Go to mark."
4010 (interactive "P")
4011 (let ((char (read-char))
2f3eb3b6
MK
4012 (com (viper-getcom arg)))
4013 (viper-goto-mark-subr char com nil)))
d5e52f99 4014
2f3eb3b6 4015(defun viper-goto-mark-and-skip-white (arg)
d5e52f99
MK
4016 "Go to mark and skip to first non-white character on line."
4017 (interactive "P")
4018 (let ((char (read-char))
2f3eb3b6
MK
4019 (com (viper-getCom arg)))
4020 (viper-goto-mark-subr char com t)))
d5e52f99 4021
2f3eb3b6 4022(defun viper-goto-mark-subr (char com skip-white)
d5e52f99
MK
4023 (if (eobp)
4024 (if (bobp)
4025 (error "Empty buffer")
4026 (backward-char 1)))
2f3eb3b6 4027 (cond ((viper-valid-register char '(letter))
d5e52f99
MK
4028 (let* ((buff (current-buffer))
4029 (reg (1+ (- char ?a)))
4030 (text-marker (get-register reg)))
2f3eb3b6
MK
4031 (if com (viper-move-marker-locally 'viper-com-point (point)))
4032 (if (not (viper-valid-marker text-marker))
4033 (error viper-EmptyTextmarker char))
4034 (if (and (viper-same-line (point) viper-last-jump)
4035 (= (point) viper-last-jump-ignore))
4036 (push-mark viper-last-jump t)
d5e52f99 4037 (push-mark nil t)) ; no msg
2f3eb3b6
MK
4038 (viper-register-to-point reg)
4039 (setq viper-last-jump (point-marker))
d5e52f99
MK
4040 (cond (skip-white
4041 (back-to-indentation)
2f3eb3b6 4042 (setq viper-last-jump-ignore (point))))
d5e52f99
MK
4043 (if com
4044 (if (equal buff (current-buffer))
2f3eb3b6
MK
4045 (viper-execute-com (if skip-white
4046 'viper-goto-mark-and-skip-white
4047 'viper-goto-mark)
d5e52f99
MK
4048 nil com)
4049 (switch-to-buffer buff)
2f3eb3b6
MK
4050 (goto-char viper-com-point)
4051 (viper-change-state-to-vi)
d5e52f99
MK
4052 (error "")))))
4053 ((and (not skip-white) (= char ?`))
2f3eb3b6
MK
4054 (if com (viper-move-marker-locally 'viper-com-point (point)))
4055 (if (and (viper-same-line (point) viper-last-jump)
4056 (= (point) viper-last-jump-ignore))
4057 (goto-char viper-last-jump))
d5e52f99
MK
4058 (if (null (mark t)) (error "Mark is not set in this buffer"))
4059 (if (= (point) (mark t)) (pop-mark))
4060 (exchange-point-and-mark)
2f3eb3b6
MK
4061 (setq viper-last-jump (point-marker)
4062 viper-last-jump-ignore 0)
4063 (if com (viper-execute-com 'viper-goto-mark nil com)))
d5e52f99 4064 ((and skip-white (= char ?'))
2f3eb3b6
MK
4065 (if com (viper-move-marker-locally 'viper-com-point (point)))
4066 (if (and (viper-same-line (point) viper-last-jump)
4067 (= (point) viper-last-jump-ignore))
4068 (goto-char viper-last-jump))
d5e52f99
MK
4069 (if (= (point) (mark t)) (pop-mark))
4070 (exchange-point-and-mark)
2f3eb3b6 4071 (setq viper-last-jump (point))
d5e52f99 4072 (back-to-indentation)
2f3eb3b6
MK
4073 (setq viper-last-jump-ignore (point))
4074 (if com (viper-execute-com 'viper-goto-mark-and-skip-white nil com)))
4075 (t (error viper-InvalidTextmarker char))))
d5e52f99 4076
2f3eb3b6 4077(defun viper-insert-tab ()
d5e52f99
MK
4078 (interactive)
4079 (insert-tab))
4080
2f3eb3b6 4081(defun viper-exchange-point-and-mark ()
d5e52f99
MK
4082 (interactive)
4083 (exchange-point-and-mark)
4084 (back-to-indentation))
4085
4086;; Input Mode Indentation
4087
4088;; Returns t, if the string before point matches the regexp STR.
2f3eb3b6 4089(defsubst viper-looking-back (str)
d5e52f99
MK
4090 (and (save-excursion (re-search-backward str nil t))
4091 (= (point) (match-end 0))))
4092
4093
2f3eb3b6 4094(defun viper-forward-indent ()
d5e52f99
MK
4095 "Indent forward -- `C-t' in Vi."
4096 (interactive)
2f3eb3b6
MK
4097 (setq viper-cted t)
4098 (indent-to (+ (current-column) viper-shift-width)))
d5e52f99 4099
2f3eb3b6 4100(defun viper-backward-indent ()
d5e52f99
MK
4101 "Backtab, C-d in VI"
4102 (interactive)
2f3eb3b6 4103 (if viper-cted
d5e52f99 4104 (let ((p (point)) (c (current-column)) bol (indent t))
2f3eb3b6 4105 (if (viper-looking-back "[0^]")
d5e52f99
MK
4106 (progn
4107 (if (eq ?^ (preceding-char))
2f3eb3b6 4108 (setq viper-preserve-indent t))
d5e52f99
MK
4109 (delete-backward-char 1)
4110 (setq p (point))
4111 (setq indent nil)))
4112 (save-excursion
4113 (beginning-of-line)
4114 (setq bol (point)))
4115 (if (re-search-backward "[^ \t]" bol 1) (forward-char))
4116 (delete-region (point) p)
4117 (if indent
2f3eb3b6
MK
4118 (indent-to (- c viper-shift-width)))
4119 (if (or (bolp) (viper-looking-back "[^ \t]"))
4120 (setq viper-cted nil)))))
d5e52f99 4121
2f3eb3b6 4122(defun viper-autoindent ()
d5e52f99
MK
4123 "Auto Indentation, Vi-style."
4124 (interactive)
4125 (let ((col (current-indentation)))
4126 (if abbrev-mode (expand-abbrev))
2f3eb3b6
MK
4127 (if viper-preserve-indent
4128 (setq viper-preserve-indent nil)
4129 (setq viper-current-indent col))
d5e52f99
MK
4130 ;; don't leave whitespace lines around
4131 (if (memq last-command
2f3eb3b6
MK
4132 '(viper-autoindent
4133 viper-open-line viper-Open-line
4134 viper-replace-state-exit-cmd))
d5e52f99
MK
4135 (indent-to-left-margin))
4136 ;; use \n instead of newline, or else <Return> will move the insert point
4137 ;;(newline 1)
4138 (insert "\n")
2f3eb3b6 4139 (if viper-auto-indent
d5e52f99 4140 (progn
2f3eb3b6
MK
4141 (setq viper-cted t)
4142 (if (and viper-electric-mode
4143 (not
4144 (memq major-mode '(fundamental-mode
4145 text-mode
4146 paragraph-indent-text-mode ))))
d5e52f99 4147 (indent-according-to-mode)
2f3eb3b6 4148 (indent-to viper-current-indent))
d5e52f99
MK
4149 ))
4150 ))
4151
4152
4153;; Viewing registers
4154
2f3eb3b6 4155(defun viper-ket-function (arg)
d5e52f99
MK
4156 "Function called by \], the ket. View registers and call \]\]."
4157 (interactive "P")
4158 (let ((reg (read-char)))
2f3eb3b6 4159 (cond ((viper-valid-register reg '(letter Letter))
d5e52f99 4160 (view-register (downcase reg)))
2f3eb3b6 4161 ((viper-valid-register reg '(digit))
d5e52f99
MK
4162 (let ((text (current-kill (- reg ?1) 'do-not-rotate)))
4163 (save-excursion
4164 (set-buffer (get-buffer-create "*Output*"))
4165 (delete-region (point-min) (point-max))
4166 (insert (format "Register %c contains the string:\n" reg))
4167 (insert text)
4168 (goto-char (point-min)))
4169 (display-buffer "*Output*")))
4170 ((= ?\] reg)
2f3eb3b6 4171 (viper-next-heading arg))
d5e52f99 4172 (t (error
2f3eb3b6 4173 viper-InvalidRegister reg)))))
d5e52f99 4174
2f3eb3b6 4175(defun viper-brac-function (arg)
d5e52f99
MK
4176 "Function called by \[, the brac. View textmarkers and call \[\["
4177 (interactive "P")
4178 (let ((reg (read-char)))
4179 (cond ((= ?\[ reg)
2f3eb3b6 4180 (viper-prev-heading arg))
d5e52f99 4181 ((= ?\] reg)
2f3eb3b6
MK
4182 (viper-heading-end arg))
4183 ((viper-valid-register reg '(letter))
d5e52f99
MK
4184 (let* ((val (get-register (1+ (- reg ?a))))
4185 (buf (if (not val)
2f3eb3b6 4186 (error viper-EmptyTextmarker reg)
d5e52f99
MK
4187 (marker-buffer val)))
4188 (pos (marker-position val))
4189 line-no text (s pos) (e pos))
4190 (save-excursion
4191 (set-buffer (get-buffer-create "*Output*"))
4192 (delete-region (point-min) (point-max))
4193 (if (and buf pos)
4194 (progn
4195 (save-excursion
4196 (set-buffer buf)
4197 (setq line-no (1+ (count-lines (point-min) val)))
4198 (goto-char pos)
4199 (beginning-of-line)
4200 (if (re-search-backward "[^ \t]" nil t)
4201 (progn
4202 (beginning-of-line)
4203 (setq s (point))))
4204 (goto-char pos)
4205 (forward-line 1)
4206 (if (re-search-forward "[^ \t]" nil t)
4207 (progn
4208 (end-of-line)
4209 (setq e (point))))
4210 (setq text (buffer-substring s e))
4211 (setq text (format "%s<%c>%s"
4212 (substring text 0 (- pos s))
4213 reg (substring text (- pos s)))))
4214 (insert
4215 (format
4216 "Textmarker `%c' is in buffer `%s' at line %d.\n"
4217 reg (buffer-name buf) line-no))
4218 (insert (format "Here is some text around %c:\n\n %s"
4219 reg text)))
2f3eb3b6 4220 (insert (format viper-EmptyTextmarker reg)))
d5e52f99
MK
4221 (goto-char (point-min)))
4222 (display-buffer "*Output*")))
2f3eb3b6 4223 (t (error viper-InvalidTextmarker reg)))))
d5e52f99
MK
4224
4225
4226\f
4227;; commands in insertion mode
4228
2f3eb3b6 4229(defun viper-delete-backward-word (arg)
d5e52f99
MK
4230 "Delete previous word."
4231 (interactive "p")
4232 (save-excursion
4233 (push-mark nil t)
4234 (backward-word arg)
4235 (delete-region (point) (mark t))
4236 (pop-mark)))
4237
4238
1e70790f 4239(defun viper-set-expert-level (&optional dont-change-unless)
d5e52f99
MK
4240 "Sets the expert level for a Viper user.
4241Can be called interactively to change (temporarily or permanently) the
4242current expert level.
4243
e36a387d 4244The optional argument DONT-CHANGE-UNLESS, if not nil, says that
d5e52f99
MK
4245the level should not be changed, unless its current value is
4246meaningless (i.e., not one of 1,2,3,4,5).
4247
4248User level determines the setting of Viper variables that are most
4249sensitive for VI-style look-and-feel."
4250
4251 (interactive)
4252
1e70790f 4253 (if (not (natnump viper-expert-level)) (setq viper-expert-level 0))
d5e52f99
MK
4254
4255 (save-window-excursion
4256 (delete-other-windows)
1e70790f 4257 ;; if 0 < viper-expert-level < viper-max-expert-level
d5e52f99 4258 ;; & dont-change-unless = t -- use it; else ask
2f3eb3b6 4259 (viper-ask-level dont-change-unless))
d5e52f99 4260
2f3eb3b6
MK
4261 (setq viper-always t
4262 viper-ex-style-motion t
4263 viper-ex-style-editing-in-insert t
4264 viper-want-ctl-h-help nil)
d5e52f99 4265
1e70790f 4266 (cond ((eq viper-expert-level 1) ; novice or beginner
d5e52f99 4267 (global-set-key ; in emacs-state
2f3eb3b6
MK
4268 viper-toggle-key
4269 (if (viper-window-display-p) 'viper-iconify 'suspend-emacs))
4270 (setq viper-no-multiple-ESC t
4271 viper-re-search t
4272 viper-vi-style-in-minibuffer t
4273 viper-search-wrap-around-t t
4274 viper-electric-mode nil
4275 viper-want-emacs-keys-in-vi nil
4276 viper-want-emacs-keys-in-insert nil))
d5e52f99 4277
1e70790f 4278 ((and (> viper-expert-level 1) (< viper-expert-level 5))
d5e52f99 4279 ;; intermediate to guru
2f3eb3b6
MK
4280 (setq viper-no-multiple-ESC (if (viper-window-display-p)
4281 t 'twice)
4282 viper-electric-mode t
4283 viper-want-emacs-keys-in-vi t
4284 viper-want-emacs-keys-in-insert (> viper-expert-level 2))
4285
4286 (if (eq viper-expert-level 4) ; respect user's ex-style motion
4287 ; and viper-no-multiple-ESC
d5e52f99 4288 (progn
1e70790f 4289 (setq-default
2f3eb3b6
MK
4290 viper-ex-style-editing-in-insert
4291 (viper-standard-value 'viper-ex-style-editing-in-insert)
4292 viper-ex-style-motion
4293 (viper-standard-value 'viper-ex-style-motion))
4294 (setq viper-ex-style-motion
4295 (viper-standard-value 'viper-ex-style-motion)
4296 viper-ex-style-editing-in-insert
4297 (viper-standard-value 'viper-ex-style-editing-in-insert)
4298 viper-re-search
4299 (viper-standard-value 'viper-re-search)
4300 viper-no-multiple-ESC
4301 (viper-standard-value 'viper-no-multiple-ESC)))))
1e70790f 4302
d5e52f99
MK
4303 ;; A wizard!!
4304 ;; Ideally, if 5 is selected, a buffer should pop up to let the
4305 ;; user toggle the values of variables.
2f3eb3b6
MK
4306 (t (setq-default viper-ex-style-editing-in-insert
4307 (viper-standard-value 'viper-ex-style-editing-in-insert)
4308 viper-ex-style-motion
4309 (viper-standard-value 'viper-ex-style-motion))
4310 (setq viper-want-ctl-h-help
4311 (viper-standard-value 'viper-want-ctl-h-help)
e36a387d 4312 viper-always
1e70790f 4313 (viper-standard-value 'viper-always)
2f3eb3b6
MK
4314 viper-no-multiple-ESC
4315 (viper-standard-value 'viper-no-multiple-ESC)
4316 viper-ex-style-motion
4317 (viper-standard-value 'viper-ex-style-motion)
4318 viper-ex-style-editing-in-insert
4319 (viper-standard-value 'viper-ex-style-editing-in-insert)
4320 viper-re-search
4321 (viper-standard-value 'viper-re-search)
4322 viper-electric-mode
4323 (viper-standard-value 'viper-electric-mode)
4324 viper-want-emacs-keys-in-vi
4325 (viper-standard-value 'viper-want-emacs-keys-in-vi)
4326 viper-want-emacs-keys-in-insert
4327 (viper-standard-value 'viper-want-emacs-keys-in-insert))))
1e70790f 4328
2f3eb3b6 4329 (viper-set-mode-vars-for viper-current-state)
e36a387d 4330 (if (or viper-always
1e70790f 4331 (and (> viper-expert-level 0) (> 5 viper-expert-level)))
2f3eb3b6 4332 (viper-set-hooks)))
d5e52f99
MK
4333
4334;; Ask user expert level.
2f3eb3b6
MK
4335(defun viper-ask-level (dont-change-unless)
4336 (let ((ask-buffer " *viper-ask-level*")
d5e52f99
MK
4337 level-changed repeated)
4338 (save-window-excursion
4339 (switch-to-buffer ask-buffer)
4340
1e70790f
MK
4341 (while (or (> viper-expert-level viper-max-expert-level)
4342 (< viper-expert-level 1)
d5e52f99
MK
4343 (null dont-change-unless))
4344 (erase-buffer)
4345 (if repeated
4346 (progn
4347 (message "Invalid user level")
4348 (beep 1))
4349 (setq repeated t))
4350 (setq dont-change-unless t
4351 level-changed t)
4352 (insert "
4353Please specify your level of familiarity with the venomous VI PERil
4354(and the VI Plan for Emacs Rescue).
1e70790f 4355You can change it at any time by typing `M-x viper-set-expert-level RET'
d5e52f99
MK
4356
4357 1 -- BEGINNER: Almost all Emacs features are suppressed.
2f3eb3b6
MK
4358 Feels almost like straight Vi. File name completion and
4359 command history in the minibuffer are thrown in as a bonus.
4360 To use Emacs productively, you must reach level 3 or higher.
d5e52f99 4361 2 -- MASTER: C-c now has its standard Emacs meaning in Vi command state,
2f3eb3b6
MK
4362 so most Emacs commands can be used when Viper is in Vi state.
4363 Good progress---you are well on the way to level 3!
d5e52f99 4364 3 -- GRAND MASTER: Like 3, but most Emacs commands are available also
2f3eb3b6
MK
4365 in Viper's insert state.
4366 4 -- GURU: Like 3, but user settings are respected for viper-no-multiple-ESC,
4367 viper-ex-style-motion, viper-ex-style-editing-in-insert, and
4368 viper-re-search variables. Adjust these settings to your taste.
e36a387d 4369 5 -- WIZARD: Like 4, but user settings are also respected for viper-always,
2f3eb3b6
MK
4370 viper-electric-mode, viper-want-ctl-h-help, viper-want-emacs-keys-in-vi,
4371 and viper-want-emacs-keys-in-insert. Adjust these to your taste.
d5e52f99
MK
4372
4373Please, specify your level now: ")
4374
2f3eb3b6 4375 (setq viper-expert-level (- (viper-read-char-exclusive) ?0))
d5e52f99
MK
4376 ) ; end while
4377
4378 ;; tell the user if level was changed
4379 (and level-changed
4380 (progn
4381 (insert
4382 (format "\n\n\n\n\n\t\tYou have selected user level %d"
1e70790f 4383 viper-expert-level))
d5e52f99 4384 (if (y-or-n-p "Do you wish to make this change permanent? ")
1e70790f 4385 ;; save the setting for viper-expert-level
2f3eb3b6 4386 (viper-save-setting
1e70790f
MK
4387 'viper-expert-level
4388 (format "Saving user level %d ..." viper-expert-level)
2f3eb3b6 4389 viper-custom-file-name))
d5e52f99
MK
4390 ))
4391 (bury-buffer) ; remove ask-buffer from screen
4392 (message "")
4393 )))
4394
4395
2f3eb3b6 4396(defun viper-nil ()
d5e52f99
MK
4397 (interactive)
4398 (beep 1))
4399
4400
4401;; if ENFORCE-BUFFER is not nil, error if CHAR is a marker in another buffer
2f3eb3b6 4402(defun viper-register-to-point (char &optional enforce-buffer)
d5e52f99
MK
4403 "Like jump-to-register, but switches to another buffer in another window."
4404 (interactive "cViper register to point: ")
4405 (let ((val (get-register char)))
4406 (cond
4407 ((and (fboundp 'frame-configuration-p)
4408 (frame-configuration-p val))
4409 (set-frame-configuration val))
4410 ((window-configuration-p val)
4411 (set-window-configuration val))
2f3eb3b6 4412 ((viper-valid-marker val)
d5e52f99
MK
4413 (if (and enforce-buffer
4414 (not (equal (current-buffer) (marker-buffer val))))
2f3eb3b6 4415 (error (concat viper-EmptyTextmarker " in this buffer")
d5e52f99
MK
4416 (1- (+ char ?a))))
4417 (pop-to-buffer (marker-buffer val))
4418 (goto-char val))
4419 ((and (consp val) (eq (car val) 'file))
4420 (find-file (cdr val)))
4421 (t
2f3eb3b6 4422 (error viper-EmptyTextmarker (1- (+ char ?a)))))))
d5e52f99
MK
4423
4424
2f3eb3b6 4425(defun viper-save-kill-buffer ()
d5e52f99
MK
4426 "Save then kill current buffer. "
4427 (interactive)
1e70790f 4428 (if (< viper-expert-level 2)
d5e52f99
MK
4429 (save-buffers-kill-emacs)
4430 (save-buffer)
4431 (kill-buffer (current-buffer))))
4432
4433
4434\f
4435;;; Bug Report
4436
2f3eb3b6 4437(defun viper-submit-report ()
d5e52f99
MK
4438 "Submit bug report on Viper."
4439 (interactive)
4440 (let ((reporter-prompt-for-summary-p t)
2f3eb3b6 4441 (viper-device-type (viper-device-type))
d5e52f99
MK
4442 color-display-p frame-parameters
4443 minibuffer-emacs-face minibuffer-vi-face minibuffer-insert-face
4444 varlist salutation window-config)
4445
4446 ;; If mode info is needed, add variable to `let' and then set it below,
4447 ;; like we did with color-display-p.
2f3eb3b6
MK
4448 (setq color-display-p (if (viper-window-display-p)
4449 (viper-color-display-p)
d5e52f99 4450 'non-x)
2f3eb3b6
MK
4451 minibuffer-vi-face (if (viper-has-face-support-p)
4452 (viper-get-face viper-minibuffer-vi-face)
d5e52f99 4453 'non-x)
2f3eb3b6
MK
4454 minibuffer-insert-face (if (viper-has-face-support-p)
4455 (viper-get-face
4456 viper-minibuffer-insert-face)
d5e52f99 4457 'non-x)
2f3eb3b6
MK
4458 minibuffer-emacs-face (if (viper-has-face-support-p)
4459 (viper-get-face
4460 viper-minibuffer-emacs-face)
d5e52f99
MK
4461 'non-x)
4462 frame-parameters (if (fboundp 'frame-parameters)
4463 (frame-parameters (selected-frame))))
4464
2f3eb3b6
MK
4465 (setq varlist (list 'viper-vi-minibuffer-minor-mode
4466 'viper-insert-minibuffer-minor-mode
4467 'viper-vi-intercept-minor-mode
4468 'viper-vi-local-user-minor-mode
4469 'viper-vi-kbd-minor-mode
4470 'viper-vi-global-user-minor-mode
4471 'viper-vi-state-modifier-minor-mode
4472 'viper-vi-diehard-minor-mode
4473 'viper-vi-basic-minor-mode
4474 'viper-replace-minor-mode
4475 'viper-insert-intercept-minor-mode
4476 'viper-insert-local-user-minor-mode
4477 'viper-insert-kbd-minor-mode
4478 'viper-insert-global-user-minor-mode
4479 'viper-insert-state-modifier-minor-mode
4480 'viper-insert-diehard-minor-mode
4481 'viper-insert-basic-minor-mode
4482 'viper-emacs-intercept-minor-mode
4483 'viper-emacs-local-user-minor-mode
4484 'viper-emacs-kbd-minor-mode
4485 'viper-emacs-global-user-minor-mode
4486 'viper-emacs-state-modifier-minor-mode
4487 'viper-automatic-iso-accents
4488 'viper-want-emacs-keys-in-insert
4489 'viper-want-emacs-keys-in-vi
4490 'viper-keep-point-on-undo
4491 'viper-no-multiple-ESC
4492 'viper-electric-mode
4493 'viper-ESC-key
4494 'viper-want-ctl-h-help
4495 'viper-ex-style-editing-in-insert
4496 'viper-delete-backwards-in-replace
4497 'viper-vi-style-in-minibuffer
4498 'viper-vi-state-hook
4499 'viper-insert-state-hook
4500 'viper-replace-state-hook
4501 'viper-emacs-state-hook
d5e52f99
MK
4502 'ex-cycle-other-window
4503 'ex-cycle-through-non-files
1e70790f 4504 'viper-expert-level
d5e52f99 4505 'major-mode
2f3eb3b6 4506 'viper-device-type
d5e52f99
MK
4507 'color-display-p
4508 'frame-parameters
4509 'minibuffer-vi-face
4510 'minibuffer-insert-face
4511 'minibuffer-emacs-face
4512 ))
4513 (setq salutation "
4514Congratulations! You may have unearthed a bug in Viper!
4515Please mail a concise, accurate summary of the problem to the address above.
4516
4517-------------------------------------------------------------------")
4518 (setq window-config (current-window-configuration))
2f3eb3b6
MK
4519 (with-output-to-temp-buffer " *viper-info*"
4520 (switch-to-buffer " *viper-info*")
d5e52f99
MK
4521 (delete-other-windows)
4522 (princ "
4523PLEASE FOLLOW THESE PROCEDURES
4524------------------------------
4525
4526Before reporting a bug, please verify that it is related to Viper, and is
4527not cause by other packages you are using.
4528
4529Don't report compilation warnings, unless you are certain that there is a
4530problem. These warnings are normal and unavoidable.
4531
4532Please note that users should not modify variables and keymaps other than
4533those advertised in the manual. Such `customization' is likely to crash
4534Viper, as it would any other improperly customized Emacs package.
4535
4536If you are reporting an error message received while executing one of the
4537Viper commands, type:
4538
4539 M-x set-variable <Return> debug-on-error <Return> t <Return>
4540
4541Then reproduce the error. The above command will cause Emacs to produce a
4542back trace of the execution that leads to the error. Please include this
4543trace in your bug report.
4544
4545If you believe that one of Viper's commands goes into an infinite loop
4546\(e.g., Emacs freezes\), type:
4547
4548 M-x set-variable <Return> debug-on-quit <Return> t <Return>
4549
4550Then reproduce the problem. Wait for a few seconds, then type C-g to abort
4551the current command. Include the resulting back trace in the bug report.
4552
4553Mail anyway (y or n)? ")
4554 (if (y-or-n-p "Mail anyway? ")
4555 ()
4556 (set-window-configuration window-config)
4557 (error "Bug report aborted")))
4558
4559 (require 'reporter)
4560 (set-window-configuration window-config)
4561
4562 (reporter-submit-bug-report "kifer@cs.sunysb.edu"
2f3eb3b6 4563 (viper-version)
d5e52f99
MK
4564 varlist
4565 nil 'delete-other-windows
4566 salutation)
4567 ))
4568
4569
4570
4571
4572;; Smoothes out the difference between Emacs' unread-command-events
4573;; and XEmacs unread-command-event. Arg is a character, an event, a list of
4574;; events or a sequence of keys.
4575;;
4576;; Due to the way unread-command-events in Emacs (not XEmacs), a non-event
4577;; symbol in unread-command-events list may cause Emacs to turn this symbol
4578;; into an event. Below, we delete nil from event lists, since nil is the most
4579;; common symbol that might appear in this wrong context.
2f3eb3b6
MK
4580(defun viper-set-unread-command-events (arg)
4581 (if viper-emacs-p
d5e52f99
MK
4582 (setq
4583 unread-command-events
4584 (let ((new-events
4585 (cond ((eventp arg) (list arg))
4586 ((listp arg) arg)
4587 ((sequencep arg)
4588 (listify-key-sequence arg))
4589 (t (error
2f3eb3b6 4590 "viper-set-unread-command-events: Invalid argument, %S"
d5e52f99
MK
4591 arg)))))
4592 (if (not (eventp nil))
4593 (setq new-events (delq nil new-events)))
4594 (append new-events unread-command-events)))
4595 ;; XEmacs
4596 (setq
4597 unread-command-events
4598 (append
2f3eb3b6 4599 (cond ((viper-characterp arg) (list (character-to-event arg)))
d5e52f99
MK
4600 ((eventp arg) (list arg))
4601 ((stringp arg) (mapcar 'character-to-event arg))
4602 ((vectorp arg) (append arg nil)) ; turn into list
2f3eb3b6 4603 ((listp arg) (viper-eventify-list-xemacs arg))
d5e52f99 4604 (t (error
2f3eb3b6 4605 "viper-set-unread-command-events: Invalid argument, %S" arg)))
d5e52f99
MK
4606 unread-command-events))))
4607
4608;; list is assumed to be a list of events of characters
2f3eb3b6 4609(defun viper-eventify-list-xemacs (lis)
d5e52f99
MK
4610 (mapcar
4611 (function (lambda (elt)
2f3eb3b6 4612 (cond ((viper-characterp elt) (character-to-event elt))
d5e52f99
MK
4613 ((eventp elt) elt)
4614 (t (error
2f3eb3b6 4615 "viper-eventify-list-xemacs: can't convert to event, %S"
d5e52f99
MK
4616 elt)))))
4617 lis))
4618
4619
4620
4621;;; viper-cmd.el ends here