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