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