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