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