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