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