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