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