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