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