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