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