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