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