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