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