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