*** empty log message ***
[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])
483e0218 953 (inhibit-quit t))
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)
483e0218
MK
2551 (let ((inhibit-quit t)
2552 char)
02f34c70
MK
2553 (viper-set-complex-command-for-undo)
2554 (or (eq viper-intermediate-command 'viper-repeat)
2555 (viper-special-read-and-insert-char))
2556
34317da2 2557 (if (eq char ?\C-m) (setq char ?\n))
02f34c70
MK
2558
2559 (delete-char 1 t)
2560
2561 (setq char (if com viper-d-char (viper-char-at-pos 'backward)))
2562 (if com (insert char))
2563
2564 (setq viper-d-char char)
2565
2566 (viper-loop (1- (if (> arg 0) arg (- arg)))
2567 (delete-char 1 t)
2568 (insert char))
2569
34317da2 2570 (viper-adjust-undo)
02f34c70
MK
2571 (backward-char arg)
2572 ))
d5e52f99
MK
2573
2574\f
2575;; basic cursor movement. j, k, l, h commands.
2576
2f3eb3b6 2577(defun viper-forward-char (arg)
d5e52f99
MK
2578 "Move point right ARG characters (left if ARG negative).
2579On reaching end of line, stop and signal error."
2580 (interactive "P")
2f3eb3b6
MK
2581 (viper-leave-region-active)
2582 (let ((val (viper-p-val arg))
2583 (com (viper-getcom arg)))
2584 (if com (viper-move-marker-locally 'viper-com-point (point)))
2585 (if viper-ex-style-motion
d5e52f99
MK
2586 (progn
2587 ;; the boundary condition check gets weird here because
2588 ;; forward-char may be the parameter of a delete, and 'dl' works
2589 ;; just like 'x' for the last char on a line, so we have to allow
2f3eb3b6 2590 ;; the forward motion before the 'viper-execute-com', but, of
d5e52f99 2591 ;; course, 'dl' doesn't work on an empty line, so we have to
2f3eb3b6 2592 ;; catch that condition before 'viper-execute-com'
d5e52f99 2593 (if (and (eolp) (bolp)) (error "") (forward-char val))
2f3eb3b6 2594 (if com (viper-execute-com 'viper-forward-char val com))
d5e52f99
MK
2595 (if (eolp) (progn (backward-char 1) (error ""))))
2596 (forward-char val)
2f3eb3b6 2597 (if com (viper-execute-com 'viper-forward-char val com)))))
d5e52f99 2598
7d3f9fd8 2599
2f3eb3b6 2600(defun viper-backward-char (arg)
f1097063 2601 "Move point left ARG characters (right if ARG negative).
d5e52f99
MK
2602On reaching beginning of line, stop and signal error."
2603 (interactive "P")
2f3eb3b6
MK
2604 (viper-leave-region-active)
2605 (let ((val (viper-p-val arg))
2606 (com (viper-getcom arg)))
2607 (if com (viper-move-marker-locally 'viper-com-point (point)))
2608 (if viper-ex-style-motion
d5e52f99
MK
2609 (progn
2610 (if (bolp) (error "") (backward-char val))
2f3eb3b6 2611 (if com (viper-execute-com 'viper-backward-char val com)))
d5e52f99 2612 (backward-char val)
2f3eb3b6 2613 (if com (viper-execute-com 'viper-backward-char val com)))))
f1097063 2614
7d3f9fd8 2615
d5e52f99 2616;; Like forward-char, but doesn't move at end of buffer.
f1097063 2617;; Returns distance traveled
34317da2 2618;; (positive or 0, if arg positive; negative if arg negative).
f1097063 2619(defun viper-forward-char-carefully (&optional arg)
d5e52f99 2620 (setq arg (or arg 1))
34317da2
MK
2621 (let ((pt (point)))
2622 (condition-case nil
2623 (forward-char arg)
2624 (error))
2625 (if (< (point) pt) ; arg was negative
2626 (- (viper-chars-in-region pt (point)))
2627 (viper-chars-in-region pt (point)))))
f1097063 2628
7d3f9fd8 2629
34317da2
MK
2630;; Like backward-char, but doesn't move at beg of buffer.
2631;; Returns distance traveled
2632;; (negative or 0, if arg positive; positive if arg negative).
f1097063 2633(defun viper-backward-char-carefully (&optional arg)
d5e52f99 2634 (setq arg (or arg 1))
34317da2
MK
2635 (let ((pt (point)))
2636 (condition-case nil
2637 (backward-char arg)
2638 (error))
2639 (if (> (point) pt) ; arg was negative
2640 (viper-chars-in-region pt (point))
2641 (- (viper-chars-in-region pt (point))))))
d5e52f99 2642
2f3eb3b6 2643(defun viper-next-line-carefully (arg)
d5e52f99
MK
2644 (condition-case nil
2645 (next-line arg)
2646 (error nil)))
2647
2648
2649\f
2650;;; Word command
2651
2f3eb3b6 2652;; Words are formed from alpha's and nonalphas - <sp>,\t\n are separators for
3af0304a 2653;; word movement. When executed with a destructive command, \n is usually left
2f3eb3b6 2654;; untouched for the last word. Viper uses syntax table to determine what is a
3af0304a 2655;; word and what is a separator. However, \n is always a separator. Also, if
2f3eb3b6 2656;; viper-syntax-preference is 'vi, then `_' is part of the word.
d5e52f99
MK
2657
2658;; skip only one \n
2f3eb3b6 2659(defun viper-skip-separators (forward)
d5e52f99
MK
2660 (if forward
2661 (progn
2f3eb3b6 2662 (viper-skip-all-separators-forward 'within-line)
d5e52f99
MK
2663 (if (looking-at "\n")
2664 (progn
2665 (forward-char)
2f3eb3b6 2666 (viper-skip-all-separators-forward 'within-line))))
3af0304a 2667 ;; check for eob and white space before it. move off of eob
6d459c4d
KH
2668 (if (and (eobp) (save-excursion
2669 (viper-backward-char-carefully)
2670 (viper-looking-at-separator)))
2671 (viper-backward-char-carefully))
2f3eb3b6 2672 (viper-skip-all-separators-backward 'within-line)
34317da2 2673 (viper-backward-char-carefully)
d5e52f99 2674 (if (looking-at "\n")
2f3eb3b6 2675 (viper-skip-all-separators-backward 'within-line)
6d459c4d 2676 (or (bobp) (forward-char)))))
f1097063 2677
7d3f9fd8 2678
2f3eb3b6 2679(defun viper-forward-word-kernel (val)
d5e52f99 2680 (while (> val 0)
2f3eb3b6
MK
2681 (cond ((viper-looking-at-alpha)
2682 (viper-skip-alpha-forward "_")
2683 (viper-skip-separators t))
2684 ((viper-looking-at-separator)
2685 (viper-skip-separators t))
2686 ((not (viper-looking-at-alphasep))
2687 (viper-skip-nonalphasep-forward)
2688 (viper-skip-separators t)))
d5e52f99
MK
2689 (setq val (1- val))))
2690
3af0304a 2691;; first skip non-newline separators backward, then skip \n. Then, if TWICE is
34317da2
MK
2692;; non-nil, skip non-\n back again, but don't overshoot the limit LIM.
2693(defun viper-separator-skipback-special (twice lim)
2694 (let ((prev-char (viper-char-at-pos 'backward))
2695 (saved-point (point)))
2696 ;; skip non-newline separators backward
657f9cb8 2697 (while (and (not (viper-memq-char prev-char '(nil \n)))
34317da2
MK
2698 (< lim (point))
2699 ;; must be non-newline separator
2700 (if (eq viper-syntax-preference 'strict-vi)
657f9cb8
MK
2701 (viper-memq-char prev-char '(?\ ?\t))
2702 (viper-memq-char (char-syntax prev-char) '(?\ ?-))))
34317da2
MK
2703 (viper-backward-char-carefully)
2704 (setq prev-char (viper-char-at-pos 'backward)))
2705
2706 (if (and (< lim (point)) (eq prev-char ?\n))
2707 (backward-char)
2708 ;; If we skipped to the next word and the prefix of this line doesn't
2709 ;; consist of separators preceded by a newline, then don't skip backwards
2710 ;; at all.
2711 (goto-char saved-point))
2712 (setq prev-char (viper-char-at-pos 'backward))
2713
2714 ;; skip again, but make sure we don't overshoot the limit
2715 (if twice
657f9cb8 2716 (while (and (not (viper-memq-char prev-char '(nil \n)))
34317da2
MK
2717 (< lim (point))
2718 ;; must be non-newline separator
2719 (if (eq viper-syntax-preference 'strict-vi)
657f9cb8
MK
2720 (viper-memq-char prev-char '(?\ ?\t))
2721 (viper-memq-char (char-syntax prev-char) '(?\ ?-))))
34317da2
MK
2722 (viper-backward-char-carefully)
2723 (setq prev-char (viper-char-at-pos 'backward))))
2724
2725 (if (= (point) lim)
2726 (viper-forward-char-carefully))
2727 ))
d5e52f99 2728
f1097063 2729
2f3eb3b6 2730(defun viper-forward-word (arg)
d5e52f99
MK
2731 "Forward word."
2732 (interactive "P")
2f3eb3b6
MK
2733 (viper-leave-region-active)
2734 (let ((val (viper-p-val arg))
2735 (com (viper-getcom arg)))
2736 (if com (viper-move-marker-locally 'viper-com-point (point)))
2737 (viper-forward-word-kernel val)
4960e757
MK
2738 (if com
2739 (progn
2740 (cond ((viper-char-equal com ?c)
2741 (viper-separator-skipback-special 'twice viper-com-point))
2742 ;; Yank words including the whitespace, but not newline
2743 ((viper-char-equal com ?y)
2744 (viper-separator-skipback-special nil viper-com-point))
2745 ((viper-dotable-command-p com)
2746 (viper-separator-skipback-special nil viper-com-point)))
2747 (viper-execute-com 'viper-forward-word val com)))
2748 ))
f1097063 2749
d5e52f99 2750
2f3eb3b6 2751(defun viper-forward-Word (arg)
d5e52f99
MK
2752 "Forward word delimited by white characters."
2753 (interactive "P")
2f3eb3b6
MK
2754 (viper-leave-region-active)
2755 (let ((val (viper-p-val arg))
2756 (com (viper-getcom arg)))
2757 (if com (viper-move-marker-locally 'viper-com-point (point)))
2758 (viper-loop val
2f3eb3b6 2759 (viper-skip-nonseparators 'forward)
34317da2 2760 (viper-skip-separators t))
d5e52f99 2761 (if com (progn
4960e757 2762 (cond ((viper-char-equal com ?c)
34317da2 2763 (viper-separator-skipback-special 'twice viper-com-point))
d5e52f99 2764 ;; Yank words including the whitespace, but not newline
4960e757 2765 ((viper-char-equal com ?y)
34317da2 2766 (viper-separator-skipback-special nil viper-com-point))
2f3eb3b6 2767 ((viper-dotable-command-p com)
34317da2 2768 (viper-separator-skipback-special nil viper-com-point)))
2f3eb3b6 2769 (viper-execute-com 'viper-forward-Word val com)))))
d5e52f99
MK
2770
2771
f1097063 2772;; this is a bit different from Vi, but Vi's end of word
d5e52f99 2773;; makes no sense whatsoever
2f3eb3b6
MK
2774(defun viper-end-of-word-kernel ()
2775 (if (viper-end-of-word-p) (forward-char))
2776 (if (viper-looking-at-separator)
2777 (viper-skip-all-separators-forward))
f1097063 2778
2f3eb3b6
MK
2779 (cond ((viper-looking-at-alpha) (viper-skip-alpha-forward "_"))
2780 ((not (viper-looking-at-alphasep)) (viper-skip-nonalphasep-forward)))
2781 (viper-backward-char-carefully))
d5e52f99 2782
2f3eb3b6 2783(defun viper-end-of-word-p ()
f1097063 2784 (or (eobp)
d5e52f99 2785 (save-excursion
2f3eb3b6 2786 (cond ((viper-looking-at-alpha)
d5e52f99 2787 (forward-char)
2f3eb3b6
MK
2788 (not (viper-looking-at-alpha)))
2789 ((not (viper-looking-at-alphasep))
d5e52f99 2790 (forward-char)
2f3eb3b6 2791 (viper-looking-at-alphasep))))))
d5e52f99
MK
2792
2793
2f3eb3b6 2794(defun viper-end-of-word (arg &optional careful)
d5e52f99
MK
2795 "Move point to end of current word."
2796 (interactive "P")
2f3eb3b6
MK
2797 (viper-leave-region-active)
2798 (let ((val (viper-p-val arg))
2799 (com (viper-getcom arg)))
2800 (if com (viper-move-marker-locally 'viper-com-point (point)))
2801 (viper-loop val (viper-end-of-word-kernel))
f1097063 2802 (if com
d5e52f99
MK
2803 (progn
2804 (forward-char)
2f3eb3b6 2805 (viper-execute-com 'viper-end-of-word val com)))))
d5e52f99 2806
2f3eb3b6 2807(defun viper-end-of-Word (arg)
d5e52f99
MK
2808 "Forward to end of word delimited by white character."
2809 (interactive "P")
2f3eb3b6
MK
2810 (viper-leave-region-active)
2811 (let ((val (viper-p-val arg))
2812 (com (viper-getcom arg)))
2813 (if com (viper-move-marker-locally 'viper-com-point (point)))
2814 (viper-loop val
2f3eb3b6
MK
2815 (viper-end-of-word-kernel)
2816 (viper-skip-nonseparators 'forward)
34317da2 2817 (backward-char))
f1097063 2818 (if com
d5e52f99
MK
2819 (progn
2820 (forward-char)
2f3eb3b6 2821 (viper-execute-com 'viper-end-of-Word val com)))))
d5e52f99 2822
2f3eb3b6 2823(defun viper-backward-word-kernel (val)
d5e52f99 2824 (while (> val 0)
34317da2 2825 (viper-backward-char-carefully)
2f3eb3b6
MK
2826 (cond ((viper-looking-at-alpha)
2827 (viper-skip-alpha-backward "_"))
2828 ((viper-looking-at-separator)
d5e52f99 2829 (forward-char)
2f3eb3b6 2830 (viper-skip-separators nil)
34317da2 2831 (viper-backward-char-carefully)
2f3eb3b6
MK
2832 (cond ((viper-looking-at-alpha)
2833 (viper-skip-alpha-backward "_"))
2834 ((not (viper-looking-at-alphasep))
2835 (viper-skip-nonalphasep-backward))
34317da2 2836 ((bobp)) ; could still be at separator, but at beg of buffer
d5e52f99 2837 (t (forward-char))))
2f3eb3b6
MK
2838 ((not (viper-looking-at-alphasep))
2839 (viper-skip-nonalphasep-backward)))
d5e52f99
MK
2840 (setq val (1- val))))
2841
2f3eb3b6 2842(defun viper-backward-word (arg)
d5e52f99
MK
2843 "Backward word."
2844 (interactive "P")
2f3eb3b6
MK
2845 (viper-leave-region-active)
2846 (let ((val (viper-p-val arg))
2847 (com (viper-getcom arg)))
d5e52f99
MK
2848 (if com
2849 (let (i)
2850 (if (setq i (save-excursion (backward-char) (looking-at "\n")))
2851 (backward-char))
2f3eb3b6 2852 (viper-move-marker-locally 'viper-com-point (point))
d5e52f99 2853 (if i (forward-char))))
2f3eb3b6
MK
2854 (viper-backward-word-kernel val)
2855 (if com (viper-execute-com 'viper-backward-word val com))))
d5e52f99 2856
2f3eb3b6 2857(defun viper-backward-Word (arg)
d5e52f99
MK
2858 "Backward word delimited by white character."
2859 (interactive "P")
2f3eb3b6
MK
2860 (viper-leave-region-active)
2861 (let ((val (viper-p-val arg))
2862 (com (viper-getcom arg)))
d5e52f99
MK
2863 (if com
2864 (let (i)
2865 (if (setq i (save-excursion (backward-char) (looking-at "\n")))
2866 (backward-char))
2f3eb3b6 2867 (viper-move-marker-locally 'viper-com-point (point))
d5e52f99 2868 (if i (forward-char))))
2f3eb3b6 2869 (viper-loop val
34317da2
MK
2870 (viper-skip-separators nil) ; nil means backward here
2871 (viper-skip-nonseparators 'backward))
2f3eb3b6 2872 (if com (viper-execute-com 'viper-backward-Word val com))))
d5e52f99
MK
2873
2874
2875\f
2876;; line commands
2877
2f3eb3b6 2878(defun viper-beginning-of-line (arg)
d5e52f99
MK
2879 "Go to beginning of line."
2880 (interactive "P")
2f3eb3b6
MK
2881 (viper-leave-region-active)
2882 (let ((val (viper-p-val arg))
2883 (com (viper-getcom arg)))
2884 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2885 (beginning-of-line val)
2f3eb3b6 2886 (if com (viper-execute-com 'viper-beginning-of-line val com))))
d5e52f99 2887
2f3eb3b6 2888(defun viper-bol-and-skip-white (arg)
d5e52f99
MK
2889 "Beginning of line at first non-white character."
2890 (interactive "P")
2f3eb3b6
MK
2891 (viper-leave-region-active)
2892 (let ((val (viper-p-val arg))
2893 (com (viper-getcom arg)))
2894 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2895 (forward-to-indentation (1- val))
2f3eb3b6 2896 (if com (viper-execute-com 'viper-bol-and-skip-white val com))))
d5e52f99 2897
2f3eb3b6 2898(defun viper-goto-eol (arg)
d5e52f99
MK
2899 "Go to end of line."
2900 (interactive "P")
2f3eb3b6
MK
2901 (viper-leave-region-active)
2902 (let ((val (viper-p-val arg))
2903 (com (viper-getcom arg)))
2904 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2905 (end-of-line val)
2f3eb3b6
MK
2906 (if com (viper-execute-com 'viper-goto-eol val com))
2907 (if viper-ex-style-motion
f1097063 2908 (if (and (eolp) (not (bolp))
2f3eb3b6
MK
2909 ;; a fix for viper-change-to-eol
2910 (not (equal viper-current-state 'insert-state)))
d5e52f99
MK
2911 (backward-char 1)
2912 ))))
2913
2914
2f3eb3b6 2915(defun viper-goto-col (arg)
d5e52f99
MK
2916 "Go to ARG's column."
2917 (interactive "P")
2f3eb3b6
MK
2918 (viper-leave-region-active)
2919 (let ((val (viper-p-val arg))
2920 (com (viper-getcom arg))
d5e52f99 2921 line-len)
34317da2
MK
2922 (setq line-len
2923 (viper-chars-in-region
2924 (viper-line-pos 'start) (viper-line-pos 'end)))
2f3eb3b6 2925 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2926 (beginning-of-line)
2927 (forward-char (1- (min line-len val)))
2928 (while (> (current-column) (1- val))
2929 (backward-char 1))
2f3eb3b6 2930 (if com (viper-execute-com 'viper-goto-col val com))
d5e52f99
MK
2931 (save-excursion
2932 (end-of-line)
2933 (if (> val (current-column)) (error "")))
2934 ))
f1097063 2935
d5e52f99 2936
2f3eb3b6 2937(defun viper-next-line (arg)
d5e52f99
MK
2938 "Go to next line."
2939 (interactive "P")
2f3eb3b6
MK
2940 (viper-leave-region-active)
2941 (let ((val (viper-p-val arg))
2942 (com (viper-getCom arg)))
2943 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2944 (next-line val)
2f3eb3b6 2945 (if viper-ex-style-motion
d5e52f99
MK
2946 (if (and (eolp) (not (bolp))) (backward-char 1)))
2947 (setq this-command 'next-line)
2f3eb3b6 2948 (if com (viper-execute-com 'viper-next-line val com))))
d5e52f99 2949
2f3eb3b6 2950(defun viper-next-line-at-bol (arg)
d5e52f99
MK
2951 "Next line at beginning of line."
2952 (interactive "P")
2f3eb3b6 2953 (viper-leave-region-active)
d5e52f99
MK
2954 (save-excursion
2955 (end-of-line)
2956 (if (eobp) (error "Last line in buffer")))
2f3eb3b6
MK
2957 (let ((val (viper-p-val arg))
2958 (com (viper-getCom arg)))
2959 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2960 (forward-line val)
2961 (back-to-indentation)
2f3eb3b6 2962 (if com (viper-execute-com 'viper-next-line-at-bol val com))))
d5e52f99 2963
7d3f9fd8 2964
f1097063
SS
2965(defun viper-previous-line (arg)
2966 "Go to previous line."
d5e52f99 2967 (interactive "P")
2f3eb3b6
MK
2968 (viper-leave-region-active)
2969 (let ((val (viper-p-val arg))
2970 (com (viper-getCom arg)))
2971 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 2972 (previous-line val)
2f3eb3b6 2973 (if viper-ex-style-motion
d5e52f99
MK
2974 (if (and (eolp) (not (bolp))) (backward-char 1)))
2975 (setq this-command 'previous-line)
2f3eb3b6 2976 (if com (viper-execute-com 'viper-previous-line val com))))
d5e52f99
MK
2977
2978
2f3eb3b6 2979(defun viper-previous-line-at-bol (arg)
d5e52f99
MK
2980 "Previous line at beginning of line."
2981 (interactive "P")
2f3eb3b6 2982 (viper-leave-region-active)
d5e52f99
MK
2983 (save-excursion
2984 (beginning-of-line)
2985 (if (bobp) (error "First line in buffer")))
2f3eb3b6
MK
2986 (let ((val (viper-p-val arg))
2987 (com (viper-getCom arg)))
2988 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
2989 (forward-line (- val))
2990 (back-to-indentation)
2f3eb3b6 2991 (if com (viper-execute-com 'viper-previous-line val com))))
d5e52f99 2992
2f3eb3b6 2993(defun viper-change-to-eol (arg)
d5e52f99
MK
2994 "Change to end of line."
2995 (interactive "P")
2f3eb3b6 2996 (viper-goto-eol (cons arg ?c)))
d5e52f99 2997
2f3eb3b6 2998(defun viper-kill-line (arg)
d5e52f99
MK
2999 "Delete line."
3000 (interactive "P")
2f3eb3b6 3001 (viper-goto-eol (cons arg ?d)))
d5e52f99 3002
2f3eb3b6 3003(defun viper-erase-line (arg)
d5e52f99
MK
3004 "Erase line."
3005 (interactive "P")
2f3eb3b6 3006 (viper-beginning-of-line (cons arg ?d)))
d5e52f99
MK
3007
3008\f
3009;;; Moving around
3010
2f3eb3b6 3011(defun viper-goto-line (arg)
d5e52f99
MK
3012 "Go to ARG's line. Without ARG go to end of buffer."
3013 (interactive "P")
2f3eb3b6
MK
3014 (let ((val (viper-P-val arg))
3015 (com (viper-getCom arg)))
3016 (viper-move-marker-locally 'viper-com-point (point))
3017 (viper-deactivate-mark)
d5e52f99
MK
3018 (push-mark nil t)
3019 (if (null val)
3020 (goto-char (point-max))
3021 (goto-char (point-min))
3022 (forward-line (1- val)))
f1097063 3023
d5e52f99
MK
3024 ;; positioning is done twice: before and after command execution
3025 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3026 (back-to-indentation)
f1097063 3027
2f3eb3b6 3028 (if com (viper-execute-com 'viper-goto-line val com))
f1097063 3029
d5e52f99
MK
3030 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3031 (back-to-indentation)
3032 ))
3033
f1097063 3034;; Find ARG's occurrence of CHAR on the current line.
d5e52f99
MK
3035;; If FORWARD then search is forward, otherwise backward. OFFSET is used to
3036;; adjust point after search.
2f3eb3b6 3037(defun viper-find-char (arg char forward offset)
d5e52f99
MK
3038 (or (char-or-string-p char) (error ""))
3039 (let ((arg (if forward arg (- arg)))
2f3eb3b6
MK
3040 (cmd (if (eq viper-intermediate-command 'viper-repeat)
3041 (nth 5 viper-d-com)
3042 (viper-array-to-string (this-command-keys))))
2eb4bdca 3043 point region-beg region-end)
d5e52f99
MK
3044 (save-excursion
3045 (save-restriction
2eb4bdca
MK
3046 (if (> arg 0) ; forward
3047 (progn
3048 (setq region-beg (point))
3049 (if viper-allow-multiline-replace-regions
3050 (viper-forward-paragraph 1)
3051 (end-of-line))
3052 (setq region-end (point)))
3053 (setq region-end (point))
3054 (if viper-allow-multiline-replace-regions
3055 (viper-backward-paragraph 1)
3056 (beginning-of-line))
3057 (setq region-beg (point)))
3058 (if (or (and (< arg 0)
3059 (< (- region-end region-beg)
3060 (if viper-allow-multiline-replace-regions
3061 2 1))
3062 (bolp))
3063 (and (> arg 0)
3064 (< (- region-end region-beg)
3065 (if viper-allow-multiline-replace-regions
3066 3 2))
3067 (eolp)))
3068 (error "Command `%s': At %s of %s"
3069 cmd
3070 (if (> arg 0) "end" "beginning")
3071 (if viper-allow-multiline-replace-regions
3072 "paragraph" "line")))
3073 (narrow-to-region region-beg region-end)
d5e52f99
MK
3074 ;; if arg > 0, point is forwarded before search.
3075 (if (> arg 0) (goto-char (1+ (point-min)))
3076 (goto-char (point-max)))
3077 (if (let ((case-fold-search nil))
3078 (search-forward (char-to-string char) nil 0 arg))
3079 (setq point (point))
3080 (error "Command `%s': `%c' not found" cmd char))))
34317da2
MK
3081 (goto-char point)
3082 (if (> arg 0)
3083 (backward-char (if offset 2 1))
3084 (forward-char (if offset 1 0)))))
d5e52f99 3085
2f3eb3b6 3086(defun viper-find-char-forward (arg)
f1097063 3087 "Find char on the line.
d5e52f99 3088If called interactively read the char to find from the terminal, and if
2f3eb3b6 3089called from viper-repeat, the char last used is used. This behaviour is
d5e52f99
MK
3090controlled by the sign of prefix numeric value."
3091 (interactive "P")
2f3eb3b6
MK
3092 (let ((val (viper-p-val arg))
3093 (com (viper-getcom arg))
3094 (cmd-representation (nth 5 viper-d-com)))
d5e52f99
MK
3095 (if (> val 0)
3096 ;; this means that the function was called interactively
2f3eb3b6
MK
3097 (setq viper-f-char (read-char)
3098 viper-f-forward t
3099 viper-f-offset nil)
3100 ;; viper-repeat --- set viper-F-char from command-keys
3101 (setq viper-F-char (if (stringp cmd-representation)
3102 (viper-seq-last-elt cmd-representation)
3103 viper-F-char)
3104 viper-f-char viper-F-char)
d5e52f99 3105 (setq val (- val)))
2f3eb3b6
MK
3106 (if com (viper-move-marker-locally 'viper-com-point (point)))
3107 (viper-find-char
3108 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) t nil)
d5e52f99
MK
3109 (setq val (- val))
3110 (if com
3111 (progn
2f3eb3b6 3112 (setq viper-F-char viper-f-char) ; set new viper-F-char
d5e52f99 3113 (forward-char)
2f3eb3b6 3114 (viper-execute-com 'viper-find-char-forward val com)))))
d5e52f99 3115
2f3eb3b6 3116(defun viper-goto-char-forward (arg)
d5e52f99
MK
3117 "Go up to char ARG forward on line."
3118 (interactive "P")
2f3eb3b6
MK
3119 (let ((val (viper-p-val arg))
3120 (com (viper-getcom arg))
3121 (cmd-representation (nth 5 viper-d-com)))
d5e52f99
MK
3122 (if (> val 0)
3123 ;; this means that the function was called interactively
2f3eb3b6
MK
3124 (setq viper-f-char (read-char)
3125 viper-f-forward t
3126 viper-f-offset t)
3127 ;; viper-repeat --- set viper-F-char from command-keys
3128 (setq viper-F-char (if (stringp cmd-representation)
3129 (viper-seq-last-elt cmd-representation)
3130 viper-F-char)
3131 viper-f-char viper-F-char)
d5e52f99 3132 (setq val (- val)))
2f3eb3b6
MK
3133 (if com (viper-move-marker-locally 'viper-com-point (point)))
3134 (viper-find-char
3135 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) t t)
d5e52f99
MK
3136 (setq val (- val))
3137 (if com
3138 (progn
2f3eb3b6 3139 (setq viper-F-char viper-f-char) ; set new viper-F-char
d5e52f99 3140 (forward-char)
2f3eb3b6 3141 (viper-execute-com 'viper-goto-char-forward val com)))))
d5e52f99 3142
2f3eb3b6 3143(defun viper-find-char-backward (arg)
d5e52f99
MK
3144 "Find char ARG on line backward."
3145 (interactive "P")
2f3eb3b6
MK
3146 (let ((val (viper-p-val arg))
3147 (com (viper-getcom arg))
3148 (cmd-representation (nth 5 viper-d-com)))
d5e52f99
MK
3149 (if (> val 0)
3150 ;; this means that the function was called interactively
2f3eb3b6
MK
3151 (setq viper-f-char (read-char)
3152 viper-f-forward nil
3153 viper-f-offset nil)
3154 ;; viper-repeat --- set viper-F-char from command-keys
3155 (setq viper-F-char (if (stringp cmd-representation)
3156 (viper-seq-last-elt cmd-representation)
3157 viper-F-char)
3158 viper-f-char viper-F-char)
d5e52f99 3159 (setq val (- val)))
2f3eb3b6
MK
3160 (if com (viper-move-marker-locally 'viper-com-point (point)))
3161 (viper-find-char
3162 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) nil nil)
d5e52f99
MK
3163 (setq val (- val))
3164 (if com
3165 (progn
2f3eb3b6
MK
3166 (setq viper-F-char viper-f-char) ; set new viper-F-char
3167 (viper-execute-com 'viper-find-char-backward val com)))))
d5e52f99 3168
2f3eb3b6 3169(defun viper-goto-char-backward (arg)
d5e52f99
MK
3170 "Go up to char ARG backward on line."
3171 (interactive "P")
2f3eb3b6
MK
3172 (let ((val (viper-p-val arg))
3173 (com (viper-getcom arg))
3174 (cmd-representation (nth 5 viper-d-com)))
d5e52f99
MK
3175 (if (> val 0)
3176 ;; this means that the function was called interactively
2f3eb3b6
MK
3177 (setq viper-f-char (read-char)
3178 viper-f-forward nil
3179 viper-f-offset t)
3180 ;; viper-repeat --- set viper-F-char from command-keys
3181 (setq viper-F-char (if (stringp cmd-representation)
3182 (viper-seq-last-elt cmd-representation)
3183 viper-F-char)
3184 viper-f-char viper-F-char)
d5e52f99 3185 (setq val (- val)))
2f3eb3b6
MK
3186 (if com (viper-move-marker-locally 'viper-com-point (point)))
3187 (viper-find-char
3188 val (if (> (viper-p-val arg) 0) viper-f-char viper-F-char) nil t)
d5e52f99
MK
3189 (setq val (- val))
3190 (if com
3191 (progn
2f3eb3b6
MK
3192 (setq viper-F-char viper-f-char) ; set new viper-F-char
3193 (viper-execute-com 'viper-goto-char-backward val com)))))
d5e52f99 3194
2f3eb3b6 3195(defun viper-repeat-find (arg)
d5e52f99
MK
3196 "Repeat previous find command."
3197 (interactive "P")
2f3eb3b6
MK
3198 (let ((val (viper-p-val arg))
3199 (com (viper-getcom arg)))
3200 (viper-deactivate-mark)
3201 (if com (viper-move-marker-locally 'viper-com-point (point)))
3202 (viper-find-char val viper-f-char viper-f-forward viper-f-offset)
d5e52f99
MK
3203 (if com
3204 (progn
2f3eb3b6
MK
3205 (if viper-f-forward (forward-char))
3206 (viper-execute-com 'viper-repeat-find val com)))))
d5e52f99 3207
2f3eb3b6 3208(defun viper-repeat-find-opposite (arg)
d5e52f99
MK
3209 "Repeat previous find command in the opposite direction."
3210 (interactive "P")
2f3eb3b6
MK
3211 (let ((val (viper-p-val arg))
3212 (com (viper-getcom arg)))
3213 (viper-deactivate-mark)
3214 (if com (viper-move-marker-locally 'viper-com-point (point)))
3215 (viper-find-char val viper-f-char (not viper-f-forward) viper-f-offset)
d5e52f99
MK
3216 (if com
3217 (progn
2f3eb3b6
MK
3218 (if viper-f-forward (forward-char))
3219 (viper-execute-com 'viper-repeat-find-opposite val com)))))
d5e52f99
MK
3220
3221\f
3222;; window scrolling etc.
3223
2f3eb3b6 3224(defun viper-window-top (arg)
d5e52f99
MK
3225 "Go to home window line."
3226 (interactive "P")
2f3eb3b6
MK
3227 (let ((val (viper-p-val arg))
3228 (com (viper-getCom arg)))
3af0304a 3229 (viper-leave-region-active)
2f3eb3b6 3230 (if com (viper-move-marker-locally 'viper-com-point (point)))
f1097063 3231 (push-mark nil t)
d5e52f99
MK
3232 (move-to-window-line (1- val))
3233
3234 ;; positioning is done twice: before and after command execution
3235 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3236 (back-to-indentation)
f1097063 3237
2f3eb3b6 3238 (if com (viper-execute-com 'viper-window-top val com))
f1097063 3239
d5e52f99
MK
3240 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3241 (back-to-indentation)
3242 ))
3243
2f3eb3b6 3244(defun viper-window-middle (arg)
d5e52f99
MK
3245 "Go to middle window line."
3246 (interactive "P")
2f3eb3b6 3247 (let ((val (viper-p-val arg))
3af0304a
MK
3248 (com (viper-getCom arg)))
3249 (viper-leave-region-active)
2f3eb3b6 3250 (if com (viper-move-marker-locally 'viper-com-point (point)))
f1097063 3251 (push-mark nil t)
3af0304a 3252 (move-to-window-line (+ (/ (1- (window-height)) 2) (1- val)))
f1097063 3253
d5e52f99
MK
3254 ;; positioning is done twice: before and after command execution
3255 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3256 (back-to-indentation)
3257
2f3eb3b6 3258 (if com (viper-execute-com 'viper-window-middle val com))
f1097063 3259
d5e52f99
MK
3260 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3261 (back-to-indentation)
3262 ))
3263
2f3eb3b6 3264(defun viper-window-bottom (arg)
d5e52f99
MK
3265 "Go to last window line."
3266 (interactive "P")
2f3eb3b6
MK
3267 (let ((val (viper-p-val arg))
3268 (com (viper-getCom arg)))
3af0304a 3269 (viper-leave-region-active)
2f3eb3b6 3270 (if com (viper-move-marker-locally 'viper-com-point (point)))
f1097063 3271 (push-mark nil t)
d5e52f99 3272 (move-to-window-line (- val))
f1097063 3273
d5e52f99
MK
3274 ;; positioning is done twice: before and after command execution
3275 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3276 (back-to-indentation)
3277
2f3eb3b6 3278 (if com (viper-execute-com 'viper-window-bottom val com))
f1097063 3279
d5e52f99
MK
3280 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3281 (back-to-indentation)
3282 ))
3283
2f3eb3b6 3284(defun viper-line-to-top (arg)
d5e52f99
MK
3285 "Put current line on the home line."
3286 (interactive "p")
3287 (recenter (1- arg)))
3288
2f3eb3b6 3289(defun viper-line-to-middle (arg)
d5e52f99
MK
3290 "Put current line on the middle line."
3291 (interactive "p")
3292 (recenter (+ (1- arg) (/ (1- (window-height)) 2))))
3293
2f3eb3b6 3294(defun viper-line-to-bottom (arg)
d5e52f99
MK
3295 "Put current line on the last line."
3296 (interactive "p")
3297 (recenter (- (window-height) (1+ arg))))
3298
2f3eb3b6 3299;; If point is within viper-search-scroll-threshold of window top or bottom,
d5e52f99 3300;; scroll up or down 1/7 of window height, depending on whether we are at the
3af0304a
MK
3301;; bottom or at the top of the window. This function is called by viper-search
3302;; (which is called from viper-search-forward/backward/next). If the value of
2f3eb3b6
MK
3303;; viper-search-scroll-threshold is negative - don't scroll.
3304(defun viper-adjust-window ()
3305 (let ((win-height (if viper-emacs-p
d5e52f99
MK
3306 (1- (window-height)) ; adjust for modeline
3307 (window-displayed-height)))
3308 (pt (point))
3309 at-top-p at-bottom-p
3310 min-scroll direction)
3311 (save-excursion
3312 (move-to-window-line 0) ; top
3313 (setq at-top-p
3314 (<= (count-lines pt (point))
2f3eb3b6 3315 viper-search-scroll-threshold))
d5e52f99
MK
3316 (move-to-window-line -1) ; bottom
3317 (setq at-bottom-p
2f3eb3b6 3318 (<= (count-lines pt (point)) viper-search-scroll-threshold))
d5e52f99 3319 )
2f3eb3b6 3320 (cond (at-top-p (setq min-scroll (1- viper-search-scroll-threshold)
d5e52f99 3321 direction 1))
2f3eb3b6 3322 (at-bottom-p (setq min-scroll (1+ viper-search-scroll-threshold)
d5e52f99
MK
3323 direction -1)))
3324 (if min-scroll
3325 (recenter
3326 (* (max min-scroll (/ win-height 7)) direction)))
3327 ))
3328
3329\f
3330;; paren match
3af0304a 3331;; must correct this to only match ( to ) etc. On the other hand
d5e52f99 3332;; it is good that paren match gets confused, because that way you
f1097063 3333;; catch _all_ imbalances.
d5e52f99 3334
2f3eb3b6 3335(defun viper-paren-match (arg)
d5e52f99
MK
3336 "Go to the matching parenthesis."
3337 (interactive "P")
2f3eb3b6
MK
3338 (viper-leave-region-active)
3339 (let ((com (viper-getcom arg))
3340 (parse-sexp-ignore-comments viper-parse-sexp-ignore-comments)
d5e52f99
MK
3341 anchor-point)
3342 (if (integerp arg)
3343 (if (or (> arg 99) (< arg 1))
3344 (error "Prefix must be between 1 and 99")
3345 (goto-char
3346 (if (> (point-max) 80000)
3347 (* (/ (point-max) 100) arg)
3348 (/ (* (point-max) arg) 100)))
3349 (back-to-indentation))
3350 (let (beg-lim end-lim)
3351 (if (and (eolp) (not (bolp))) (forward-char -1))
3352 (if (not (looking-at "[][(){}]"))
3353 (setq anchor-point (point)))
3354 (save-excursion
3355 (beginning-of-line)
3356 (setq beg-lim (point))
3357 (end-of-line)
3358 (setq end-lim (point)))
f1097063 3359 (cond ((re-search-forward "[][(){}]" end-lim t)
d5e52f99
MK
3360 (backward-char) )
3361 ((re-search-backward "[][(){}]" beg-lim t))
3362 (t
3363 (error "No matching character on line"))))
3364 (cond ((looking-at "[\(\[{]")
2f3eb3b6 3365 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
3366 (forward-sexp 1)
3367 (if com
2f3eb3b6 3368 (viper-execute-com 'viper-paren-match nil com)
d5e52f99
MK
3369 (backward-char)))
3370 (anchor-point
3371 (if com
3372 (progn
2f3eb3b6 3373 (viper-move-marker-locally 'viper-com-point anchor-point)
d5e52f99 3374 (forward-char 1)
2f3eb3b6 3375 (viper-execute-com 'viper-paren-match nil com)
d5e52f99
MK
3376 )))
3377 ((looking-at "[])}]")
3378 (forward-char)
2f3eb3b6 3379 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 3380 (backward-sexp 1)
2f3eb3b6 3381 (if com (viper-execute-com 'viper-paren-match nil com)))
d5e52f99
MK
3382 (t (error ""))))))
3383
2f3eb3b6 3384(defun viper-toggle-parse-sexp-ignore-comments ()
d5e52f99 3385 (interactive)
2f3eb3b6
MK
3386 (setq viper-parse-sexp-ignore-comments
3387 (not viper-parse-sexp-ignore-comments))
1e70790f
MK
3388 (princ (format
3389 "From now on, `%%' will %signore parentheses inside comment fields"
2f3eb3b6 3390 (if viper-parse-sexp-ignore-comments "" "NOT "))))
d5e52f99
MK
3391
3392\f
2eb4bdca 3393;; sentence, paragraph and heading
d5e52f99 3394
2f3eb3b6 3395(defun viper-forward-sentence (arg)
d5e52f99
MK
3396 "Forward sentence."
3397 (interactive "P")
8e41a31c
MK
3398 (or (eq last-command this-command)
3399 (push-mark nil t))
2f3eb3b6
MK
3400 (let ((val (viper-p-val arg))
3401 (com (viper-getcom arg)))
3402 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 3403 (forward-sentence val)
2f3eb3b6 3404 (if com (viper-execute-com 'viper-forward-sentence nil com))))
d5e52f99 3405
2f3eb3b6 3406(defun viper-backward-sentence (arg)
d5e52f99
MK
3407 "Backward sentence."
3408 (interactive "P")
8e41a31c
MK
3409 (or (eq last-command this-command)
3410 (push-mark nil t))
2f3eb3b6
MK
3411 (let ((val (viper-p-val arg))
3412 (com (viper-getcom arg)))
3413 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 3414 (backward-sentence val)
2f3eb3b6 3415 (if com (viper-execute-com 'viper-backward-sentence nil com))))
d5e52f99 3416
2f3eb3b6 3417(defun viper-forward-paragraph (arg)
d5e52f99
MK
3418 "Forward paragraph."
3419 (interactive "P")
8e41a31c
MK
3420 (or (eq last-command this-command)
3421 (push-mark nil t))
2f3eb3b6 3422 (let ((val (viper-p-val arg))
2eb4bdca
MK
3423 ;; if you want d} operate on whole lines, change viper-getcom to
3424 ;; viper-getCom below
3425 (com (viper-getcom arg)))
2f3eb3b6 3426 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
3427 (forward-paragraph val)
3428 (if com
3429 (progn
3430 (backward-char 1)
2f3eb3b6 3431 (viper-execute-com 'viper-forward-paragraph nil com)))))
d5e52f99 3432
2f3eb3b6 3433(defun viper-backward-paragraph (arg)
d5e52f99
MK
3434 "Backward paragraph."
3435 (interactive "P")
8e41a31c
MK
3436 (or (eq last-command this-command)
3437 (push-mark nil t))
2f3eb3b6 3438 (let ((val (viper-p-val arg))
2eb4bdca
MK
3439 ;; if you want d{ operate on whole lines, change viper-getcom to
3440 ;; viper-getCom below
3441 (com (viper-getcom arg)))
2f3eb3b6 3442 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99
MK
3443 (backward-paragraph val)
3444 (if com
3445 (progn
3446 (forward-char 1)
2f3eb3b6 3447 (viper-execute-com 'viper-backward-paragraph nil com)
d5e52f99
MK
3448 (backward-char 1)))))
3449
8e41a31c 3450;; should be mode-specific
2f3eb3b6 3451(defun viper-prev-heading (arg)
d5e52f99 3452 (interactive "P")
2f3eb3b6
MK
3453 (let ((val (viper-p-val arg))
3454 (com (viper-getCom arg)))
3455 (if com (viper-move-marker-locally 'viper-com-point (point)))
3456 (re-search-backward viper-heading-start nil t val)
d5e52f99 3457 (goto-char (match-beginning 0))
2f3eb3b6 3458 (if com (viper-execute-com 'viper-prev-heading nil com))))
d5e52f99 3459
2f3eb3b6 3460(defun viper-heading-end (arg)
d5e52f99 3461 (interactive "P")
2f3eb3b6
MK
3462 (let ((val (viper-p-val arg))
3463 (com (viper-getCom arg)))
3464 (if com (viper-move-marker-locally 'viper-com-point (point)))
3465 (re-search-forward viper-heading-end nil t val)
d5e52f99 3466 (goto-char (match-beginning 0))
2f3eb3b6 3467 (if com (viper-execute-com 'viper-heading-end nil com))))
d5e52f99 3468
2f3eb3b6 3469(defun viper-next-heading (arg)
d5e52f99 3470 (interactive "P")
2f3eb3b6
MK
3471 (let ((val (viper-p-val arg))
3472 (com (viper-getCom arg)))
3473 (if com (viper-move-marker-locally 'viper-com-point (point)))
d5e52f99 3474 (end-of-line)
2f3eb3b6 3475 (re-search-forward viper-heading-start nil t val)
d5e52f99 3476 (goto-char (match-beginning 0))
2f3eb3b6 3477 (if com (viper-execute-com 'viper-next-heading nil com))))
d5e52f99
MK
3478
3479\f
3480;; scrolling
3481
2f3eb3b6 3482(defun viper-scroll-screen (arg)
d5e52f99
MK
3483 "Scroll to next screen."
3484 (interactive "p")
3485 (condition-case nil
3486 (if (> arg 0)
3487 (while (> arg 0)
3488 (scroll-up)
3489 (setq arg (1- arg)))
3490 (while (> 0 arg)
3491 (scroll-down)
3492 (setq arg (1+ arg))))
3493 (error (beep 1)
3494 (if (> arg 0)
3495 (progn
3496 (message "End of buffer")
3497 (goto-char (point-max)))
3498 (message "Beginning of buffer")
3499 (goto-char (point-min))))
3500 ))
3501
2f3eb3b6 3502(defun viper-scroll-screen-back (arg)
d5e52f99
MK
3503 "Scroll to previous screen."
3504 (interactive "p")
2f3eb3b6 3505 (viper-scroll-screen (- arg)))
d5e52f99 3506
2f3eb3b6 3507(defun viper-scroll-down (arg)
d5e52f99
MK
3508 "Pull down half screen."
3509 (interactive "P")
3510 (condition-case nil
3511 (if (null arg)
3512 (scroll-down (/ (window-height) 2))
3513 (scroll-down arg))
3514 (error (beep 1)
3515 (message "Beginning of buffer")
3516 (goto-char (point-min)))))
3517
2f3eb3b6 3518(defun viper-scroll-down-one (arg)
d5e52f99
MK
3519 "Scroll up one line."
3520 (interactive "p")
3521 (scroll-down arg))
3522
2f3eb3b6 3523(defun viper-scroll-up (arg)
d5e52f99
MK
3524 "Pull up half screen."
3525 (interactive "P")
3526 (condition-case nil
3527 (if (null arg)
3528 (scroll-up (/ (window-height) 2))
3529 (scroll-up arg))
3530 (error (beep 1)
3531 (message "End of buffer")
3532 (goto-char (point-max)))))
3533
2f3eb3b6 3534(defun viper-scroll-up-one (arg)
d5e52f99
MK
3535 "Scroll down one line."
3536 (interactive "p")
3537 (scroll-up arg))
3538
3539\f
3540;; searching
3541
2f3eb3b6
MK
3542(defun viper-if-string (prompt)
3543 (if (memq viper-intermediate-command
3544 '(viper-command-argument viper-digit-argument viper-repeat))
3545 (setq viper-this-command-keys (this-command-keys)))
3546 (let ((s (viper-read-string-with-history
d5e52f99
MK
3547 prompt
3548 nil ; no initial
2f3eb3b6
MK
3549 'viper-search-history
3550 (car viper-search-history))))
d5e52f99 3551 (if (not (string= s ""))
f1097063
SS
3552 (setq viper-s-string s))))
3553
3554
3555(defun viper-toggle-search-style (arg)
2f3eb3b6 3556 "Toggle the value of viper-case-fold-search/viper-re-search.
3af0304a 3557Without prefix argument, will ask which search style to toggle. With prefix
2f3eb3b6 3558arg 1,toggles viper-case-fold-search; with arg 2 toggles viper-re-search.
d5e52f99 3559
2f3eb3b6 3560Although this function is bound to \\[viper-toggle-search-style], the most
d5e52f99 3561convenient way to use it is to bind `//' to the macro
2f3eb3b6 3562`1 M-x viper-toggle-search-style' and `///' to
3af0304a 3563`2 M-x viper-toggle-search-style'. In this way, hitting `//' quickly will
d5e52f99 3564toggle case-fold-search and hitting `/' three times witth toggle regexp
3af0304a 3565search. Macros are more convenient in this case because they don't affect
d5e52f99
MK
3566the Emacs binding of `/'."
3567 (interactive "P")
3568 (let (msg)
3569 (cond ((or (eq arg 1)
3570 (and (null arg)
3af0304a 3571 (y-or-n-p (format "Search style: '%s'. Want '%s'? "
2f3eb3b6 3572 (if viper-case-fold-search
d5e52f99 3573 "case-insensitive" "case-sensitive")
2f3eb3b6 3574 (if viper-case-fold-search
d5e52f99
MK
3575 "case-sensitive"
3576 "case-insensitive")))))
2f3eb3b6
MK
3577 (setq viper-case-fold-search (null viper-case-fold-search))
3578 (if viper-case-fold-search
d5e52f99
MK
3579 (setq msg "Search becomes case-insensitive")
3580 (setq msg "Search becomes case-sensitive")))
3581 ((or (eq arg 2)
3582 (and (null arg)
3af0304a 3583 (y-or-n-p (format "Search style: '%s'. Want '%s'? "
2f3eb3b6 3584 (if viper-re-search
d5e52f99 3585 "regexp-search" "vanilla-search")
2f3eb3b6 3586 (if viper-re-search
d5e52f99
MK
3587 "vanilla-search"
3588 "regexp-search")))))
2f3eb3b6
MK
3589 (setq viper-re-search (null viper-re-search))
3590 (if viper-re-search
d5e52f99
MK
3591 (setq msg "Search becomes regexp-style")
3592 (setq msg "Search becomes vanilla-style")))
3593 (t
3594 (setq msg "Search style remains unchanged")))
1e70790f 3595 (princ msg t)))
d5e52f99 3596
2f3eb3b6 3597(defun viper-set-searchstyle-toggling-macros (unset)
d5e52f99
MK
3598 "Set the macros for toggling the search style in Viper's vi-state.
3599The macro that toggles case sensitivity is bound to `//', and the one that
3600toggles regexp search is bound to `///'.
3601With a prefix argument, this function unsets the macros. "
3602 (interactive "P")
3603 (or noninteractive
3604 (if (not unset)
3605 (progn
3606 ;; toggle case sensitivity in search
2f3eb3b6 3607 (viper-record-kbd-macro
d5e52f99 3608 "//" 'vi-state
2f3eb3b6 3609 [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
3610 't)
3611 ;; toggle regexp/vanila search
2f3eb3b6 3612 (viper-record-kbd-macro
d5e52f99 3613 "///" 'vi-state
2f3eb3b6 3614 [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
3615 't)
3616 (if (interactive-p)
3617 (message
1e70790f 3618 "// and /// now toggle case-sensitivity and regexp search")))
2f3eb3b6 3619 (viper-unrecord-kbd-macro "//" 'vi-state)
d5e52f99 3620 (sit-for 2)
2f3eb3b6 3621 (viper-unrecord-kbd-macro "///" 'vi-state))))
d5e52f99 3622
1e70790f 3623
2f3eb3b6 3624(defun viper-set-parsing-style-toggling-macro (unset)
1e70790f
MK
3625 "Set `%%%' to be a macro that toggles whether comment fields should be parsed for matching parentheses.
3626This is used in conjunction with the `%' command.
3627
3628With a prefix argument, unsets the macro."
3629 (interactive "P")
3630 (or noninteractive
3631 (if (not unset)
3632 (progn
3633 ;; Make %%% toggle parsing comments for matching parentheses
2f3eb3b6 3634 (viper-record-kbd-macro
1e70790f 3635 "%%%" 'vi-state
2f3eb3b6 3636 [(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
3637 't)
3638 (if (interactive-p)
3639 (message
3640 "%%%%%% now toggles whether comments should be parsed for matching parentheses")))
2f3eb3b6 3641 (viper-unrecord-kbd-macro "%%%" 'vi-state))))
1e70790f
MK
3642
3643
2f3eb3b6 3644(defun viper-set-emacs-state-searchstyle-macros (unset &optional arg-majormode)
d5e52f99
MK
3645 "Set the macros for toggling the search style in Viper's emacs-state.
3646The macro that toggles case sensitivity is bound to `//', and the one that
3647toggles regexp search is bound to `///'.
f1097063 3648With a prefix argument, this function unsets the macros.
d5e52f99 3649If the optional prefix argument is non-nil and specifies a valid major mode,
3af0304a 3650this sets the macros only in the macros in that major mode. Otherwise,
d5e52f99
MK
3651the macros are set in the current major mode.
3652\(When unsetting the macros, the second argument has no effect.\)"
3653 (interactive "P")
3654 (or noninteractive
3655 (if (not unset)
3656 (progn
3657 ;; toggle case sensitivity in search
2f3eb3b6 3658 (viper-record-kbd-macro
d5e52f99 3659 "//" 'emacs-state
f1097063 3660 [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
3661 (or arg-majormode major-mode))
3662 ;; toggle regexp/vanila search
2f3eb3b6 3663 (viper-record-kbd-macro
d5e52f99 3664 "///" 'emacs-state
2f3eb3b6 3665 [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
3666 (or arg-majormode major-mode))
3667 (if (interactive-p)
3668 (message
3669 "// and /// now toggle case-sensitivity and regexp search.")))
2f3eb3b6 3670 (viper-unrecord-kbd-macro "//" 'emacs-state)
d5e52f99 3671 (sit-for 2)
2f3eb3b6 3672 (viper-unrecord-kbd-macro "///" 'emacs-state))))
d5e52f99
MK
3673
3674
2f3eb3b6 3675(defun viper-search-forward (arg)
f1097063 3676 "Search a string forward.
d5e52f99
MK
3677ARG is used to find the ARG's occurrence of the string.
3678Null string will repeat previous search."
3679 (interactive "P")
2f3eb3b6
MK
3680 (let ((val (viper-P-val arg))
3681 (com (viper-getcom arg))
3682 (old-str viper-s-string))
3683 (setq viper-s-forward t)
3684 (viper-if-string "/")
d5e52f99 3685 ;; this is not used at present, but may be used later
2f3eb3b6
MK
3686 (if (or (not (equal old-str viper-s-string))
3687 (not (markerp viper-local-search-start-marker))
3688 (not (marker-buffer viper-local-search-start-marker)))
3689 (setq viper-local-search-start-marker (point-marker)))
3690 (viper-search viper-s-string t val)
d5e52f99
MK
3691 (if com
3692 (progn
2f3eb3b6
MK
3693 (viper-move-marker-locally 'viper-com-point (mark t))
3694 (viper-execute-com 'viper-search-next val com)))))
d5e52f99 3695
2f3eb3b6 3696(defun viper-search-backward (arg)
f1097063 3697 "Search a string backward.
d5e52f99
MK
3698ARG is used to find the ARG's occurrence of the string.
3699Null string will repeat previous search."
3700 (interactive "P")
2f3eb3b6
MK
3701 (let ((val (viper-P-val arg))
3702 (com (viper-getcom arg))
3703 (old-str viper-s-string))
3704 (setq viper-s-forward nil)
3705 (viper-if-string "?")
d5e52f99 3706 ;; this is not used at present, but may be used later
2f3eb3b6
MK
3707 (if (or (not (equal old-str viper-s-string))
3708 (not (markerp viper-local-search-start-marker))
3709 (not (marker-buffer viper-local-search-start-marker)))
3710 (setq viper-local-search-start-marker (point-marker)))
3711 (viper-search viper-s-string nil val)
d5e52f99
MK
3712 (if com
3713 (progn
2f3eb3b6
MK
3714 (viper-move-marker-locally 'viper-com-point (mark t))
3715 (viper-execute-com 'viper-search-next val com)))))
f1097063 3716
d5e52f99
MK
3717
3718;; Search for COUNT's occurrence of STRING.
3719;; Search is forward if FORWARD is non-nil, otherwise backward.
3720;; INIT-POINT is the position where search is to start.
3721;; Arguments:
3722;; (STRING FORW COUNT &optional NO-OFFSET INIT-POINT LIMIT FAIL-IF-NOT-FOUND)
2f3eb3b6
MK
3723(defun viper-search (string forward arg
3724 &optional no-offset init-point fail-if-not-found)
d5e52f99 3725 (if (not (equal string ""))
2f3eb3b6
MK
3726 (let ((val (viper-p-val arg))
3727 (com (viper-getcom arg))
d5e52f99 3728 (offset (not no-offset))
2f3eb3b6 3729 (case-fold-search viper-case-fold-search)
d5e52f99 3730 (start-point (or init-point (point))))
2f3eb3b6 3731 (viper-deactivate-mark)
d5e52f99
MK
3732 (if forward
3733 (condition-case nil
3734 (progn
2f3eb3b6
MK
3735 (if offset (viper-forward-char-carefully))
3736 (if viper-re-search
d5e52f99
MK
3737 (progn
3738 (re-search-forward string nil nil val)
3739 (re-search-backward string))
3740 (search-forward string nil nil val)
3741 (search-backward string))
3742 (if (not (equal start-point (point)))
f1097063 3743 (push-mark start-point t)))
d5e52f99 3744 (search-failed
2f3eb3b6 3745 (if (and (not fail-if-not-found) viper-search-wrap-around-t)
d5e52f99
MK
3746 (progn
3747 (message "Search wrapped around BOTTOM of buffer")
3748 (goto-char (point-min))
2f3eb3b6 3749 (viper-search string forward (cons 1 com) t start-point 'fail)
d5e52f99 3750 ;; don't wait in macros
2f3eb3b6 3751 (or executing-kbd-macro
f1097063 3752 (memq viper-intermediate-command
2f3eb3b6
MK
3753 '(viper-repeat
3754 viper-digit-argument
3755 viper-command-argument))
3756 (sit-for 2))
d5e52f99
MK
3757 ;; delete the wrap-around message
3758 (message "")
3759 )
3760 (goto-char start-point)
3761 (error "`%s': %s not found"
3762 string
2f3eb3b6 3763 (if viper-re-search "Pattern" "String"))
d5e52f99
MK
3764 )))
3765 ;; backward
3766 (condition-case nil
3767 (progn
2f3eb3b6 3768 (if viper-re-search
d5e52f99
MK
3769 (re-search-backward string nil nil val)
3770 (search-backward string nil nil val))
3771 (if (not (equal start-point (point)))
f1097063 3772 (push-mark start-point t)))
d5e52f99 3773 (search-failed
2f3eb3b6 3774 (if (and (not fail-if-not-found) viper-search-wrap-around-t)
d5e52f99
MK
3775 (progn
3776 (message "Search wrapped around TOP of buffer")
3777 (goto-char (point-max))
2f3eb3b6 3778 (viper-search string forward (cons 1 com) t start-point 'fail)
d5e52f99 3779 ;; don't wait in macros
2f3eb3b6 3780 (or executing-kbd-macro
f1097063 3781 (memq viper-intermediate-command
2f3eb3b6
MK
3782 '(viper-repeat
3783 viper-digit-argument
3784 viper-command-argument))
3785 (sit-for 2))
d5e52f99
MK
3786 ;; delete the wrap-around message
3787 (message "")
3788 )
3789 (goto-char start-point)
3790 (error "`%s': %s not found"
3791 string
2f3eb3b6 3792 (if viper-re-search "Pattern" "String"))
d5e52f99
MK
3793 ))))
3794 ;; pull up or down if at top/bottom of window
2f3eb3b6 3795 (viper-adjust-window)
d5e52f99
MK
3796 ;; highlight the result of search
3797 ;; don't wait and don't highlight in macros
3798 (or executing-kbd-macro
f1097063 3799 (memq viper-intermediate-command
2f3eb3b6
MK
3800 '(viper-repeat viper-digit-argument viper-command-argument))
3801 (viper-flash-search-pattern))
d5e52f99
MK
3802 )))
3803
2f3eb3b6 3804(defun viper-search-next (arg)
d5e52f99
MK
3805 "Repeat previous search."
3806 (interactive "P")
2f3eb3b6
MK
3807 (let ((val (viper-p-val arg))
3808 (com (viper-getcom arg)))
3809 (if (null viper-s-string) (error viper-NoPrevSearch))
3810 (viper-search viper-s-string viper-s-forward arg)
d5e52f99
MK
3811 (if com
3812 (progn
2f3eb3b6
MK
3813 (viper-move-marker-locally 'viper-com-point (mark t))
3814 (viper-execute-com 'viper-search-next val com)))))
d5e52f99 3815
2f3eb3b6 3816(defun viper-search-Next (arg)
d5e52f99
MK
3817 "Repeat previous search in the reverse direction."
3818 (interactive "P")
2f3eb3b6
MK
3819 (let ((val (viper-p-val arg))
3820 (com (viper-getcom arg)))
3821 (if (null viper-s-string) (error viper-NoPrevSearch))
3822 (viper-search viper-s-string (not viper-s-forward) arg)
d5e52f99
MK
3823 (if com
3824 (progn
2f3eb3b6
MK
3825 (viper-move-marker-locally 'viper-com-point (mark t))
3826 (viper-execute-com 'viper-search-Next val com)))))
d5e52f99
MK
3827
3828
3829;; Search contents of buffer defined by one of Viper's motion commands.
3830;; Repeatable via `n' and `N'.
2f3eb3b6
MK
3831(defun viper-buffer-search-enable (&optional c)
3832 (cond (c (setq viper-buffer-search-char c))
3833 ((null viper-buffer-search-char)
3834 (setq viper-buffer-search-char ?g)))
3835 (define-key viper-vi-basic-map
f1097063 3836 (cond ((viper-characterp viper-buffer-search-char)
3af0304a
MK
3837 (char-to-string viper-buffer-search-char))
3838 (t (error "viper-buffer-search-char: wrong value type, %s"
3839 viper-buffer-search-char)))
3840 'viper-command-argument)
2f3eb3b6
MK
3841 (aset viper-exec-array viper-buffer-search-char 'viper-exec-buffer-search)
3842 (setq viper-prefix-commands
3843 (cons viper-buffer-search-char viper-prefix-commands)))
d5e52f99
MK
3844
3845;; This is a Viper wraper for isearch-forward.
2f3eb3b6 3846(defun viper-isearch-forward (arg)
d5e52f99
MK
3847 "Do incremental search forward."
3848 (interactive "P")
3849 ;; emacs bug workaround
3850 (if (listp arg) (setq arg (car arg)))
2f3eb3b6 3851 (viper-exec-form-in-emacs (list 'isearch-forward arg)))
d5e52f99
MK
3852
3853;; This is a Viper wraper for isearch-backward."
2f3eb3b6 3854(defun viper-isearch-backward (arg)
d5e52f99
MK
3855 "Do incremental search backward."
3856 (interactive "P")
3857 ;; emacs bug workaround
3858 (if (listp arg) (setq arg (car arg)))
2f3eb3b6 3859 (viper-exec-form-in-emacs (list 'isearch-backward arg)))
d5e52f99
MK
3860
3861\f
3862;; visiting and killing files, buffers
3863
2f3eb3b6 3864(defun viper-switch-to-buffer ()
d5e52f99
MK
3865 "Switch to buffer in the current window."
3866 (interactive)
6d459c4d
KH
3867 (let ((other-buffer (other-buffer (current-buffer)))
3868 buffer)
d5e52f99 3869 (setq buffer
3af0304a
MK
3870 (funcall viper-read-buffer-function
3871 "Switch to buffer in this window: " other-buffer))
6d459c4d 3872 (switch-to-buffer buffer)))
d5e52f99 3873
2f3eb3b6 3874(defun viper-switch-to-buffer-other-window ()
d5e52f99
MK
3875 "Switch to buffer in another window."
3876 (interactive)
6d459c4d
KH
3877 (let ((other-buffer (other-buffer (current-buffer)))
3878 buffer)
d5e52f99 3879 (setq buffer
3af0304a
MK
3880 (funcall viper-read-buffer-function
3881 "Switch to buffer in another window: " other-buffer))
6d459c4d 3882 (switch-to-buffer-other-window buffer)))
d5e52f99 3883
2f3eb3b6 3884(defun viper-kill-buffer ()
d5e52f99
MK
3885 "Kill a buffer."
3886 (interactive)
3887 (let (buffer buffer-name)
3888 (setq buffer-name
3af0304a
MK
3889 (funcall viper-read-buffer-function
3890 (format "Kill buffer \(%s\): "
3891 (buffer-name (current-buffer)))))
d5e52f99
MK
3892 (setq buffer
3893 (if (null buffer-name)
3894 (current-buffer)
3895 (get-buffer buffer-name)))
3896 (if (null buffer) (error "`%s': No such buffer" buffer-name))
3897 (if (or (not (buffer-modified-p buffer))
f1097063 3898 (y-or-n-p
d5e52f99
MK
3899 (format
3900 "Buffer `%s' is modified, are you sure you want to kill it? "
3901 buffer-name)))
3902 (kill-buffer buffer)
3903 (error "Buffer not killed"))))
3904
f1097063 3905
d5e52f99
MK
3906\f
3907;; yank and pop
3908
2f3eb3b6 3909(defsubst viper-yank (text)
3af0304a 3910 "Yank TEXT silently. This works correctly with Emacs's yank-pop command."
d5e52f99
MK
3911 (insert text)
3912 (setq this-command 'yank))
3913
2f3eb3b6 3914(defun viper-put-back (arg)
d5e52f99
MK
3915 "Put back after point/below line."
3916 (interactive "P")
2f3eb3b6
MK
3917 (let ((val (viper-p-val arg))
3918 (text (if viper-use-register
3919 (cond ((viper-valid-register viper-use-register '(digit))
3920 (current-kill
3921 (- viper-use-register ?1) 'do-not-rotate))
3922 ((viper-valid-register viper-use-register)
3923 (get-register (downcase viper-use-register)))
3924 (t (error viper-InvalidRegister viper-use-register)))
2eb4bdca 3925 (current-kill 0)))
3af0304a 3926 sv-point chars-inserted lines-inserted)
d5e52f99 3927 (if (null text)
2f3eb3b6
MK
3928 (if viper-use-register
3929 (let ((reg viper-use-register))
3930 (setq viper-use-register nil)
3931 (error viper-EmptyRegister reg))
d5e52f99 3932 (error "")))
2f3eb3b6
MK
3933 (setq viper-use-register nil)
3934 (if (viper-end-with-a-newline-p text)
d5e52f99
MK
3935 (progn
3936 (end-of-line)
3937 (if (eobp)
3938 (insert "\n")
3939 (forward-line 1))
3940 (beginning-of-line))
2f3eb3b6
MK
3941 (if (not (eolp)) (viper-forward-char-carefully)))
3942 (set-marker (viper-mark-marker) (point) (current-buffer))
3943 (viper-set-destructive-command
3944 (list 'viper-put-back val nil viper-use-register nil nil))
2eb4bdca
MK
3945 (setq sv-point (point))
3946 (viper-loop val (viper-yank text))
3af0304a
MK
3947 (setq chars-inserted (abs (- (point) sv-point))
3948 lines-inserted (abs (count-lines (point) sv-point)))
3949 (if (or (> chars-inserted viper-change-notification-threshold)
3950 (> lines-inserted viper-change-notification-threshold))
3951 (message "Inserted %d character(s), %d line(s)"
3952 chars-inserted lines-inserted)))
d5e52f99 3953 ;; Vi puts cursor on the last char when the yanked text doesn't contain a
f1097063 3954 ;; newline; it leaves the cursor at the beginning when the text contains
d5e52f99 3955 ;; a newline
2f3eb3b6
MK
3956 (if (viper-same-line (point) (mark))
3957 (or (= (point) (mark)) (viper-backward-char-carefully))
d5e52f99
MK
3958 (exchange-point-and-mark)
3959 (if (bolp)
3960 (back-to-indentation)))
2f3eb3b6 3961 (viper-deactivate-mark))
d5e52f99 3962
2f3eb3b6 3963(defun viper-Put-back (arg)
d5e52f99
MK
3964 "Put back at point/above line."
3965 (interactive "P")
2f3eb3b6
MK
3966 (let ((val (viper-p-val arg))
3967 (text (if viper-use-register
3968 (cond ((viper-valid-register viper-use-register '(digit))
3969 (current-kill
3970 (- viper-use-register ?1) 'do-not-rotate))
3971 ((viper-valid-register viper-use-register)
3972 (get-register (downcase viper-use-register)))
3973 (t (error viper-InvalidRegister viper-use-register)))
3af0304a
MK
3974 (current-kill 0)))
3975 sv-point chars-inserted lines-inserted)
d5e52f99 3976 (if (null text)
2f3eb3b6
MK
3977 (if viper-use-register
3978 (let ((reg viper-use-register))
3979 (setq viper-use-register nil)
3980 (error viper-EmptyRegister reg))
d5e52f99 3981 (error "")))
2f3eb3b6
MK
3982 (setq viper-use-register nil)
3983 (if (viper-end-with-a-newline-p text) (beginning-of-line))
3984 (viper-set-destructive-command
3985 (list 'viper-Put-back val nil viper-use-register nil nil))
3986 (set-marker (viper-mark-marker) (point) (current-buffer))
3af0304a
MK
3987 (setq sv-point (point))
3988 (viper-loop val (viper-yank text))
3989 (setq chars-inserted (abs (- (point) sv-point))
3990 lines-inserted (abs (count-lines (point) sv-point)))
3991 (if (or (> chars-inserted viper-change-notification-threshold)
3992 (> lines-inserted viper-change-notification-threshold))
3993 (message "Inserted %d character(s), %d line(s)"
3994 chars-inserted lines-inserted)))
d5e52f99 3995 ;; Vi puts cursor on the last char when the yanked text doesn't contain a
f1097063 3996 ;; newline; it leaves the cursor at the beginning when the text contains
d5e52f99 3997 ;; a newline
2f3eb3b6
MK
3998 (if (viper-same-line (point) (mark))
3999 (or (= (point) (mark)) (viper-backward-char-carefully))
d5e52f99
MK
4000 (exchange-point-and-mark)
4001 (if (bolp)
4002 (back-to-indentation)))
2f3eb3b6 4003 (viper-deactivate-mark))
f1097063 4004
d5e52f99
MK
4005
4006;; Copy region to kill-ring.
4007;; If BEG and END do not belong to the same buffer, copy empty region.
2f3eb3b6 4008(defun viper-copy-region-as-kill (beg end)
d5e52f99
MK
4009 (condition-case nil
4010 (copy-region-as-kill beg end)
4011 (error (copy-region-as-kill beg beg))))
f1097063 4012
d5e52f99 4013
2f3eb3b6 4014(defun viper-delete-char (arg)
34317da2 4015 "Delete next character."
d5e52f99 4016 (interactive "P")
34317da2
MK
4017 (let ((val (viper-p-val arg))
4018 end-del-pos)
2f3eb3b6
MK
4019 (viper-set-destructive-command
4020 (list 'viper-delete-char val nil nil nil nil))
34317da2
MK
4021 (if (and viper-ex-style-editing
4022 (> val (viper-chars-in-region (point) (viper-line-pos 'end))))
4023 (setq val (viper-chars-in-region (point) (viper-line-pos 'end))))
2f3eb3b6 4024 (if (and viper-ex-style-motion (eolp))
d5e52f99 4025 (if (bolp) (error "") (setq val 0))) ; not bol---simply back 1 ch
34317da2
MK
4026 (save-excursion
4027 (viper-forward-char-carefully val)
4028 (setq end-del-pos (point)))
2f3eb3b6 4029 (if viper-use-register
d5e52f99 4030 (progn
2f3eb3b6
MK
4031 (cond ((viper-valid-register viper-use-register '((Letter)))
4032 (viper-append-to-register
34317da2 4033 (downcase viper-use-register) (point) end-del-pos))
2f3eb3b6 4034 ((viper-valid-register viper-use-register)
d5e52f99 4035 (copy-to-register
34317da2 4036 viper-use-register (point) end-del-pos nil))
2f3eb3b6
MK
4037 (t (error viper-InvalidRegister viper-use-register)))
4038 (setq viper-use-register nil)))
34317da2
MK
4039
4040 (delete-char val t)
2f3eb3b6 4041 (if viper-ex-style-motion
34317da2
MK
4042 (if (and (eolp) (not (bolp))) (backward-char 1)))
4043 ))
d5e52f99 4044
2f3eb3b6 4045(defun viper-delete-backward-char (arg)
3af0304a 4046 "Delete previous character. On reaching beginning of line, stop and beep."
d5e52f99 4047 (interactive "P")
34317da2
MK
4048 (let ((val (viper-p-val arg))
4049 end-del-pos)
2f3eb3b6
MK
4050 (viper-set-destructive-command
4051 (list 'viper-delete-backward-char val nil nil nil nil))
f1097063 4052 (if (and
34317da2
MK
4053 viper-ex-style-editing
4054 (> val (viper-chars-in-region (viper-line-pos 'start) (point))))
4055 (setq val (viper-chars-in-region (viper-line-pos 'start) (point))))
4056 (save-excursion
4057 (viper-backward-char-carefully val)
4058 (setq end-del-pos (point)))
2f3eb3b6 4059 (if viper-use-register
d5e52f99 4060 (progn
2f3eb3b6
MK
4061 (cond ((viper-valid-register viper-use-register '(Letter))
4062 (viper-append-to-register
34317da2 4063 (downcase viper-use-register) end-del-pos (point)))
2f3eb3b6 4064 ((viper-valid-register viper-use-register)
d5e52f99 4065 (copy-to-register
34317da2 4066 viper-use-register end-del-pos (point) nil))
2f3eb3b6
MK
4067 (t (error viper-InvalidRegister viper-use-register)))
4068 (setq viper-use-register nil)))
34317da2
MK
4069 (if (and (bolp) viper-ex-style-editing)
4070 (ding))
4071 (delete-backward-char val t)))
f1097063 4072
7d3f9fd8 4073
2f3eb3b6 4074(defun viper-del-backward-char-in-insert ()
d5e52f99 4075 "Delete 1 char backwards while in insert mode."
f1097063 4076 (interactive)
34317da2 4077 (if (and viper-ex-style-editing (bolp))
d5e52f99
MK
4078 (beep 1)
4079 (delete-backward-char 1 t)))
f1097063 4080
7d3f9fd8 4081
2f3eb3b6 4082(defun viper-del-backward-char-in-replace ()
d5e52f99 4083 "Delete one character in replace mode.
2f3eb3b6 4084If `viper-delete-backwards-in-replace' is t, then DEL key actually deletes
3af0304a
MK
4085charecters. If it is nil, then the cursor just moves backwards, similarly
4086to Vi. The variable `viper-ex-style-editing', if t, doesn't let the
d5e52f99
MK
4087cursor move past the beginning of line."
4088 (interactive)
2f3eb3b6 4089 (cond (viper-delete-backwards-in-replace
d5e52f99
MK
4090 (cond ((not (bolp))
4091 (delete-backward-char 1 t))
34317da2 4092 (viper-ex-style-editing
d5e52f99
MK
4093 (beep 1))
4094 ((bobp)
4095 (beep 1))
4096 (t
4097 (delete-backward-char 1 t))))
34317da2 4098 (viper-ex-style-editing
d5e52f99
MK
4099 (if (bolp)
4100 (beep 1)
4101 (backward-char 1)))
f1097063 4102 (t
d5e52f99
MK
4103 (backward-char 1))))
4104
4105
4106\f
4107;; join lines.
4108
2f3eb3b6 4109(defun viper-join-lines (arg)
d5e52f99
MK
4110 "Join this line to next, if ARG is nil. Otherwise, join ARG lines."
4111 (interactive "*P")
2f3eb3b6
MK
4112 (let ((val (viper-P-val arg)))
4113 (viper-set-destructive-command
4114 (list 'viper-join-lines val nil nil nil nil))
4115 (viper-loop (if (null val) 1 (1- val))
d5e52f99
MK
4116 (end-of-line)
4117 (if (not (eobp))
4118 (progn
4119 (forward-line 1)
4120 (delete-region (point) (1- (point)))
1e70790f
MK
4121 (fixup-whitespace)
4122 ;; fixup-whitespace sometimes does not leave space
4123 ;; between objects, so we insert it as in Vi
4124 (or (looking-at " ")
4125 (insert " ")
4126 (backward-char 1))
34317da2 4127 )))))
d5e52f99
MK
4128
4129\f
4130;; Replace state
4131
2f3eb3b6 4132(defun viper-change (beg end)
d5e52f99
MK
4133 (if (markerp beg) (setq beg (marker-position beg)))
4134 (if (markerp end) (setq end (marker-position end)))
4135 ;; beg is sometimes (mark t), which may be nil
4136 (or beg (setq beg end))
f1097063 4137
2f3eb3b6
MK
4138 (viper-set-complex-command-for-undo)
4139 (if viper-use-register
d5e52f99 4140 (progn
2f3eb3b6
MK
4141 (copy-to-register viper-use-register beg end nil)
4142 (setq viper-use-register nil)))
4143 (viper-set-replace-overlay beg end)
d5e52f99 4144 (setq last-command nil) ; separate repl text from prev kills
f1097063 4145
2f3eb3b6 4146 (if (= (viper-replace-start) (point-max))
d5e52f99 4147 (error "End of buffer"))
f1097063 4148
2f3eb3b6
MK
4149 (setq viper-last-replace-region
4150 (buffer-substring (viper-replace-start)
4151 (viper-replace-end)))
f1097063 4152
d5e52f99
MK
4153 ;; protect against error while inserting "@" and other disasters
4154 ;; (e.g., read-only buff)
4155 (condition-case conds
2f3eb3b6
MK
4156 (if (or viper-allow-multiline-replace-regions
4157 (viper-same-line (viper-replace-start)
41497c90 4158 (viper-replace-end)))
d5e52f99
MK
4159 (progn
4160 ;; tabs cause problems in replace, so untabify
2f3eb3b6 4161 (goto-char (viper-replace-end))
d5e52f99 4162 (insert-before-markers "@") ; put placeholder after the TAB
2f3eb3b6 4163 (untabify (viper-replace-start) (point))
f1097063 4164 ;; del @, don't put on kill ring
d5e52f99 4165 (delete-backward-char 1)
f1097063 4166
2f3eb3b6
MK
4167 (viper-set-replace-overlay-glyphs
4168 viper-replace-region-start-delimiter
4169 viper-replace-region-end-delimiter)
d5e52f99 4170 ;; this move takes care of the last posn in the overlay, which
3af0304a 4171 ;; has to be shifted because of insert. We can't simply insert
d5e52f99
MK
4172 ;; "$" before-markers because then overlay-start will shift the
4173 ;; beginning of the overlay in case we are replacing a single
3af0304a 4174 ;; character. This fixes the bug with `s' and `cl' commands.
2f3eb3b6
MK
4175 (viper-move-replace-overlay (viper-replace-start) (point))
4176 (goto-char (viper-replace-start))
4177 (viper-change-state-to-replace t))
4178 (kill-region (viper-replace-start)
4179 (viper-replace-end))
4180 (viper-hide-replace-overlay)
4181 (viper-change-state-to-insert))
d5e52f99
MK
4182 (error ;; make sure that the overlay doesn't stay.
4183 ;; go back to the original point
2f3eb3b6
MK
4184 (goto-char (viper-replace-start))
4185 (viper-hide-replace-overlay)
4186 (viper-message-conditions conds))))
d5e52f99
MK
4187
4188
2f3eb3b6 4189(defun viper-change-subr (beg end)
d5e52f99
MK
4190 ;; beg is sometimes (mark t), which may be nil
4191 (or beg (setq beg end))
2f3eb3b6 4192 (if viper-use-register
d5e52f99 4193 (progn
2f3eb3b6
MK
4194 (copy-to-register viper-use-register beg end nil)
4195 (setq viper-use-register nil)))
d5e52f99 4196 (kill-region beg end)
2f3eb3b6
MK
4197 (setq this-command 'viper-change)
4198 (viper-yank-last-insertion))
d5e52f99 4199
2f3eb3b6 4200(defun viper-toggle-case (arg)
d5e52f99
MK
4201 "Toggle character case."
4202 (interactive "P")
2f3eb3b6
MK
4203 (let ((val (viper-p-val arg)) (c))
4204 (viper-set-destructive-command
4205 (list 'viper-toggle-case val nil nil nil nil))
d5e52f99
MK
4206 (while (> val 0)
4207 (setq c (following-char))
4208 (delete-char 1 nil)
4209 (if (eq c (upcase c))
4210 (insert-char (downcase c) 1)
4211 (insert-char (upcase c) 1))
4212 (if (eolp) (backward-char 1))
4213 (setq val (1- val)))))
4214
4215\f
4216;; query replace
4217
2f3eb3b6 4218(defun viper-query-replace ()
f1097063 4219 "Query replace.
d5e52f99
MK
4220If a null string is suplied as the string to be replaced,
4221the query replace mode will toggle between string replace
4222and regexp replace."
4223 (interactive)
4224 (let (str)
2f3eb3b6
MK
4225 (setq str (viper-read-string-with-history
4226 (if viper-re-query-replace "Query replace regexp: "
d5e52f99
MK
4227 "Query replace: ")
4228 nil ; no initial
2f3eb3b6
MK
4229 'viper-replace1-history
4230 (car viper-replace1-history) ; default
d5e52f99
MK
4231 ))
4232 (if (string= str "")
4233 (progn
2f3eb3b6 4234 (setq viper-re-query-replace (not viper-re-query-replace))
d5e52f99 4235 (message "Query replace mode changed to %s"
2f3eb3b6 4236 (if viper-re-query-replace "regexp replace"
d5e52f99 4237 "string replace")))
2f3eb3b6 4238 (if viper-re-query-replace
d5e52f99
MK
4239 (query-replace-regexp
4240 str
2f3eb3b6 4241 (viper-read-string-with-history
d5e52f99
MK
4242 (format "Query replace regexp `%s' with: " str)
4243 nil ; no initial
2f3eb3b6
MK
4244 'viper-replace1-history
4245 (car viper-replace1-history) ; default
d5e52f99
MK
4246 ))
4247 (query-replace
4248 str
2f3eb3b6 4249 (viper-read-string-with-history
d5e52f99
MK
4250 (format "Query replace `%s' with: " str)
4251 nil ; no initial
2f3eb3b6
MK
4252 'viper-replace1-history
4253 (car viper-replace1-history) ; default
d5e52f99
MK
4254 ))))))
4255
4256\f
4257;; marking
4258
2f3eb3b6 4259(defun viper-mark-beginning-of-buffer ()
d5e52f99
MK
4260 "Mark beginning of buffer."
4261 (interactive)
4262 (push-mark (point))
4263 (goto-char (point-min))
4264 (exchange-point-and-mark)
4265 (message "Mark set at the beginning of buffer"))
4266
2f3eb3b6 4267(defun viper-mark-end-of-buffer ()
d5e52f99
MK
4268 "Mark end of buffer."
4269 (interactive)
4270 (push-mark (point))
4271 (goto-char (point-max))
4272 (exchange-point-and-mark)
4273 (message "Mark set at the end of buffer"))
4274
2f3eb3b6 4275(defun viper-mark-point ()
d5e52f99
MK
4276 "Set mark at point of buffer."
4277 (interactive)
4278 (let ((char (read-char)))
4279 (cond ((and (<= ?a char) (<= char ?z))
4960e757 4280 (point-to-register (viper-int-to-char (1+ (- char ?a)))))
657f9cb8
MK
4281 ((viper= char ?<) (viper-mark-beginning-of-buffer))
4282 ((viper= char ?>) (viper-mark-end-of-buffer))
4283 ((viper= char ?.) (viper-set-mark-if-necessary))
4284 ((viper= char ?,) (viper-cycle-through-mark-ring))
4285 ((viper= char ?^) (push-mark viper-saved-mark t t))
4286 ((viper= char ?D) (mark-defun))
d5e52f99
MK
4287 (t (error ""))
4288 )))
f1097063 4289
d5e52f99
MK
4290;; Algorithm: If first invocation of this command save mark on ring, goto
4291;; mark, M0, and pop the most recent elt from the mark ring into mark,
4292;; making it into the new mark, M1.
4293;; Push this mark back and set mark to the original point position, p1.
4294;; So, if you hit '' or `` then you can return to p1.
4295;;
4296;; If repeated command, pop top elt from the ring into mark and
3af0304a 4297;; jump there. This forgets the position, p1, and puts M1 back into mark.
d5e52f99
MK
4298;; Then we save the current pos, which is M0, jump to M1 and pop M2 from
4299;; the ring into mark. Push M2 back on the ring and set mark to M0.
4300;; etc.
2f3eb3b6 4301(defun viper-cycle-through-mark-ring ()
d5e52f99
MK
4302 "Visit previous locations on the mark ring.
4303One can use `` and '' to temporarily jump 1 step back."
4304 (let* ((sv-pt (point)))
4305 ;; if repeated `m,' command, pop the previously saved mark.
3af0304a 4306 ;; Prev saved mark is actually prev saved point. It is used if the
f1097063
SS
4307 ;; user types `` or '' and is discarded
4308 ;; from the mark ring by the next `m,' command.
d5e52f99
MK
4309 ;; In any case, go to the previous or previously saved mark.
4310 ;; Then push the current mark (popped off the ring) and set current
3af0304a 4311 ;; point to be the mark. Current pt as mark is discarded by the next
d5e52f99 4312 ;; m, command.
2f3eb3b6 4313 (if (eq last-command 'viper-cycle-through-mark-ring)
d5e52f99
MK
4314 ()
4315 ;; save current mark if the first iteration
2f3eb3b6 4316 (setq mark-ring (delete (viper-mark-marker) mark-ring))
d5e52f99
MK
4317 (if (mark t)
4318 (push-mark (mark t) t)) )
4319 (pop-mark)
4320 (set-mark-command 1)
4321 ;; don't duplicate mark on the ring
2f3eb3b6 4322 (setq mark-ring (delete (viper-mark-marker) mark-ring))
d5e52f99 4323 (push-mark sv-pt t)
2f3eb3b6
MK
4324 (viper-deactivate-mark)
4325 (setq this-command 'viper-cycle-through-mark-ring)
d5e52f99 4326 ))
f1097063 4327
d5e52f99 4328
2f3eb3b6 4329(defun viper-goto-mark (arg)
d5e52f99
MK
4330 "Go to mark."
4331 (interactive "P")
4332 (let ((char (read-char))
2f3eb3b6
MK
4333 (com (viper-getcom arg)))
4334 (viper-goto-mark-subr char com nil)))
d5e52f99 4335
2f3eb3b6 4336(defun viper-goto-mark-and-skip-white (arg)
d5e52f99
MK
4337 "Go to mark and skip to first non-white character on line."
4338 (interactive "P")
4339 (let ((char (read-char))
2f3eb3b6
MK
4340 (com (viper-getCom arg)))
4341 (viper-goto-mark-subr char com t)))
d5e52f99 4342
2f3eb3b6 4343(defun viper-goto-mark-subr (char com skip-white)
f1097063 4344 (if (eobp)
d5e52f99
MK
4345 (if (bobp)
4346 (error "Empty buffer")
4347 (backward-char 1)))
2f3eb3b6 4348 (cond ((viper-valid-register char '(letter))
d5e52f99 4349 (let* ((buff (current-buffer))
4960e757 4350 (reg (viper-int-to-char (1+ (- char ?a))))
d5e52f99 4351 (text-marker (get-register reg)))
55d7ff38
MK
4352 ;; If marker points to file that had markers set (and those markers
4353 ;; were saved (as e.g., in session.el), then restore those markers
4354 (if (and (consp text-marker)
4355 (eq (car text-marker) 'file-query)
4356 (or (find-buffer-visiting (nth 1 text-marker))
4357 (y-or-n-p (format "Visit file %s again? "
4358 (nth 1 text-marker)))))
4359 (save-excursion
4360 (find-file (nth 1 text-marker))
4361 (when (and (<= (nth 2 text-marker) (point-max))
4362 (<= (point-min) (nth 2 text-marker)))
4363 (setq text-marker (copy-marker (nth 2 text-marker)))
4364 (set-register reg text-marker))))
2f3eb3b6
MK
4365 (if com (viper-move-marker-locally 'viper-com-point (point)))
4366 (if (not (viper-valid-marker text-marker))
4367 (error viper-EmptyTextmarker char))
4368 (if (and (viper-same-line (point) viper-last-jump)
4369 (= (point) viper-last-jump-ignore))
f1097063 4370 (push-mark viper-last-jump t)
d5e52f99 4371 (push-mark nil t)) ; no msg
2f3eb3b6
MK
4372 (viper-register-to-point reg)
4373 (setq viper-last-jump (point-marker))
f1097063 4374 (cond (skip-white
d5e52f99 4375 (back-to-indentation)
2f3eb3b6 4376 (setq viper-last-jump-ignore (point))))
d5e52f99
MK
4377 (if com
4378 (if (equal buff (current-buffer))
2f3eb3b6
MK
4379 (viper-execute-com (if skip-white
4380 'viper-goto-mark-and-skip-white
4381 'viper-goto-mark)
d5e52f99
MK
4382 nil com)
4383 (switch-to-buffer buff)
2f3eb3b6
MK
4384 (goto-char viper-com-point)
4385 (viper-change-state-to-vi)
d5e52f99 4386 (error "")))))
657f9cb8 4387 ((and (not skip-white) (viper= char ?`))
2f3eb3b6
MK
4388 (if com (viper-move-marker-locally 'viper-com-point (point)))
4389 (if (and (viper-same-line (point) viper-last-jump)
4390 (= (point) viper-last-jump-ignore))
4391 (goto-char viper-last-jump))
d5e52f99
MK
4392 (if (null (mark t)) (error "Mark is not set in this buffer"))
4393 (if (= (point) (mark t)) (pop-mark))
4394 (exchange-point-and-mark)
2f3eb3b6
MK
4395 (setq viper-last-jump (point-marker)
4396 viper-last-jump-ignore 0)
4397 (if com (viper-execute-com 'viper-goto-mark nil com)))
657f9cb8 4398 ((and skip-white (viper= char ?'))
2f3eb3b6
MK
4399 (if com (viper-move-marker-locally 'viper-com-point (point)))
4400 (if (and (viper-same-line (point) viper-last-jump)
4401 (= (point) viper-last-jump-ignore))
4402 (goto-char viper-last-jump))
d5e52f99
MK
4403 (if (= (point) (mark t)) (pop-mark))
4404 (exchange-point-and-mark)
2f3eb3b6 4405 (setq viper-last-jump (point))
d5e52f99 4406 (back-to-indentation)
2f3eb3b6
MK
4407 (setq viper-last-jump-ignore (point))
4408 (if com (viper-execute-com 'viper-goto-mark-and-skip-white nil com)))
4409 (t (error viper-InvalidTextmarker char))))
f1097063 4410
2f3eb3b6 4411(defun viper-insert-tab ()
d5e52f99
MK
4412 (interactive)
4413 (insert-tab))
4414
2f3eb3b6 4415(defun viper-exchange-point-and-mark ()
d5e52f99
MK
4416 (interactive)
4417 (exchange-point-and-mark)
4418 (back-to-indentation))
4419
4420;; Input Mode Indentation
4421
4422;; Returns t, if the string before point matches the regexp STR.
2f3eb3b6 4423(defsubst viper-looking-back (str)
d5e52f99
MK
4424 (and (save-excursion (re-search-backward str nil t))
4425 (= (point) (match-end 0))))
4426
4427
2f3eb3b6 4428(defun viper-forward-indent ()
d5e52f99
MK
4429 "Indent forward -- `C-t' in Vi."
4430 (interactive)
2f3eb3b6
MK
4431 (setq viper-cted t)
4432 (indent-to (+ (current-column) viper-shift-width)))
d5e52f99 4433
2f3eb3b6 4434(defun viper-backward-indent ()
d5e52f99
MK
4435 "Backtab, C-d in VI"
4436 (interactive)
2f3eb3b6 4437 (if viper-cted
d5e52f99 4438 (let ((p (point)) (c (current-column)) bol (indent t))
2f3eb3b6 4439 (if (viper-looking-back "[0^]")
d5e52f99
MK
4440 (progn
4441 (if (eq ?^ (preceding-char))
2f3eb3b6 4442 (setq viper-preserve-indent t))
d5e52f99
MK
4443 (delete-backward-char 1)
4444 (setq p (point))
4445 (setq indent nil)))
4446 (save-excursion
4447 (beginning-of-line)
4448 (setq bol (point)))
4449 (if (re-search-backward "[^ \t]" bol 1) (forward-char))
4450 (delete-region (point) p)
4451 (if indent
2f3eb3b6
MK
4452 (indent-to (- c viper-shift-width)))
4453 (if (or (bolp) (viper-looking-back "[^ \t]"))
4454 (setq viper-cted nil)))))
d5e52f99 4455
4960e757
MK
4456;; do smart indent
4457(defun viper-indent-line (col)
4458 (if viper-auto-indent
4459 (progn
4460 (setq viper-cted t)
4461 (if (and viper-electric-mode
4462 (not (memq major-mode '(fundamental-mode
4463 text-mode
4464 paragraph-indent-text-mode))))
4465 (indent-according-to-mode)
4466 (indent-to col)))))
4467
4468
2f3eb3b6 4469(defun viper-autoindent ()
d5e52f99
MK
4470 "Auto Indentation, Vi-style."
4471 (interactive)
4472 (let ((col (current-indentation)))
4473 (if abbrev-mode (expand-abbrev))
2f3eb3b6
MK
4474 (if viper-preserve-indent
4475 (setq viper-preserve-indent nil)
4476 (setq viper-current-indent col))
d5e52f99
MK
4477 ;; don't leave whitespace lines around
4478 (if (memq last-command
2f3eb3b6
MK
4479 '(viper-autoindent
4480 viper-open-line viper-Open-line
4481 viper-replace-state-exit-cmd))
d5e52f99
MK
4482 (indent-to-left-margin))
4483 ;; use \n instead of newline, or else <Return> will move the insert point
4484 ;;(newline 1)
4485 (insert "\n")
4960e757 4486 (viper-indent-line viper-current-indent)
d5e52f99
MK
4487 ))
4488
f1097063 4489
d5e52f99
MK
4490;; Viewing registers
4491
2f3eb3b6 4492(defun viper-ket-function (arg)
3af0304a 4493 "Function called by \], the ket. View registers and call \]\]."
d5e52f99
MK
4494 (interactive "P")
4495 (let ((reg (read-char)))
2f3eb3b6 4496 (cond ((viper-valid-register reg '(letter Letter))
d5e52f99 4497 (view-register (downcase reg)))
2f3eb3b6 4498 ((viper-valid-register reg '(digit))
d5e52f99 4499 (let ((text (current-kill (- reg ?1) 'do-not-rotate)))
8e41a31c
MK
4500 (with-output-to-temp-buffer " *viper-info*"
4501 (princ (format "Register %c contains the string:\n" reg))
4502 (princ text))
4503 ))
657f9cb8 4504 ((viper= ?\] reg)
2f3eb3b6 4505 (viper-next-heading arg))
d5e52f99 4506 (t (error
2f3eb3b6 4507 viper-InvalidRegister reg)))))
d5e52f99 4508
2f3eb3b6 4509(defun viper-brac-function (arg)
3af0304a 4510 "Function called by \[, the brac. View textmarkers and call \[\["
d5e52f99
MK
4511 (interactive "P")
4512 (let ((reg (read-char)))
657f9cb8 4513 (cond ((viper= ?\[ reg)
2f3eb3b6 4514 (viper-prev-heading arg))
657f9cb8 4515 ((viper= ?\] reg)
2f3eb3b6
MK
4516 (viper-heading-end arg))
4517 ((viper-valid-register reg '(letter))
4960e757 4518 (let* ((val (get-register (viper-int-to-char (1+ (- reg ?a)))))
8e41a31c 4519 (buf (if (not (markerp val))
2f3eb3b6 4520 (error viper-EmptyTextmarker reg)
d5e52f99
MK
4521 (marker-buffer val)))
4522 (pos (marker-position val))
4523 line-no text (s pos) (e pos))
8e41a31c 4524 (with-output-to-temp-buffer " *viper-info*"
d5e52f99
MK
4525 (if (and buf pos)
4526 (progn
f1097063 4527 (save-excursion
d5e52f99
MK
4528 (set-buffer buf)
4529 (setq line-no (1+ (count-lines (point-min) val)))
4530 (goto-char pos)
4531 (beginning-of-line)
4532 (if (re-search-backward "[^ \t]" nil t)
4533 (progn
4534 (beginning-of-line)
4535 (setq s (point))))
4536 (goto-char pos)
4537 (forward-line 1)
4538 (if (re-search-forward "[^ \t]" nil t)
4539 (progn
4540 (end-of-line)
4541 (setq e (point))))
4542 (setq text (buffer-substring s e))
f1097063
SS
4543 (setq text (format "%s<%c>%s"
4544 (substring text 0 (- pos s))
d5e52f99 4545 reg (substring text (- pos s)))))
8e41a31c 4546 (princ
d5e52f99
MK
4547 (format
4548 "Textmarker `%c' is in buffer `%s' at line %d.\n"
4549 reg (buffer-name buf) line-no))
f1097063 4550 (princ (format "Here is some text around %c:\n\n %s"
d5e52f99 4551 reg text)))
8e41a31c
MK
4552 (princ (format viper-EmptyTextmarker reg))))
4553 ))
2f3eb3b6 4554 (t (error viper-InvalidTextmarker reg)))))
f1097063 4555
d5e52f99
MK
4556
4557\f
4558;; commands in insertion mode
4559
2f3eb3b6 4560(defun viper-delete-backward-word (arg)
d5e52f99
MK
4561 "Delete previous word."
4562 (interactive "p")
4563 (save-excursion
4564 (push-mark nil t)
4565 (backward-word arg)
4566 (delete-region (point) (mark t))
4567 (pop-mark)))
4568
4569
1e70790f 4570(defun viper-set-expert-level (&optional dont-change-unless)
d5e52f99
MK
4571 "Sets the expert level for a Viper user.
4572Can be called interactively to change (temporarily or permanently) the
4573current expert level.
4574
e36a387d 4575The optional argument DONT-CHANGE-UNLESS, if not nil, says that
d5e52f99
MK
4576the level should not be changed, unless its current value is
4577meaningless (i.e., not one of 1,2,3,4,5).
4578
4579User level determines the setting of Viper variables that are most
4580sensitive for VI-style look-and-feel."
f1097063 4581
d5e52f99 4582 (interactive)
f1097063 4583
1e70790f 4584 (if (not (natnump viper-expert-level)) (setq viper-expert-level 0))
f1097063 4585
d5e52f99
MK
4586 (save-window-excursion
4587 (delete-other-windows)
1e70790f 4588 ;; if 0 < viper-expert-level < viper-max-expert-level
d5e52f99 4589 ;; & dont-change-unless = t -- use it; else ask
2f3eb3b6 4590 (viper-ask-level dont-change-unless))
f1097063 4591
2f3eb3b6
MK
4592 (setq viper-always t
4593 viper-ex-style-motion t
f1097063 4594 viper-ex-style-editing t
2f3eb3b6 4595 viper-want-ctl-h-help nil)
d5e52f99 4596
1e70790f 4597 (cond ((eq viper-expert-level 1) ; novice or beginner
f1097063 4598 (global-set-key ; in emacs-state
2f3eb3b6
MK
4599 viper-toggle-key
4600 (if (viper-window-display-p) 'viper-iconify 'suspend-emacs))
4601 (setq viper-no-multiple-ESC t
4602 viper-re-search t
4603 viper-vi-style-in-minibuffer t
4604 viper-search-wrap-around-t t
4605 viper-electric-mode nil
4606 viper-want-emacs-keys-in-vi nil
4607 viper-want-emacs-keys-in-insert nil))
f1097063 4608
1e70790f 4609 ((and (> viper-expert-level 1) (< viper-expert-level 5))
d5e52f99 4610 ;; intermediate to guru
2f3eb3b6
MK
4611 (setq viper-no-multiple-ESC (if (viper-window-display-p)
4612 t 'twice)
4613 viper-electric-mode t
4614 viper-want-emacs-keys-in-vi t
4615 viper-want-emacs-keys-in-insert (> viper-expert-level 2))
4616
4617 (if (eq viper-expert-level 4) ; respect user's ex-style motion
4618 ; and viper-no-multiple-ESC
d5e52f99 4619 (progn
1e70790f 4620 (setq-default
34317da2
MK
4621 viper-ex-style-editing
4622 (viper-standard-value 'viper-ex-style-editing)
2f3eb3b6
MK
4623 viper-ex-style-motion
4624 (viper-standard-value 'viper-ex-style-motion))
f1097063 4625 (setq viper-ex-style-motion
2f3eb3b6 4626 (viper-standard-value 'viper-ex-style-motion)
34317da2
MK
4627 viper-ex-style-editing
4628 (viper-standard-value 'viper-ex-style-editing)
2f3eb3b6
MK
4629 viper-re-search
4630 (viper-standard-value 'viper-re-search)
f1097063 4631 viper-no-multiple-ESC
2f3eb3b6 4632 (viper-standard-value 'viper-no-multiple-ESC)))))
f1097063 4633
d5e52f99
MK
4634 ;; A wizard!!
4635 ;; Ideally, if 5 is selected, a buffer should pop up to let the
4636 ;; user toggle the values of variables.
34317da2
MK
4637 (t (setq-default viper-ex-style-editing
4638 (viper-standard-value 'viper-ex-style-editing)
2f3eb3b6
MK
4639 viper-ex-style-motion
4640 (viper-standard-value 'viper-ex-style-motion))
f1097063 4641 (setq viper-want-ctl-h-help
2f3eb3b6 4642 (viper-standard-value 'viper-want-ctl-h-help)
e36a387d 4643 viper-always
1e70790f 4644 (viper-standard-value 'viper-always)
f1097063 4645 viper-no-multiple-ESC
2f3eb3b6 4646 (viper-standard-value 'viper-no-multiple-ESC)
f1097063 4647 viper-ex-style-motion
2f3eb3b6 4648 (viper-standard-value 'viper-ex-style-motion)
34317da2
MK
4649 viper-ex-style-editing
4650 (viper-standard-value 'viper-ex-style-editing)
2f3eb3b6
MK
4651 viper-re-search
4652 (viper-standard-value 'viper-re-search)
f1097063 4653 viper-electric-mode
2f3eb3b6 4654 (viper-standard-value 'viper-electric-mode)
f1097063 4655 viper-want-emacs-keys-in-vi
2f3eb3b6
MK
4656 (viper-standard-value 'viper-want-emacs-keys-in-vi)
4657 viper-want-emacs-keys-in-insert
4658 (viper-standard-value 'viper-want-emacs-keys-in-insert))))
f1097063 4659
2f3eb3b6 4660 (viper-set-mode-vars-for viper-current-state)
e36a387d 4661 (if (or viper-always
1e70790f 4662 (and (> viper-expert-level 0) (> 5 viper-expert-level)))
2f3eb3b6 4663 (viper-set-hooks)))
d5e52f99 4664
7d3f9fd8 4665
d5e52f99 4666;; Ask user expert level.
2f3eb3b6
MK
4667(defun viper-ask-level (dont-change-unless)
4668 (let ((ask-buffer " *viper-ask-level*")
d5e52f99
MK
4669 level-changed repeated)
4670 (save-window-excursion
4671 (switch-to-buffer ask-buffer)
f1097063 4672
1e70790f
MK
4673 (while (or (> viper-expert-level viper-max-expert-level)
4674 (< viper-expert-level 1)
d5e52f99
MK
4675 (null dont-change-unless))
4676 (erase-buffer)
4677 (if repeated
4678 (progn
4679 (message "Invalid user level")
4680 (beep 1))
4681 (setq repeated t))
4682 (setq dont-change-unless t
4683 level-changed t)
4684 (insert "
4685Please specify your level of familiarity with the venomous VI PERil
4686(and the VI Plan for Emacs Rescue).
1e70790f 4687You can change it at any time by typing `M-x viper-set-expert-level RET'
f1097063 4688
d5e52f99 4689 1 -- BEGINNER: Almost all Emacs features are suppressed.
3af0304a 4690 Feels almost like straight Vi. File name completion and
f1097063 4691 command history in the minibuffer are thrown in as a bonus.
2f3eb3b6 4692 To use Emacs productively, you must reach level 3 or higher.
d5e52f99 4693 2 -- MASTER: C-c now has its standard Emacs meaning in Vi command state,
2f3eb3b6
MK
4694 so most Emacs commands can be used when Viper is in Vi state.
4695 Good progress---you are well on the way to level 3!
d5e52f99 4696 3 -- GRAND MASTER: Like 3, but most Emacs commands are available also
2f3eb3b6
MK
4697 in Viper's insert state.
4698 4 -- GURU: Like 3, but user settings are respected for viper-no-multiple-ESC,
34317da2 4699 viper-ex-style-motion, viper-ex-style-editing, and
3af0304a 4700 viper-re-search variables. Adjust these settings to your taste.
e36a387d 4701 5 -- WIZARD: Like 4, but user settings are also respected for viper-always,
2f3eb3b6 4702 viper-electric-mode, viper-want-ctl-h-help, viper-want-emacs-keys-in-vi,
3af0304a 4703 and viper-want-emacs-keys-in-insert. Adjust these to your taste.
f1097063 4704
d5e52f99 4705Please, specify your level now: ")
f1097063 4706
2f3eb3b6 4707 (setq viper-expert-level (- (viper-read-char-exclusive) ?0))
d5e52f99 4708 ) ; end while
f1097063 4709
d5e52f99
MK
4710 ;; tell the user if level was changed
4711 (and level-changed
4712 (progn
4713 (insert
4714 (format "\n\n\n\n\n\t\tYou have selected user level %d"
1e70790f 4715 viper-expert-level))
d5e52f99 4716 (if (y-or-n-p "Do you wish to make this change permanent? ")
1e70790f 4717 ;; save the setting for viper-expert-level
2f3eb3b6 4718 (viper-save-setting
1e70790f
MK
4719 'viper-expert-level
4720 (format "Saving user level %d ..." viper-expert-level)
2f3eb3b6 4721 viper-custom-file-name))
d5e52f99
MK
4722 ))
4723 (bury-buffer) ; remove ask-buffer from screen
4724 (message "")
4725 )))
4726
4727
2f3eb3b6 4728(defun viper-nil ()
d5e52f99
MK
4729 (interactive)
4730 (beep 1))
f1097063
SS
4731
4732
d5e52f99 4733;; if ENFORCE-BUFFER is not nil, error if CHAR is a marker in another buffer
2f3eb3b6 4734(defun viper-register-to-point (char &optional enforce-buffer)
d5e52f99
MK
4735 "Like jump-to-register, but switches to another buffer in another window."
4736 (interactive "cViper register to point: ")
4737 (let ((val (get-register char)))
4738 (cond
4739 ((and (fboundp 'frame-configuration-p)
4740 (frame-configuration-p val))
4741 (set-frame-configuration val))
4742 ((window-configuration-p val)
4743 (set-window-configuration val))
2f3eb3b6 4744 ((viper-valid-marker val)
d5e52f99
MK
4745 (if (and enforce-buffer
4746 (not (equal (current-buffer) (marker-buffer val))))
2f3eb3b6 4747 (error (concat viper-EmptyTextmarker " in this buffer")
4960e757 4748 (viper-int-to-char (1- (+ char ?a)))))
d5e52f99
MK
4749 (pop-to-buffer (marker-buffer val))
4750 (goto-char val))
4751 ((and (consp val) (eq (car val) 'file))
4752 (find-file (cdr val)))
4753 (t
4960e757 4754 (error viper-EmptyTextmarker (viper-int-to-char (1- (+ char ?a))))))))
d5e52f99
MK
4755
4756
2f3eb3b6 4757(defun viper-save-kill-buffer ()
4960e757 4758 "Save then kill current buffer."
d5e52f99 4759 (interactive)
1e70790f 4760 (if (< viper-expert-level 2)
d5e52f99
MK
4761 (save-buffers-kill-emacs)
4762 (save-buffer)
4763 (kill-buffer (current-buffer))))
4764
4765
4766\f
4767;;; Bug Report
4768
2f3eb3b6 4769(defun viper-submit-report ()
d5e52f99
MK
4770 "Submit bug report on Viper."
4771 (interactive)
4772 (let ((reporter-prompt-for-summary-p t)
2f3eb3b6 4773 (viper-device-type (viper-device-type))
d5e52f99
MK
4774 color-display-p frame-parameters
4775 minibuffer-emacs-face minibuffer-vi-face minibuffer-insert-face
4776 varlist salutation window-config)
f1097063 4777
d5e52f99
MK
4778 ;; If mode info is needed, add variable to `let' and then set it below,
4779 ;; like we did with color-display-p.
f1097063 4780 (setq color-display-p (if (viper-window-display-p)
2f3eb3b6 4781 (viper-color-display-p)
d5e52f99 4782 'non-x)
2f3eb3b6
MK
4783 minibuffer-vi-face (if (viper-has-face-support-p)
4784 (viper-get-face viper-minibuffer-vi-face)
d5e52f99 4785 'non-x)
2f3eb3b6 4786 minibuffer-insert-face (if (viper-has-face-support-p)
f1097063 4787 (viper-get-face
2f3eb3b6 4788 viper-minibuffer-insert-face)
d5e52f99 4789 'non-x)
2f3eb3b6
MK
4790 minibuffer-emacs-face (if (viper-has-face-support-p)
4791 (viper-get-face
4792 viper-minibuffer-emacs-face)
d5e52f99
MK
4793 'non-x)
4794 frame-parameters (if (fboundp 'frame-parameters)
4795 (frame-parameters (selected-frame))))
f1097063 4796
2f3eb3b6
MK
4797 (setq varlist (list 'viper-vi-minibuffer-minor-mode
4798 'viper-insert-minibuffer-minor-mode
4799 'viper-vi-intercept-minor-mode
f1097063
SS
4800 'viper-vi-local-user-minor-mode
4801 'viper-vi-kbd-minor-mode
2f3eb3b6
MK
4802 'viper-vi-global-user-minor-mode
4803 'viper-vi-state-modifier-minor-mode
f1097063
SS
4804 'viper-vi-diehard-minor-mode
4805 'viper-vi-basic-minor-mode
4806 'viper-replace-minor-mode
2f3eb3b6 4807 'viper-insert-intercept-minor-mode
f1097063
SS
4808 'viper-insert-local-user-minor-mode
4809 'viper-insert-kbd-minor-mode
2f3eb3b6
MK
4810 'viper-insert-global-user-minor-mode
4811 'viper-insert-state-modifier-minor-mode
f1097063
SS
4812 'viper-insert-diehard-minor-mode
4813 'viper-insert-basic-minor-mode
4814 'viper-emacs-intercept-minor-mode
4815 'viper-emacs-local-user-minor-mode
4816 'viper-emacs-kbd-minor-mode
2f3eb3b6
MK
4817 'viper-emacs-global-user-minor-mode
4818 'viper-emacs-state-modifier-minor-mode
4819 'viper-automatic-iso-accents
34317da2 4820 'viper-special-input-method
2f3eb3b6
MK
4821 'viper-want-emacs-keys-in-insert
4822 'viper-want-emacs-keys-in-vi
4823 'viper-keep-point-on-undo
4824 'viper-no-multiple-ESC
4825 'viper-electric-mode
4826 'viper-ESC-key
4827 'viper-want-ctl-h-help
34317da2 4828 'viper-ex-style-editing
2f3eb3b6
MK
4829 'viper-delete-backwards-in-replace
4830 'viper-vi-style-in-minibuffer
4831 'viper-vi-state-hook
4832 'viper-insert-state-hook
4833 'viper-replace-state-hook
4834 'viper-emacs-state-hook
d5e52f99
MK
4835 'ex-cycle-other-window
4836 'ex-cycle-through-non-files
1e70790f 4837 'viper-expert-level
d5e52f99 4838 'major-mode
2f3eb3b6 4839 'viper-device-type
d5e52f99
MK
4840 'color-display-p
4841 'frame-parameters
4842 'minibuffer-vi-face
4843 'minibuffer-insert-face
4844 'minibuffer-emacs-face
4845 ))
4846 (setq salutation "
4847Congratulations! You may have unearthed a bug in Viper!
4848Please mail a concise, accurate summary of the problem to the address above.
4849
4850-------------------------------------------------------------------")
4851 (setq window-config (current-window-configuration))
2f3eb3b6
MK
4852 (with-output-to-temp-buffer " *viper-info*"
4853 (switch-to-buffer " *viper-info*")
d5e52f99
MK
4854 (delete-other-windows)
4855 (princ "
4856PLEASE FOLLOW THESE PROCEDURES
4857------------------------------
4858
4859Before reporting a bug, please verify that it is related to Viper, and is
4860not cause by other packages you are using.
4861
4862Don't report compilation warnings, unless you are certain that there is a
3af0304a 4863problem. These warnings are normal and unavoidable.
d5e52f99
MK
4864
4865Please note that users should not modify variables and keymaps other than
3af0304a 4866those advertised in the manual. Such `customization' is likely to crash
d5e52f99
MK
4867Viper, as it would any other improperly customized Emacs package.
4868
4869If you are reporting an error message received while executing one of the
4870Viper commands, type:
4871
4872 M-x set-variable <Return> debug-on-error <Return> t <Return>
f1097063 4873
3af0304a
MK
4874Then reproduce the error. The above command will cause Emacs to produce a
4875back trace of the execution that leads to the error. Please include this
d5e52f99
MK
4876trace in your bug report.
4877
4878If you believe that one of Viper's commands goes into an infinite loop
4879\(e.g., Emacs freezes\), type:
4880
4881 M-x set-variable <Return> debug-on-quit <Return> t <Return>
f1097063 4882
3af0304a
MK
4883Then reproduce the problem. Wait for a few seconds, then type C-g to abort
4884the current command. Include the resulting back trace in the bug report.
d5e52f99
MK
4885
4886Mail anyway (y or n)? ")
4887 (if (y-or-n-p "Mail anyway? ")
4888 ()
4889 (set-window-configuration window-config)
4890 (error "Bug report aborted")))
4891
4892 (require 'reporter)
4893 (set-window-configuration window-config)
f1097063 4894
d5e52f99 4895 (reporter-submit-bug-report "kifer@cs.sunysb.edu"
2f3eb3b6 4896 (viper-version)
d5e52f99
MK
4897 varlist
4898 nil 'delete-other-windows
4899 salutation)
4900 ))
d5e52f99 4901
f1097063
SS
4902
4903
d5e52f99 4904;; Smoothes out the difference between Emacs' unread-command-events
3af0304a 4905;; and XEmacs unread-command-event. Arg is a character, an event, a list of
d5e52f99
MK
4906;; events or a sequence of keys.
4907;;
4908;; Due to the way unread-command-events in Emacs (not XEmacs), a non-event
4909;; symbol in unread-command-events list may cause Emacs to turn this symbol
3af0304a 4910;; into an event. Below, we delete nil from event lists, since nil is the most
d5e52f99 4911;; common symbol that might appear in this wrong context.
2f3eb3b6
MK
4912(defun viper-set-unread-command-events (arg)
4913 (if viper-emacs-p
d5e52f99
MK
4914 (setq
4915 unread-command-events
4916 (let ((new-events
4917 (cond ((eventp arg) (list arg))
4918 ((listp arg) arg)
4919 ((sequencep arg)
4920 (listify-key-sequence arg))
4921 (t (error
2f3eb3b6 4922 "viper-set-unread-command-events: Invalid argument, %S"
d5e52f99
MK
4923 arg)))))
4924 (if (not (eventp nil))
4925 (setq new-events (delq nil new-events)))
4926 (append new-events unread-command-events)))
4927 ;; XEmacs
4928 (setq
4929 unread-command-events
4930 (append
2f3eb3b6 4931 (cond ((viper-characterp arg) (list (character-to-event arg)))
d5e52f99
MK
4932 ((eventp arg) (list arg))
4933 ((stringp arg) (mapcar 'character-to-event arg))
4934 ((vectorp arg) (append arg nil)) ; turn into list
2f3eb3b6 4935 ((listp arg) (viper-eventify-list-xemacs arg))
d5e52f99 4936 (t (error
2f3eb3b6 4937 "viper-set-unread-command-events: Invalid argument, %S" arg)))
d5e52f99
MK
4938 unread-command-events))))
4939
4940;; list is assumed to be a list of events of characters
2f3eb3b6 4941(defun viper-eventify-list-xemacs (lis)
d5e52f99 4942 (mapcar
3af0304a
MK
4943 (lambda (elt)
4944 (cond ((viper-characterp elt) (character-to-event elt))
4945 ((eventp elt) elt)
4946 (t (error
4947 "viper-eventify-list-xemacs: can't convert to event, %S"
4948 elt))))
d5e52f99 4949 lis))
7d3f9fd8
MK
4950
4951
d5e52f99 4952
60370d40 4953;;; viper-cmd.el ends here