Sync to HEAD
[bpt/emacs.git] / lisp / emulation / viper-cmd.el
1 ;;; viper-cmd.el --- Vi command support for Viper
2
3 ;; Copyright (C) 1997, 98, 99, 2000, 01, 02 Free Software Foundation, Inc.
4
5 ;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
6
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.
23
24 ;;; Commentary:
25
26 ;;; Code:
27
28 (provide 'viper-cmd)
29 (require 'advice)
30
31 ;; Compiler pacifier
32 (defvar viper-minibuffer-current-face)
33 (defvar viper-minibuffer-insert-face)
34 (defvar viper-minibuffer-vi-face)
35 (defvar viper-minibuffer-emacs-face)
36 (defvar viper-always)
37 (defvar viper-mode-string)
38 (defvar viper-custom-file-name)
39 (defvar iso-accents-mode)
40 (defvar quail-mode)
41 (defvar quail-current-str)
42 (defvar zmacs-region-stays)
43 (defvar mark-even-if-inactive)
44 (defvar init-message)
45 (defvar initial)
46
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 )))
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
81 ;; (macroexpand '(viper-test-com-defun foo))
82 ;; (defun foo-p (com) (consp (memq com foos)))
83
84 (defmacro viper-test-com-defun (name)
85 (let* ((snm (symbol-name name))
86 (nm-p (intern (concat snm "-p")))
87 (nms (intern (concat snm "s"))))
88 `(defun ,nm-p (com)
89 (consp (viper-memq-char com ,nms)
90 ))))
91
92 ;; Variables for defining VI commands
93
94 ;; Modifying commands that can be prefixes to movement commands
95 (defconst viper-prefix-commands '(?c ?d ?y ?! ?= ?# ?< ?> ?\"))
96 ;; define viper-prefix-command-p
97 (viper-test-com-defun viper-prefix-command)
98
99 ;; Commands that are pairs eg. dd. r and R here are a hack
100 (defconst viper-charpair-commands '(?c ?d ?y ?! ?= ?< ?> ?r ?R))
101 ;; define viper-charpair-command-p
102 (viper-test-com-defun viper-charpair-command)
103
104 (defconst viper-movement-commands '(?b ?B ?e ?E ?f ?F ?G ?h ?H ?j ?k ?l
105 ?H ?M ?L ?n ?t ?T ?w ?W ?$ ?%
106 ?^ ?( ?) ?- ?+ ?| ?{ ?} ?[ ?] ?' ?`
107 ?\; ?, ?0 ?? ?/ ?\ ?\C-m
108 space return
109 delete backspace
110 )
111 "Movement commands")
112 ;; define viper-movement-command-p
113 (viper-test-com-defun viper-movement-command)
114
115 ;; Vi digit commands
116 (defconst viper-digit-commands '(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9))
117
118 ;; define viper-digit-command-p
119 (viper-test-com-defun viper-digit-command)
120
121 ;; Commands that can be repeated by . (dotted)
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)
125
126 ;; Commands that can follow a #
127 (defconst viper-hash-commands '(?c ?C ?g ?q ?s))
128 ;; define viper-hash-command-p
129 (viper-test-com-defun viper-hash-command)
130
131 ;; Commands that may have registers as prefix
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)
143 "The list of all commands in Vi-state.")
144 ;; define viper-vi-command-p
145 (viper-test-com-defun viper-vi-command)
146
147 ;; Where viper saves mark. This mark is resurrected by m^
148 (defvar viper-saved-mark nil)
149
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
154
155 \f
156 ;;; CODE
157
158 ;; sentinels
159
160 ;; Runs viper-after-change-functions inside after-change-functions
161 (defun viper-after-change-sentinel (beg end len)
162 (run-hook-with-args 'viper-after-change-functions beg end len))
163
164 ;; Runs viper-before-change-functions inside before-change-functions
165 (defun viper-before-change-sentinel (beg end)
166 (run-hook-with-args 'viper-before-change-functions beg end))
167
168 (defsubst viper-post-command-sentinel ()
169 (run-hooks 'viper-post-command-hooks)
170 (if (eq viper-current-state 'vi-state)
171 (viper-restore-cursor-color 'after-insert-mode)))
172
173 (defsubst viper-pre-command-sentinel ()
174 (run-hooks 'viper-pre-command-hooks))
175
176 ;; Needed so that Viper will be able to figure the last inserted
177 ;; chunk of text with reasonable accuracy.
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)))
183 (or (viper-overlay-p viper-replace-overlay)
184 (progn
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
191 (string= (viper-get-cursor-color) viper-insert-state-cursor-color)
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))
198 (integerp viper-pre-command-point)
199 (markerp viper-insert-point)
200 (marker-position viper-insert-point)
201 (> viper-insert-point viper-pre-command-point))
202 (viper-move-marker-locally viper-insert-point viper-pre-command-point))
203 )
204
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))
212 (memq (viper-event-key last-command-event)
213 '(up down left right (meta f) (meta b)
214 (control n) (control p) (control f) (control b)))))
215
216 (defsubst viper-insert-state-pre-command-sentinel ()
217 (or (viper-preserve-cursor-color)
218 (viper-restore-cursor-color 'after-insert-mode))
219 (if (and (memq this-command '(dabbrev-expand hippie-expand))
220 (markerp viper-insert-point)
221 (marker-position viper-insert-point))
222 (setq viper-pre-command-point (marker-position viper-insert-point))))
223
224 (defsubst viper-R-state-post-command-sentinel ()
225 ;; Restoring cursor color is needed despite
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
230 ;; will remain red. Restoring the default, below, prevents this.
231 (if (and (<= (viper-replace-start) (point))
232 (<= (point) (viper-replace-end)))
233 (viper-change-cursor-color viper-replace-overlay-cursor-color)
234 (viper-restore-cursor-color 'after-replace-mode)
235 ))
236
237 ;; to speed up, don't change cursor color before self-insert
238 ;; and common move commands
239 (defsubst viper-replace-state-pre-command-sentinel ()
240 (or (viper-preserve-cursor-color)
241 (viper-restore-cursor-color 'after-replace-mode)))
242
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
259 (defun viper-replace-state-post-command-sentinel ()
260 ;; Restoring cursor color is needed despite
261 ;; viper-replace-state-pre-command-sentinel: When one jumps to another buffer
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
265 ;; will remain red. Restoring the default, below, fixes this problem.
266 ;;
267 ;; We optimize for some commands, like self-insert-command,
268 ;; viper-delete-backward-char, etc., since they either don't change
269 ;; cursor color or, if they terminate replace mode, the color will be changed
270 ;; in viper-finish-change
271 (or (viper-preserve-cursor-color)
272 (viper-restore-cursor-color 'after-replace-mode))
273 (cond
274 ((eq viper-current-state 'replace-state)
275 ;; delete characters to compensate for inserted chars.
276 (let ((replace-boundary (viper-replace-end)))
277 (save-excursion
278 (goto-char viper-last-posn-in-replace-region)
279 (viper-trim-replace-chars-to-delete-if-necessary)
280 (delete-char viper-replace-chars-to-delete)
281 (setq viper-replace-chars-to-delete 0)
282 ;; terminate replace mode if reached replace limit
283 (if (= viper-last-posn-in-replace-region (viper-replace-end))
284 (viper-finish-change)))
285
286 (if (viper-pos-within-region
287 (point) (viper-replace-start) replace-boundary)
288 (progn
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))))
293 ))
294 ;; terminate replace mode if changed Viper states.
295 (t (viper-finish-change))))
296
297
298 ;; changing mode
299
300 ;; Change state to NEW-STATE---either emacs-state, vi-state, or insert-state.
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.
306
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 )
316
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)
321 ;; These hooks will be added back if switching to insert/replace mode
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)
326 (setq viper-intermediate-command nil)
327 (cond ((eq new-state 'vi-state)
328 (cond ((member viper-current-state '(insert-state replace-state))
329
330 ;; move viper-last-posn-while-in-insert-state
331 ;; This is a normal hook that is executed in insert/replace
332 ;; states after each command. In Vi/Emacs state, it does
333 ;; nothing. We need to execute it here to make sure that
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
338 (viper-insert-state-post-command-sentinel)
339
340 (condition-case conds
341 (progn
342 (viper-save-last-insertion
343 viper-insert-point
344 viper-last-posn-while-in-insert-state)
345 (if viper-began-as-replace
346 (setq viper-began-as-replace nil)
347 ;; repeat insert commands if numerical arg > 1
348 (save-excursion
349 (viper-repeat-insert-command))))
350 (error
351 (viper-message-conditions conds)))
352
353 (if (> (length viper-last-insertion) 0)
354 (viper-push-onto-ring viper-last-insertion
355 'viper-insertion-ring))
356
357 (if viper-ESC-moves-cursor-back
358 (or (bolp) (backward-char 1))))
359 ))
360
361 ;; insert or replace
362 ((memq new-state '(insert-state replace-state))
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))
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))
371 ) ; outermost cond
372
373 ;; Nothing needs to be done to switch to emacs mode! Just set some
374 ;; variables, which is already done in viper-change-state-to-emacs!
375
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
387 (setq viper-current-state new-state)
388
389 (viper-update-syntax-classes)
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)
394 )
395
396
397
398 (defun viper-adjust-keys-for (state)
399 "Make necessary adjustments to keymaps before entering STATE."
400 (cond ((memq state '(insert-state replace-state))
401 (if viper-auto-indent
402 (progn
403 (define-key viper-insert-basic-map "\C-m" 'viper-autoindent)
404 (if viper-want-emacs-keys-in-insert
405 ;; expert
406 (define-key viper-insert-basic-map "\C-j" nil)
407 ;; novice
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))
411
412 (setq viper-insert-diehard-minor-mode
413 (not viper-want-emacs-keys-in-insert))
414
415 (if viper-want-ctl-h-help
416 (progn
417 (define-key viper-insert-basic-map "\C-h" 'help-command)
418 (define-key viper-replace-map "\C-h" 'help-command))
419 (define-key viper-insert-basic-map
420 "\C-h" 'viper-del-backward-char-in-insert)
421 (define-key viper-replace-map
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
429 (t ; Vi state
430 (setq viper-vi-diehard-minor-mode (not viper-want-emacs-keys-in-vi))
431 (if viper-want-ctl-h-help
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))
436 ))
437
438
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.
442 (defun viper-normalize-minor-mode-map-alist ()
443 (setq minor-mode-map-alist
444 (viper-append-filter-alist
445 (list (cons 'viper-vi-intercept-minor-mode viper-vi-intercept-map)
446 (cons 'viper-vi-minibuffer-minor-mode viper-minibuffer-map)
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
451 (if (keymapp
452 (cdr (assoc major-mode
453 viper-vi-state-modifier-alist)))
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)
458 (cons 'viper-insert-intercept-minor-mode
459 viper-insert-intercept-map)
460 (cons 'viper-replace-minor-mode viper-replace-map)
461 ;; viper-insert-minibuffer-minor-mode must come after
462 ;; viper-replace-minor-mode
463 (cons 'viper-insert-minibuffer-minor-mode
464 viper-minibuffer-map)
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
471 (if (keymapp
472 (cdr (assoc major-mode
473 viper-insert-state-modifier-alist)))
474 (cdr (assoc major-mode
475 viper-insert-state-modifier-alist))
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
487 (if (keymapp
488 (cdr
489 (assoc major-mode viper-emacs-state-modifier-alist)))
490 (cdr
491 (assoc major-mode viper-emacs-state-modifier-alist))
492 viper-empty-keymap))
493 )
494 minor-mode-map-alist)))
495
496
497 \f
498 ;; Viper mode-changing commands and utilities
499
500 ;; Modifies mode-line-buffer-identification.
501 (defun viper-refresh-mode-line ()
502 (setq viper-mode-string
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)))
507
508 ;; Sets Viper mode string in global-mode-string
509 (force-mode-line-update))
510
511
512 ;; Switch from Insert state to Vi state.
513 (defun viper-exit-insert-state ()
514 (interactive)
515 (viper-change-state-to-vi))
516
517 (defun viper-set-mode-vars-for (state)
518 "Sets Viper minor mode variables to put Viper's state STATE in effect."
519
520 ;; Emacs state
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
525
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
532
533 viper-replace-minor-mode nil
534
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
546 )
547
548 ;; Vi state
549 (if (eq state 'vi-state) ; adjust for vi-state
550 (setq
551 viper-vi-intercept-minor-mode t
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
557 ;; don't let the diehard keymap block command completion
558 ;; and other things in the minibuffer
559 viper-vi-diehard-minor-mode (not
560 (or viper-want-emacs-keys-in-vi
561 (viper-is-in-minibuffer)))
562 viper-vi-basic-minor-mode t
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
568 ))
569
570 ;; Insert and Replace states
571 (if (member state '(insert-state replace-state))
572 (setq
573 viper-insert-intercept-minor-mode t
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
580 ;; don't let the diehard keymap block command completion
581 ;; and other things in the minibuffer
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
592 ))
593
594 ;; minibuffer faces
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)
599 ((memq state '(insert-state replace-state))
600 viper-minibuffer-insert-face))))
601
602 (if (viper-is-in-minibuffer)
603 (viper-set-minibuffer-overlay))
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.
608 (defun viper-change-state-to-vi ()
609 "Change Viper state to Vi."
610 (interactive)
611 (if (and viper-first-time (not (viper-is-in-minibuffer)))
612 (viper-mode)
613 (if overwrite-mode (overwrite-mode -1))
614 (or (viper-overlay-p viper-replace-overlay)
615 (viper-set-replace-overlay (point-min) (point-min)))
616 (viper-hide-replace-overlay)
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
622 '(viper-autoindent
623 viper-open-line viper-Open-line
624 viper-replace-state-exit-cmd))
625 (viper-over-whitespace-line))
626 (indent-to-left-margin))
627 (viper-add-newline-at-eob-if-necessary)
628 (viper-adjust-undo)
629 (viper-change-state 'vi-state)
630
631 (viper-restore-cursor-color 'after-insert-mode)
632
633 ;; Protect against user errors in hooks
634 (condition-case conds
635 (run-hooks 'viper-vi-state-hook)
636 (error
637 (viper-message-conditions conds)))))
638
639 (defun viper-change-state-to-insert ()
640 "Change Viper state to Insert."
641 (interactive)
642 (viper-change-state 'insert-state)
643
644 (or (viper-overlay-p viper-replace-overlay)
645 (viper-set-replace-overlay (point-min) (point-min)))
646 (viper-hide-replace-overlay)
647
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))
653 (viper-change-cursor-color viper-insert-state-cursor-color))
654
655 ;; Protect against user errors in hooks
656 (condition-case conds
657 (run-hooks 'viper-insert-state-hook)
658 (error
659 (viper-message-conditions conds))))
660
661 (defsubst viper-downgrade-to-insert ()
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))
669
670
671
672 ;; Change to replace state. When the end of replacement region is reached,
673 ;; replace state changes to insert state.
674 (defun viper-change-state-to-replace (&optional non-R-cmd)
675 (viper-change-state 'replace-state)
676 ;; Run insert-state-hook
677 (condition-case conds
678 (run-hooks 'viper-insert-state-hook 'viper-replace-state-hook)
679 (error
680 (viper-message-conditions conds)))
681
682 (if non-R-cmd
683 (viper-start-replace)
684 ;; 'R' is implemented using Emacs's overwrite-mode
685 (viper-start-R-mode))
686 )
687
688
689 (defun viper-change-state-to-emacs ()
690 "Change Viper state to Emacs."
691 (interactive)
692 (or (viper-overlay-p viper-replace-overlay)
693 (viper-set-replace-overlay (point-min) (point-min)))
694 (viper-hide-replace-overlay)
695 (viper-change-state 'emacs-state)
696
697 ;; Protect agains user errors in hooks
698 (condition-case conds
699 (run-hooks 'viper-emacs-state-hook)
700 (error
701 (viper-message-conditions conds))))
702
703 ;; escape to emacs mode termporarily
704 (defun viper-escape-to-emacs (arg &optional events)
705 "Escape to Emacs state from Vi state for one Emacs command.
706 ARG is used as the prefix value for the executed command. If
707 EVENTS is a list of events, which become the beginning of the command."
708 (interactive "P")
709 (if (viper= last-command-char ?\\)
710 (message "Switched to EMACS state for the next command..."))
711 (viper-escape-to-state arg events 'emacs-state))
712
713 ;; escape to Vi mode termporarily
714 (defun viper-escape-to-vi (arg)
715 "Escape from Emacs state to Vi state for one Vi 1-character command.
716 If the Vi command that the user types has a prefix argument, e.g., `d2w', then
717 Vi's prefix argument will be used. Otherwise, the prefix argument passed to
718 `viper-escape-to-vi' is used."
719 (interactive "P")
720 (message "Switched to VI state for the next command...")
721 (viper-escape-to-state arg nil 'vi-state))
722
723 ;; Escape to STATE mode for one Emacs command.
724 (defun viper-escape-to-state (arg events state)
725 ;;(let (com key prefix-arg)
726 (let (com key)
727 ;; this temporarily turns off Viper's minor mode keymaps
728 (viper-set-mode-vars-for state)
729 (viper-normalize-minor-mode-map-alist)
730 (if events (viper-set-unread-command-events events))
731
732 ;; protect against keyboard quit and other errors
733 (condition-case nil
734 (let (viper-vi-kbd-minor-mode
735 viper-insert-kbd-minor-mode
736 viper-emacs-kbd-minor-mode)
737 (unwind-protect
738 (progn
739 (setq com
740 (key-binding (setq key (viper-read-key-sequence nil))))
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
748 ;; `state'. Otherwise, if we switch buffers while executing the
749 ;; escaped to command, Viper's mode vars will remain those of
750 ;; `state'. When we return to the orig buffer, the bindings will be
751 ;; screwed up.
752 (viper-set-mode-vars-for viper-current-state)
753
754 ;; this-command, last-command-char, last-command-event
755 (setq this-command com)
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 )
766
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
775 (viper-set-mode-vars-for viper-current-state))
776
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)
802 ;; The next cmd and viper-set-unread-command-events
803 ;; are intended to prevent the input method
804 ;; from swallowing ^M, ^Q and other special characters
805 (setq ch (read-char))
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)))
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))
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)))
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))
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)))
850 (insert ch))
851 (t
852 (setq ch (read-char))
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)))
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
864 (error nil)
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
873 (defun viper-exec-form-in-vi (form)
874 "Execute FORM in Vi state, regardless of the Ccurrent Vi state."
875 (let ((buff (current-buffer))
876 result)
877 (viper-set-mode-vars-for 'vi-state)
878
879 (condition-case nil
880 (let (viper-vi-kbd-minor-mode) ; execute without kbd macros
881 (setq result (eval form))
882 )
883 (error
884 (signal 'quit nil)))
885
886 (if (not (equal buff (current-buffer))) ; cmd switched buffer
887 (save-excursion
888 (set-buffer buff)
889 (viper-set-mode-vars-for viper-current-state)))
890 (viper-set-mode-vars-for viper-current-state)
891 result))
892
893 (defun viper-exec-form-in-emacs (form)
894 "Execute FORM in Emacs, temporarily disabling Viper's minor modes.
895 Similar to viper-escape-to-emacs, but accepts forms rather than keystrokes."
896 (let ((buff (current-buffer))
897 result)
898 (viper-set-mode-vars-for 'emacs-state)
899 (setq result (eval form))
900 (if (not (equal buff (current-buffer))) ; cmd switched buffer
901 (save-excursion
902 (set-buffer buff)
903 (viper-set-mode-vars-for viper-current-state)))
904 (viper-set-mode-vars-for viper-current-state)
905 result))
906
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
913
914 ;; This is needed because minor modes sometimes override essential Viper
915 ;; bindings. By letting Viper know which files these modes are in, it will
916 ;; arrange to reorganize minor-mode-map-alist so that things will work right.
917 (defun viper-harness-minor-mode (load-file)
918 "Familiarize Viper with a minor mode defined in LOAD_FILE.
919 Minor modes that have their own keymaps may overshadow Viper keymaps.
920 This function is designed to make Viper aware of the packages that define
921 such minor modes.
922 Usage:
923 (viper-harness-minor-mode load-file)
924
925 LOAD-FILE is a name of the file where the specific minor mode is defined.
926 Suffixes such as .el or .elc should be stripped."
927
928 (interactive "sEnter name of the load file: ")
929
930 (eval-after-load load-file '(viper-normalize-minor-mode-map-alist))
931
932 ;; Change the default for minor-mode-map-alist each time a harnessed minor
933 ;; mode adds its own keymap to the a-list.
934 (eval-after-load
935 load-file '(setq-default minor-mode-map-alist minor-mode-map-alist))
936 )
937
938
939 (defun viper-ESC (arg)
940 "Emulate ESC key in Emacs.
941 Prevents multiple escape keystrokes if viper-no-multiple-ESC is true.
942 If viper-no-multiple-ESC is 'twice double ESC would ding in vi-state.
943 Other ESC sequences are emulated via the current Emacs's major mode
944 keymap. This is more convenient on TTYs, since this won't block
945 function keys such as up,down, etc. ESC will also will also work as
946 a Meta key in this case. When viper-no-multiple-ESC is nil, ESC functions
947 as a Meta key and any number of multiple escapes is allowed."
948 (interactive "P")
949 (let (char)
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) ))
953 ((and (eq viper-no-multiple-ESC 'twice)
954 (eq viper-current-state 'vi-state))
955 (setq char (viper-read-char-exclusive))
956 (if (= char (string-to-char viper-ESC-key))
957 (ding)
958 (viper-escape-to-emacs arg (list ?\e char) )))
959 (t (ding)))
960 ))
961
962 (defun viper-alternate-Meta-key (arg)
963 "Simulate Emacs Meta key."
964 (interactive "P")
965 (sit-for 1) (message "ESC-")
966 (viper-escape-to-emacs arg '(?\e)))
967
968 (defun viper-toggle-key-action ()
969 "Action bound to `viper-toggle-key'."
970 (interactive)
971 (if (and (< viper-expert-level 2) (equal viper-toggle-key "\C-z"))
972 (if (viper-window-display-p)
973 (viper-iconify)
974 (suspend-emacs))
975 (viper-change-state-to-emacs)))
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.
984 (defun viper-envelop-ESC-key ()
985 (let ((event last-input-event)
986 (keyseq [nil])
987 (inhibit-quit t))
988 (if (viper-ESC-event-p event)
989 (progn
990 (if (viper-fast-keysequence-p)
991 (progn
992 (let (minor-mode-map-alist)
993 (viper-set-unread-command-events event)
994 (setq keyseq (read-key-sequence nil 'continue-echo))
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
1013 ;; pressed. This is needed to allow arrow keys to be bound to
1014 ;; macros. Otherwise, viper-exec-mapped-kbd-macro will think
1015 ;; that the last event was ESC and so it'll execute whatever is
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)))
1020 (cond ((and (viper-ESC-event-p first-key)
1021 (not viper-translate-all-ESC-keysequences))
1022 ;; put keys following ESC on the unread list
1023 ;; and return ESC as the key-sequence
1024 (viper-set-unread-command-events (subseq keyseq 1))
1025 (setq last-input-event event
1026 keyseq (if viper-emacs-p
1027 "\e"
1028 (vector (character-to-event ?\e)))))
1029 ((and viper-xemacs-p
1030 (key-press-event-p first-key)
1031 (equal '(meta) key-mod))
1032 (viper-set-unread-command-events
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)
1039 (setq last-command-event
1040 (viper-copy-event first-key)))
1041 ))
1042 ) ; end progn
1043
1044 ;; this is escape event with nothing after it
1045 ;; put in unread-command-event and then re-read
1046 (viper-set-unread-command-events event)
1047 (setq keyseq (read-key-sequence nil))
1048 ))
1049 ;; not an escape event
1050 (setq keyseq (vector event)))
1051 keyseq))
1052
1053
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.
1058 (defun viper-intercept-ESC-key ()
1059 "Function that implements ESC key in Viper emulation of Vi."
1060 (interactive)
1061 (let ((cmd (or (key-binding (viper-envelop-ESC-key))
1062 '(lambda () (interactive) (error "")))))
1063
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.
1067 (if (eq cmd 'viper-intercept-ESC-key)
1068 (setq cmd
1069 (cond ((eq viper-current-state 'vi-state)
1070 'viper-ESC)
1071 ((eq viper-current-state 'insert-state)
1072 'viper-exit-insert-state)
1073 ((eq viper-current-state 'replace-state)
1074 'viper-replace-state-exit-cmd)
1075 (t 'viper-change-state-to-vi)
1076 )))
1077 (call-interactively cmd)))
1078
1079
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.
1089 (defsubst viper-p-val (arg)
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.
1097 (defsubst viper-P-val (arg)
1098 (cond ((consp arg) (car arg))
1099 (t arg)))
1100
1101 ;; Get com part of prefix-argument ARG.
1102 (defsubst viper-getcom (arg)
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.
1108 (defun viper-getCom (arg)
1109 (let ((com (viper-getcom arg)))
1110 (cond ((viper= com ?c) ?c)
1111 ;; Previously, ?c was being converted to ?C, but this prevented
1112 ;; multiline replace regions.
1113 ;;((viper= com ?c) ?C)
1114 ((viper= com ?d) ?D)
1115 ((viper= com ?y) ?Y)
1116 (t com))))
1117
1118
1119 ;; Compute numeric prefix arg value.
1120 ;; Invoked by EVENT-CHAR. COM is the command part obtained so far.
1121 (defun viper-prefix-arg-value (event-char com)
1122 (let ((viper-intermediate-command 'viper-digit-argument)
1123 value func)
1124 ;; read while number
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)))
1129
1130 (setq prefix-arg value)
1131 (if com (setq prefix-arg (cons prefix-arg com)))
1132 (while (eq event-char ?U)
1133 (viper-describe-arg prefix-arg)
1134 (setq event-char (viper-read-event-convert-to-char)))
1135
1136 (if (or com (and (not (eq viper-current-state 'vi-state))
1137 ;; make sure it is a Vi command
1138 (viper-characterp event-char)
1139 (viper-vi-command-p event-char)
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
1146 ;; last. If com is not nil, the viper-digit-argument command was
1147 ;; called from within viper-prefix-arg command, such as `d', `w',
1148 ;; etc., i.e., the user typed, say, d2. In this case, `com' would be
1149 ;; `d', `w', etc. If viper-digit-argument was invoked by
1150 ;; viper-escape-to-vi (which is indicated by the fact that the
1151 ;; current state is not vi-state), then `event-char' represents the
1152 ;; vi command to be executed (e.g., `d', `w', etc). Again,
1153 ;; last-command-char must make emacs believe that this is the command
1154 ;; we typed.
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))
1160 (setq func (viper-exec-form-in-vi
1161 `(key-binding (char-to-string ,event-char))))
1162 (funcall func prefix-arg)
1163 (setq prefix-arg nil))
1164 ;; some other command -- let emacs do it in its own way
1165 (viper-set-unread-command-events event-char))
1166 ))
1167
1168
1169 ;; Vi operator as prefix argument."
1170 (defun viper-prefix-arg-com (char value com)
1171 (let ((cont t)
1172 cmd-info
1173 cmd-to-exec-at-end)
1174 (while (and cont
1175 (viper-memq-char char
1176 (list ?c ?d ?y ?! ?< ?> ?= ?# ?r ?R ?\"
1177 viper-buffer-search-char)))
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)
1184 (if (viper-memq-char char '(?# ?\")) (error ""))
1185 (setq com (cons char com))
1186 (setq cont nil))
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.
1191 (cond ((viper-memq-char char '(?! ?=))
1192 (setq com char)
1193 (setq char (read-char))
1194 (setq cont nil))
1195 ((viper= char ?#)
1196 ;; read a char and encode it as com
1197 (setq com (+ 128 (read-char)))
1198 (setq char (read-char)))
1199 ((viper= char ?\")
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))
1215 (while (viper= char ?U)
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)
1223 (viper= char ?!) ; bang command
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
1230 (if (viper-memq-char (car com) '(?r ?R))
1231 ;; execute apropriate region command.
1232 (let ((char (car com)) (com (cdr com)))
1233 (setq prefix-arg (cons value com))
1234 (if (viper= char ?r)
1235 (viper-region prefix-arg)
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 ))
1268
1269 (defun viper-describe-arg (arg)
1270 (let (val com)
1271 (setq val (viper-P-val arg)
1272 com (viper-getcom arg))
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
1281 (defun viper-digit-argument (arg)
1282 "Begin numeric argument for the next command."
1283 (interactive "P")
1284 (viper-leave-region-active)
1285 (viper-prefix-arg-value
1286 last-command-char (if (consp arg) (cdr arg) nil)))
1287
1288 (defun viper-command-argument (arg)
1289 "Accept a motion command as an argument."
1290 (interactive "P")
1291 (let ((viper-intermediate-command 'viper-command-argument))
1292 (condition-case nil
1293 (viper-prefix-arg-com
1294 last-command-char
1295 (cond ((null arg) nil)
1296 ((consp arg) (car arg))
1297 ((integerp arg) arg)
1298 (t (error viper-InvalidCommandArgument)))
1299 (cond ((null arg) nil)
1300 ((consp arg) (cdr arg))
1301 ((integerp arg) nil)
1302 (t (error viper-InvalidCommandArgument))))
1303 (quit (setq viper-use-register nil)
1304 (signal 'quit nil)))
1305 (viper-deactivate-mark)))
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.
1312 (defsubst viper-append-to-register (reg start end)
1313 (set-register reg (concat (if (stringp (get-register reg))
1314 (get-register reg) "")
1315 (buffer-substring start end))))
1316
1317 ;; Saves last inserted text for possible use by viper-repeat command.
1318 (defun viper-save-last-insertion (beg end)
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)))
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)
1329 (progn
1330 (setcar (nthcdr 4 (viper-current-ring-item viper-command-ring))
1331 viper-last-insertion)
1332 ;; del most recent elt, if identical to the second most-recent
1333 (viper-cleanup-ring viper-command-ring)))
1334 )
1335
1336 (defsubst viper-yank-last-insertion ()
1337 "Inserts the text saved by the previous viper-save-last-insertion command."
1338 (condition-case nil
1339 (insert viper-last-insertion)
1340 (error nil)))
1341
1342
1343 ;; define functions to be executed
1344
1345 ;; invoked by the `C' command
1346 (defun viper-exec-change (m-com com)
1347 (or (and (markerp viper-com-point) (marker-position viper-com-point))
1348 (set-marker viper-com-point (point) (current-buffer)))
1349 ;; handle C cmd at the eol and at eob.
1350 (if (or (and (eolp) (= viper-com-point (point)))
1351 (= viper-com-point (point-max)))
1352 (progn
1353 (insert " ")(backward-char 1)))
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
1363 (if (eq viper-intermediate-command 'viper-repeat)
1364 (viper-change-subr (mark t) (point))
1365 (viper-change (mark t) (point))
1366 ))
1367
1368 ;; this is invoked by viper-substitute-line
1369 (defun viper-exec-Change (m-com com)
1370 (save-excursion
1371 (set-mark viper-com-point)
1372 (viper-enlarge-region (mark t) (point))
1373 (if viper-use-register
1374 (progn
1375 (cond ((viper-valid-register viper-use-register '(letter digit))
1376 (copy-to-register
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)))
1384 (delete-region (mark t) (point)))
1385 (open-line 1)
1386 (if (eq viper-intermediate-command 'viper-repeat)
1387 (viper-yank-last-insertion)
1388 (viper-change-state-to-insert)
1389 ))
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)))
1394 (let (chars-deleted)
1395 (if viper-use-register
1396 (progn
1397 (cond ((viper-valid-register viper-use-register '(letter digit))
1398 (copy-to-register
1399 viper-use-register viper-com-point (point) nil))
1400 ((viper-valid-register viper-use-register '(Letter))
1401 (viper-append-to-register
1402 (downcase viper-use-register) viper-com-point (point)))
1403 (t (setq viper-use-register nil)
1404 (error viper-InvalidRegister viper-use-register)))
1405 (setq viper-use-register nil)))
1406 (setq last-command
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)))
1440
1441 ;; save region
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)))
1445 (let (chars-saved)
1446 (if viper-use-register
1447 (progn
1448 (cond ((viper-valid-register viper-use-register '(letter digit))
1449 (copy-to-register
1450 viper-use-register viper-com-point (point) nil))
1451 ((viper-valid-register viper-use-register '(Letter))
1452 (viper-append-to-register
1453 (downcase viper-use-register) viper-com-point (point)))
1454 (t (setq viper-use-register nil)
1455 (error viper-InvalidRegister viper-use-register)))
1456 (setq viper-use-register nil)))
1457 (setq last-command nil)
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))))
1486 (viper-deactivate-mark)
1487 (goto-char viper-com-point))
1488
1489 (defun viper-exec-bang (m-com com)
1490 (save-excursion
1491 (set-mark viper-com-point)
1492 (viper-enlarge-region (mark t) (point))
1493 (exchange-point-and-mark)
1494 (shell-command-on-region
1495 (mark t) (point)
1496 (if (viper= com ?!)
1497 (setq viper-last-shell-com
1498 (viper-read-string-with-history
1499 "!"
1500 nil
1501 'viper-shell-history
1502 (car viper-shell-history)
1503 ))
1504 viper-last-shell-com)
1505 t)))
1506
1507 (defun viper-exec-equals (m-com com)
1508 (save-excursion
1509 (set-mark viper-com-point)
1510 (viper-enlarge-region (mark t) (point))
1511 (if (> (mark t) (point)) (exchange-point-and-mark))
1512 (indent-region (mark t) (point) nil)))
1513
1514 (defun viper-exec-shift (m-com com)
1515 (save-excursion
1516 (set-mark viper-com-point)
1517 (viper-enlarge-region (mark t) (point))
1518 (if (> (mark t) (point)) (exchange-point-and-mark))
1519 (indent-rigidly (mark t) (point)
1520 (if (viper= com ?>)
1521 viper-shift-width
1522 (- viper-shift-width))))
1523 ;; return point to where it was before shift
1524 (goto-char viper-com-point))
1525
1526 ;; this is needed because some commands fake com by setting it to ?r, which
1527 ;; denotes repeated insert command.
1528 (defsubst viper-exec-dummy (m-com com)
1529 nil)
1530
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))
1535 (setq viper-intermediate-command 'viper-exec-buffer-search)
1536 (viper-search viper-s-string viper-s-forward 1))
1537
1538 (defvar viper-exec-array (make-vector 128 nil))
1539
1540 ;; Using a dispatch array allows adding functions like buffer search
1541 ;; without affecting other functions. Buffer search can now be bound
1542 ;; to any character.
1543
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)
1555
1556
1557
1558 ;; This function is called by various movement commands to execute a
1559 ;; destructive command on the region specified by the movement command. For
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.
1563 ;;
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))
1568 ;; this is the special command `#'
1569 (if (> com 128)
1570 (viper-special-prefix-com (- com 128))
1571 (let ((fn (aref viper-exec-array com)))
1572 (if (null fn)
1573 (error "%c: %s" com viper-InvalidViCommand)
1574 (funcall fn m-com com))))
1575 (if (viper-dotable-command-p com)
1576 (viper-set-destructive-command
1577 (list m-com val com reg nil nil)))
1578 ))
1579
1580
1581 (defun viper-repeat (arg)
1582 "Re-execute last destructive command.
1583 Use the info in viper-d-com, which has the form
1584 \(com val ch reg inserted-text command-keys\),
1585 where `com' is the command to be re-executed, `val' is the
1586 argument to `com', `ch' is a flag for repeat, and `reg' is optional;
1587 if it exists, it is the name of the register for `com'.
1588 If 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
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)))
1605 (if (null m-com) (error "No previous command to repeat"))
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)))
1610 (funcall m-com (cons val com))
1611 (cond ((and (< save-point (point)) viper-keep-point-on-repeat)
1612 (goto-char save-point)) ; go back to before repeat.
1613 ((and (< save-point (point)) viper-ex-style-editing)
1614 (or (bolp) (backward-char 1))))
1615 (if (and (eolp) (not (bolp)))
1616 (backward-char 1))
1617 ))
1618 (viper-adjust-undo) ; take care of undo
1619 ;; If the prev cmd was rotating the command ring, this means that `.' has
1620 ;; just executed a command from that ring. So, push it on the ring again.
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
1623 ;; the inserted text may be nil). Besides, in this case, the command
1624 ;; executed by `.' is already on the ring.
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)
1628 ))
1629
1630 (defun viper-repeat-from-history ()
1631 "Repeat a destructive command from history.
1632 Doesn't change viper-command-ring in any way, so `.' will work as before
1633 executing this command.
1634 This command is supposed to be bound to a two-character Vi macro where
1635 the second character is a digit 0 to 9. The digit indicates which
1636 history command to execute. `<char>0' is equivalent to `.', `<char>1'
1637 invokes the command before that, etc."
1638 (interactive)
1639 (let* ((viper-intermediate-command 'repeating-display-destructive-command)
1640 (idx (cond (viper-this-kbd-macro
1641 (string-to-number
1642 (symbol-name (elt viper-this-kbd-macro 1))))
1643 (t 0)))
1644 (num idx)
1645 (viper-d-com viper-d-com))
1646
1647 (or (and (numberp num) (<= 0 num) (<= num 9))
1648 (progn
1649 (setq idx 0
1650 num 0)
1651 (message
1652 "`viper-repeat-from-history' must be invoked as a Vi macro bound to `<key><digit>'")))
1653 (while (< 0 num)
1654 (setq viper-d-com (viper-special-ring-rotate1 viper-command-ring -1))
1655 (setq num (1- num)))
1656 (viper-repeat nil)
1657 (while (> idx num)
1658 (viper-special-ring-rotate1 viper-command-ring 1)
1659 (setq num (1+ num)))
1660 ))
1661
1662
1663 ;; The hash-command. It is invoked interactively by the key sequence #<char>.
1664 ;; The chars that can follow `#' are determined by viper-hash-command-p
1665 (defun viper-special-prefix-com (char)
1666 (cond ((viper= char ?c)
1667 (downcase-region (min viper-com-point (point))
1668 (max viper-com-point (point))))
1669 ((viper= char ?C)
1670 (upcase-region (min viper-com-point (point))
1671 (max viper-com-point (point))))
1672 ((viper= char ?g)
1673 (push-mark viper-com-point t)
1674 (viper-global-execute))
1675 ((viper= char ?q)
1676 (push-mark viper-com-point t)
1677 (viper-quote-region))
1678 ((viper= char ?s)
1679 (funcall viper-spell-function viper-com-point (point)))
1680 (t (error "#%c: %s" char viper-InvalidViCommand))))
1681
1682 \f
1683 ;; undoing
1684
1685 (defun viper-undo ()
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)
1693
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))))
1699
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))
1704
1705 (goto-char undo-beg-posn)
1706 (sit-for 0)
1707 (if (and viper-keep-point-on-undo
1708 (pos-visible-in-window-p before-undo-pt))
1709 (progn
1710 (push-mark (point-marker) t)
1711 (viper-sit-for-short 300)
1712 (goto-char undo-end-posn)
1713 (viper-sit-for-short 300)
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))
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)))
1721 (setq this-command 'viper-undo))
1722
1723 ;; Continue undoing previous changes.
1724 (defun viper-undo-more ()
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))
1731 (setq this-command 'viper-undo))
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,
1735 ;; they are undone all at once.
1736 (defun viper-adjust-undo ()
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
1745
1746 ;; cut tail from buffer-undo-list temporarily by direct
1747 ;; manipulation with pointers in buffer-undo-list
1748 (setcdr tmp nil)
1749
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 ))
1757
1758
1759 (defun viper-set-complex-command-for-undo ()
1760 (if (listp buffer-undo-list)
1761 (if (not viper-undo-needs-adjustment)
1762 (let ((inhibit-quit t))
1763 (setq buffer-undo-list
1764 (cons viper-buffer-undo-list-mark buffer-undo-list))
1765 (setq viper-undo-needs-adjustment t)))))
1766
1767
1768
1769
1770 (defun viper-display-current-destructive-command ()
1771 (let ((text (nth 4 viper-d-com))
1772 (keys (nth 5 viper-d-com))
1773 (max-text-len 30))
1774
1775 (setq this-command 'viper-display-current-destructive-command)
1776
1777 (message " `.' runs %s%s"
1778 (concat "`" (viper-array-to-string keys) "'")
1779 (viper-abbreviate-string
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 )
1788 max-text-len
1789 " inserting `" "'" " ......."))
1790 ))
1791
1792
1793 ;; don't change viper-d-com if it was viper-repeat command invoked with `.'
1794 ;; or in some other way (non-interactively).
1795 (defun viper-set-destructive-command (list)
1796 (or (eq viper-intermediate-command 'viper-repeat)
1797 (progn
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))
1805
1806
1807 (defun viper-prev-destructive-command (next)
1808 "Find previous destructive command in the history of destructive commands.
1809 With prefix argument, find next destructive command."
1810 (interactive "P")
1811 (let (cmd viper-intermediate-command)
1812 (if (eq last-command 'viper-display-current-destructive-command)
1813 ;; repeated search through command history
1814 (setq viper-intermediate-command
1815 'repeating-display-destructive-command)
1816 ;; first search through command history--set temp ring
1817 (setq viper-temp-command-ring (copy-list viper-command-ring)))
1818 (setq cmd (if next
1819 (viper-special-ring-rotate1 viper-temp-command-ring 1)
1820 (viper-special-ring-rotate1 viper-temp-command-ring -1)))
1821 (if (null cmd)
1822 ()
1823 (setq viper-d-com cmd))
1824 (viper-display-current-destructive-command)))
1825
1826
1827 (defun viper-next-destructive-command ()
1828 "Find next destructive command in the history of destructive commands."
1829 (interactive)
1830 (viper-prev-destructive-command 'next))
1831
1832
1833 (defun viper-insert-prev-from-insertion-ring (arg)
1834 "Cycle through insertion ring in the direction of older insertions.
1835 Undoes previous insertion and inserts new.
1836 With prefix argument, cycles in the direction of newer elements.
1837 In minibuffer, this command executes whatever the invocation key is bound
1838 to in the global map, instead of cycling through the insertion ring."
1839 (interactive "P")
1840 (let (viper-intermediate-command)
1841 (if (eq last-command 'viper-insert-from-insertion-ring)
1842 (progn ; repeated search through insertion history
1843 (setq viper-intermediate-command 'repeating-insertion-from-ring)
1844 (if (eq viper-current-state 'replace-state)
1845 (undo 1)
1846 (if viper-last-inserted-string-from-insertion-ring
1847 (backward-delete-char
1848 (length viper-last-inserted-string-from-insertion-ring))))
1849 )
1850 ;;first search through insertion history
1851 (setq viper-temp-insertion-ring (copy-list viper-insertion-ring)))
1852 (setq this-command 'viper-insert-from-insertion-ring)
1853 ;; so that things will be undone properly
1854 (setq buffer-undo-list (cons nil buffer-undo-list))
1855 (setq viper-last-inserted-string-from-insertion-ring
1856 (viper-special-ring-rotate1 viper-temp-insertion-ring (if arg 1 -1)))
1857
1858 ;; this change of viper-intermediate-command must come after
1859 ;; viper-special-ring-rotate1, so that the ring will rotate, but before the
1860 ;; insertion.
1861 (setq viper-intermediate-command nil)
1862 (if viper-last-inserted-string-from-insertion-ring
1863 (insert viper-last-inserted-string-from-insertion-ring))
1864 ))
1865
1866 (defun viper-insert-next-from-insertion-ring ()
1867 "Cycle through insertion ring in the direction of older insertions.
1868 Undo previous insertion and inserts new."
1869 (interactive)
1870 (viper-insert-prev-from-insertion-ring 'next))
1871
1872
1873 \f
1874 ;; some region utilities
1875
1876 ;; If at the last line of buffer, add \\n before eob, if newline is missing.
1877 (defun viper-add-newline-at-eob-if-necessary ()
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
1885 (not (viper-is-in-minibuffer))
1886 (not buffer-read-only))
1887 (insert "\n"))))
1888
1889 (defun viper-yank-defun ()
1890 (mark-defun)
1891 (copy-region-as-kill (point) (mark t)))
1892
1893 ;; Enlarge region between BEG and END.
1894 (defun viper-enlarge-region (beg end)
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
1897
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.
1910 (defun viper-quote-region ()
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)
1931 (beginning-of-line)
1932 (forward-line 1)
1933 (while (and (< (point) (mark t)) (bolp))
1934 (insert quote-str)
1935 (beginning-of-line)
1936 (forward-line 1))))
1937
1938 ;; Tells whether BEG is on the same line as END.
1939 ;; If one of the args is nil, it'll return nil.
1940 (defun viper-same-line (beg end)
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.
1954 (if (= (viper-line-pos 'start) end)
1955 (setq incr 1))
1956 (<= (+ incr (count-lines beg end)) 1))))
1957 ))
1958
1959
1960 ;; Check if the string ends with a newline.
1961 (defun viper-end-with-a-newline-p (string)
1962 (or (string= string "")
1963 (= (viper-seq-last-elt string) ?\n)))
1964
1965 (defun viper-tmp-insert-at-eob (msg)
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))
1971 ))
1972
1973
1974 \f
1975 ;;; Minibuffer business
1976
1977 (defsubst viper-set-minibuffer-style ()
1978 (add-hook 'minibuffer-setup-hook 'viper-minibuffer-setup-sentinel))
1979
1980
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)))
1985 (funcall hook)
1986 ))
1987
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.
1990 ;; The INIT-MESSAGE and INITIAL vars come from the scope set by
1991 ;; viper-read-string-with-history
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))
2004 (insert initial))))
2005
2006 (defsubst viper-minibuffer-real-start ()
2007 (if (fboundp 'minibuffer-prompt-end)
2008 (minibuffer-prompt-end)
2009 (point-min)))
2010
2011
2012 ;; Interpret last event in the local map first; if fails, use exit-minibuffer.
2013 ;; Run viper-minibuffer-exit-hook before exiting.
2014 (defun viper-exit-minibuffer ()
2015 "Exit minibuffer Viper way."
2016 (interactive)
2017 (let (command)
2018 (setq command (local-key-binding (char-to-string last-command-char)))
2019 (run-hooks 'viper-minibuffer-exit-hook)
2020 (if command
2021 (command-execute command)
2022 (exit-minibuffer))))
2023
2024
2025 (defcustom viper-smart-suffix-list
2026 '("" "tex" "c" "cc" "C" "java" "el" "html" "htm" "xml"
2027 "pl" "flr" "P" "p" "h" "H")
2028 "*List of suffixes that Viper tries to append to filenames ending with a `.'.
2029 This is useful when the current directory contains files with the same
2030 prefix and many different suffixes. Usually, only one of the suffixes
2031 represents an editable file. However, file completion will stop at the `.'
2032 The smart suffix feature lets you hit RET in such a case, and Viper will
2033 select the appropriate suffix.
2034
2035 Suffixes are tried in the order given and the first suffix for which a
2036 corresponding file exists is selected. If no file exists for any of the
2037 suffixes, the user is asked to confirm.
2038
2039 To turn this feature off, set this variable to nil."
2040 :type '(repeat string)
2041 :group 'viper-misc)
2042
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))
2050 (file (buffer-substring-no-properties
2051 (viper-minibuffer-real-start) (point-max)))
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))))
2064
2065 (if found
2066 ()
2067 (viper-tmp-insert-at-eob " [Please complete file name]")
2068 (unwind-protect
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)
2074 (viper-cond-compile-for-xemacs-or-emacs
2075 (insert (events-to-keys key)) ; xemacs
2076 (insert key) ; emacs
2077 ))
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.
2087 Remove this function from `viper-minibuffer-exit-hook', if this causes
2088 problems."
2089 (if (viper-is-in-minibuffer)
2090 (progn
2091 (goto-char (viper-minibuffer-real-start))
2092 (end-of-line)
2093 (delete-region (point) (point-max)))))
2094
2095 \f
2096 ;;; Reading string with history
2097
2098 (defun viper-read-string-with-history (prompt &optional initial
2099 history-var default keymap
2100 init-message)
2101 ;; Read string, prompting with PROMPT and inserting the INITIAL
2102 ;; value. Uses HISTORY-VAR. DEFAULT is the default value to accept if the
2103 ;; input is an empty string.
2104 ;; Default value is displayed until the user types something in the
2105 ;; minibuffer.
2106 ;; KEYMAP is used, if given, instead of minibuffer-local-map.
2107 ;; INIT-MESSAGE is the message temporarily displayed after entering the
2108 ;; minibuffer.
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))))
2119 (val "")
2120 (padding "")
2121 temp-msg)
2122
2123 (setq keymap (or keymap minibuffer-local-map)
2124 initial (or initial "")
2125 temp-msg (if default
2126 (format "(default: %s) " default)
2127 ""))
2128
2129 (setq viper-incomplete-ex-cmd nil)
2130 (setq val (read-from-minibuffer prompt
2131 (concat temp-msg initial val padding)
2132 keymap nil history-var))
2133 (setq minibuffer-setup-hook nil
2134 padding (viper-array-to-string (this-command-keys))
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))))
2142 ;; If the user enters nothing but the prev cmd wasn't viper-ex,
2143 ;; viper-command-argument, or `! shell-command', this probably means
2144 ;; that the user typed something then erased. Return "" in this case, not
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
2149 '(viper-ex
2150 viper-command-argument
2151 t)
2152 )))
2153 "")
2154 ((string= val "") (or default ""))
2155 (t val))
2156 ))
2157
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.
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)))
2169 (if (and val (> val 1)) ; first check that val is non-nil
2170 (progn
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))
2174 ))))
2175
2176 (defun viper-insert (arg)
2177 "Insert before point."
2178 (interactive "P")
2179 (viper-set-complex-command-for-undo)
2180 (let ((val (viper-p-val arg))
2181 ;;(com (viper-getcom arg))
2182 )
2183 (viper-set-destructive-command (list 'viper-insert val ?r nil nil nil))
2184 (if (eq viper-intermediate-command 'viper-repeat)
2185 (viper-loop val (viper-yank-last-insertion))
2186 (viper-change-state-to-insert))))
2187
2188 (defun viper-append (arg)
2189 "Append after point."
2190 (interactive "P")
2191 (viper-set-complex-command-for-undo)
2192 (let ((val (viper-p-val arg))
2193 ;;(com (viper-getcom arg))
2194 )
2195 (viper-set-destructive-command (list 'viper-append val ?r nil nil nil))
2196 (if (not (eolp)) (forward-char))
2197 (if (eq viper-intermediate-command 'viper-repeat)
2198 (viper-loop val (viper-yank-last-insertion))
2199 (viper-change-state-to-insert))))
2200
2201 (defun viper-Append (arg)
2202 "Append at end of line."
2203 (interactive "P")
2204 (viper-set-complex-command-for-undo)
2205 (let ((val (viper-p-val arg))
2206 ;;(com (viper-getcom arg))
2207 )
2208 (viper-set-destructive-command (list 'viper-Append val ?r nil nil nil))
2209 (end-of-line)
2210 (if (eq viper-intermediate-command 'viper-repeat)
2211 (viper-loop val (viper-yank-last-insertion))
2212 (viper-change-state-to-insert))))
2213
2214 (defun viper-Insert (arg)
2215 "Insert before first non-white."
2216 (interactive "P")
2217 (viper-set-complex-command-for-undo)
2218 (let ((val (viper-p-val arg))
2219 ;;(com (viper-getcom arg))
2220 )
2221 (viper-set-destructive-command (list 'viper-Insert val ?r nil nil nil))
2222 (back-to-indentation)
2223 (if (eq viper-intermediate-command 'viper-repeat)
2224 (viper-loop val (viper-yank-last-insertion))
2225 (viper-change-state-to-insert))))
2226
2227 (defun viper-open-line (arg)
2228 "Open line below."
2229 (interactive "P")
2230 (viper-set-complex-command-for-undo)
2231 (let ((val (viper-p-val arg))
2232 ;;(com (viper-getcom arg))
2233 )
2234 (viper-set-destructive-command (list 'viper-open-line val ?r nil nil nil))
2235 (let ((col (current-indentation)))
2236 (if (eq viper-intermediate-command 'viper-repeat)
2237 (viper-loop val
2238 (end-of-line)
2239 (newline 1)
2240 (viper-indent-line col)
2241 (viper-yank-last-insertion))
2242 (end-of-line)
2243 (newline 1)
2244 (viper-indent-line col)
2245 (viper-change-state-to-insert)))))
2246
2247 (defun viper-Open-line (arg)
2248 "Open line above."
2249 (interactive "P")
2250 (viper-set-complex-command-for-undo)
2251 (let ((val (viper-p-val arg))
2252 ;;(com (viper-getcom arg))
2253 )
2254 (viper-set-destructive-command (list 'viper-Open-line val ?r nil nil nil))
2255 (let ((col (current-indentation)))
2256 (if (eq viper-intermediate-command 'viper-repeat)
2257 (viper-loop val
2258 (beginning-of-line)
2259 (open-line 1)
2260 (viper-indent-line col)
2261 (viper-yank-last-insertion))
2262 (beginning-of-line)
2263 (open-line 1)
2264 (viper-indent-line col)
2265 (viper-change-state-to-insert)))))
2266
2267 (defun viper-open-line-at-point (arg)
2268 "Open line at point."
2269 (interactive "P")
2270 (viper-set-complex-command-for-undo)
2271 (let ((val (viper-p-val arg))
2272 ;;(com (viper-getcom arg))
2273 )
2274 (viper-set-destructive-command
2275 (list 'viper-open-line-at-point val ?r nil nil nil))
2276 (if (eq viper-intermediate-command 'viper-repeat)
2277 (viper-loop val
2278 (open-line 1)
2279 (viper-yank-last-insertion))
2280 (open-line 1)
2281 (viper-change-state-to-insert))))
2282
2283 ;; bound to s
2284 (defun viper-substitute (arg)
2285 "Substitute characters."
2286 (interactive "P")
2287 (let ((val (viper-p-val arg))
2288 ;;(com (viper-getcom arg))
2289 )
2290 (push-mark nil t)
2291 (forward-char val)
2292 (if (eq viper-intermediate-command 'viper-repeat)
2293 (viper-change-subr (mark t) (point))
2294 (viper-change (mark t) (point)))
2295 ;; com is set to ?r when we repeat this comand with dot
2296 (viper-set-destructive-command (list 'viper-substitute val ?r nil nil nil))
2297 ))
2298
2299 ;; Command bound to S
2300 (defun viper-substitute-line (arg)
2301 "Substitute lines."
2302 (interactive "p")
2303 (viper-set-complex-command-for-undo)
2304 (viper-line (cons arg ?C)))
2305
2306 ;; Prepare for replace
2307 (defun viper-start-replace ()
2308 (setq viper-began-as-replace t
2309 viper-sitting-in-replace t
2310 viper-replace-chars-to-delete 0)
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)
2315 ;; this will get added repeatedly, but no harm
2316 (add-hook 'after-change-functions 'viper-after-change-sentinel t)
2317 (add-hook 'before-change-functions 'viper-before-change-sentinel t)
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)
2325 ;; guard against a smartie who switched from R-replace to normal replace
2326 (remove-hook
2327 'viper-post-command-hooks 'viper-R-state-post-command-sentinel 'local)
2328 (if overwrite-mode (overwrite-mode -1))
2329 )
2330
2331
2332 (defun viper-replace-mode-spy-before (beg end)
2333 (setq viper-replace-region-chars-deleted (viper-chars-in-region beg end))
2334 )
2335
2336 ;; Invoked as an after-change-function to calculate how many chars have to be
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
2340 ;; deleted.
2341 (defun viper-replace-mode-spy-after (beg end length)
2342 (if (memq viper-intermediate-command
2343 '(dabbrev-expand hippie-expand repeating-insertion-from-ring))
2344 ;; Take special care of text insertion from insertion ring inside
2345 ;; replacement overlays.
2346 (progn
2347 (setq viper-replace-chars-to-delete 0)
2348 (viper-move-marker-locally
2349 'viper-last-posn-in-replace-region (point)))
2350
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
2363 (-
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,
2368 ;; which occupy 2 columns instead of one. If we use
2369 ;; column-shift here, we may delete two chars instead of
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
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
2385 (viper-move-marker-locally
2386 'viper-last-posn-in-replace-region
2387 (max (if (> end (viper-replace-end)) (viper-replace-end) end)
2388 (or (marker-position viper-last-posn-in-replace-region)
2389 (viper-replace-start))
2390 ))
2391
2392 )))
2393
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 ()
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
2405 'viper-post-command-hooks 'viper-replace-state-post-command-sentinel 'local)
2406 (remove-hook
2407 'viper-pre-command-hooks 'viper-replace-state-pre-command-sentinel 'local)
2408 (viper-restore-cursor-color 'after-replace-mode)
2409 (setq viper-sitting-in-replace nil) ; just in case we'll need to know it
2410 (save-excursion
2411 (if (and viper-replace-overlay
2412 (viper-pos-within-region viper-last-posn-in-replace-region
2413 (viper-replace-start)
2414 (viper-replace-end))
2415 (< (point) (viper-replace-end)))
2416 (delete-region
2417 viper-last-posn-in-replace-region (viper-replace-end))))
2418
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)
2426 )
2427
2428 ;; Make STRING be the first element of the kill ring.
2429 (defun viper-put-string-on-kill-ring (string)
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
2435 (defun viper-finish-R-mode ()
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)
2440 (viper-downgrade-to-insert))
2441
2442 (defun viper-start-R-mode ()
2443 ;; Leave arg as 1, not t: XEmacs insists that it must be a pos number
2444 (overwrite-mode 1)
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)
2449 ;; guard against a smartie who switched from R-replace to normal replace
2450 (remove-hook
2451 'viper-post-command-hooks 'viper-replace-state-post-command-sentinel 'local)
2452 )
2453
2454
2455
2456 (defun viper-replace-state-exit-cmd ()
2457 "Binding for keys that cause Replace state to switch to Vi or to Insert.
2458 These keys are ESC, RET, and LineFeed"
2459 (interactive)
2460 (if overwrite-mode ; if in replace mode invoked via 'R'
2461 (viper-finish-R-mode)
2462 (viper-finish-change))
2463 (let (com)
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)
2467 (setq com (key-binding (viper-read-key-sequence nil))))
2468
2469 (condition-case conds
2470 (command-execute com)
2471 (error
2472 (viper-message-conditions conds)))
2473 )
2474 (viper-hide-replace-overlay))
2475
2476
2477 (defun viper-replace-state-carriage-return ()
2478 "Carriage return in Viper replace state."
2479 (interactive)
2480 ;; If Emacs start supporting overlay maps, as it currently supports
2481 ;; text-property maps, we could do away with viper-replace-minor-mode and
2482 ;; just have keymap attached to replace overlay. Then the "if part" of this
2483 ;; statement can be deleted.
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)
2488 (setq com (key-binding (read-key-sequence nil)))
2489 (condition-case conds
2490 (command-execute com)
2491 (error
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 ))))
2506
2507
2508 ;; This is the function bound to 'R'---unlimited replace.
2509 ;; Similar to Emacs's own overwrite-mode.
2510 (defun viper-overwrite (arg)
2511 "Begin overwrite mode."
2512 (interactive "P")
2513 (let ((val (viper-p-val arg))
2514 ;;(com (viper-getcom arg))
2515 (len))
2516 (viper-set-destructive-command (list 'viper-overwrite val ?r nil nil nil))
2517 (if (eq viper-intermediate-command 'viper-repeat)
2518 (progn
2519 ;; Viper saves inserted text in viper-last-insertion
2520 (setq len (length viper-last-insertion))
2521 (delete-char (min len (- (point-max) (point) 1)))
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)
2527 )))
2528
2529 \f
2530 ;; line commands
2531
2532 (defun viper-line (arg)
2533 (let ((val (car arg))
2534 (com (cdr arg)))
2535 (viper-move-marker-locally 'viper-com-point (point))
2536 (if (not (eobp))
2537 (viper-next-line-carefully (1- val)))
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.
2540 (viper-add-newline-at-eob-if-necessary)
2541 (viper-execute-com 'viper-line val com))
2542 (if (and (eobp) (not (bobp))) (forward-line -1))
2543 )
2544
2545 (defun viper-yank-line (arg)
2546 "Yank ARG lines (in Vi's sense)."
2547 (interactive "P")
2548 (let ((val (viper-p-val arg)))
2549 (viper-line (cons val ?Y))))
2550
2551 \f
2552 ;; region commands
2553
2554 (defun viper-region (arg)
2555 "Execute command on a region."
2556 (interactive "P")
2557 (let ((val (viper-P-val arg))
2558 (com (viper-getcom arg)))
2559 (viper-move-marker-locally 'viper-com-point (point))
2560 (exchange-point-and-mark)
2561 (viper-execute-com 'viper-region val com)))
2562
2563 (defun viper-Region (arg)
2564 "Execute command on a Region."
2565 (interactive "P")
2566 (let ((val (viper-P-val arg))
2567 (com (viper-getCom arg)))
2568 (viper-move-marker-locally 'viper-com-point (point))
2569 (exchange-point-and-mark)
2570 (viper-execute-com 'viper-Region val com)))
2571
2572 (defun viper-replace-char (arg)
2573 "Replace the following ARG chars by the character read."
2574 (interactive "P")
2575 (if (and (eolp) (bolp)) (error "No character to replace here"))
2576 (let ((val (viper-p-val arg))
2577 (com (viper-getcom arg)))
2578 (viper-replace-char-subr com val)
2579 (if (and (eolp) (not (bolp))) (forward-char 1))
2580 (setq viper-this-command-keys
2581 (format "%sr" (if (integerp arg) arg "")))
2582 (viper-set-destructive-command
2583 (list 'viper-replace-char val ?r nil viper-d-char nil))
2584 ))
2585
2586 (defun viper-replace-char-subr (com arg)
2587 (let ((inhibit-quit t)
2588 char)
2589 (viper-set-complex-command-for-undo)
2590 (or (eq viper-intermediate-command 'viper-repeat)
2591 (viper-special-read-and-insert-char))
2592
2593 (delete-char 1 t)
2594 (setq char (if com viper-d-char (viper-char-at-pos 'backward)))
2595
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 ))
2607
2608 \f
2609 ;; basic cursor movement. j, k, l, h commands.
2610
2611 (defun viper-forward-char (arg)
2612 "Move point right ARG characters (left if ARG negative).
2613 On reaching end of line, stop and signal error."
2614 (interactive "P")
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
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
2624 ;; the forward motion before the 'viper-execute-com', but, of
2625 ;; course, 'dl' doesn't work on an empty line, so we have to
2626 ;; catch that condition before 'viper-execute-com'
2627 (if (and (eolp) (bolp)) (error "") (forward-char val))
2628 (if com (viper-execute-com 'viper-forward-char val com))
2629 (if (eolp) (progn (backward-char 1) (error ""))))
2630 (forward-char val)
2631 (if com (viper-execute-com 'viper-forward-char val com)))))
2632
2633
2634 (defun viper-backward-char (arg)
2635 "Move point left ARG characters (right if ARG negative).
2636 On reaching beginning of line, stop and signal error."
2637 (interactive "P")
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
2643 (progn
2644 (if (bolp) (error "") (backward-char val))
2645 (if com (viper-execute-com 'viper-backward-char val com)))
2646 (backward-char val)
2647 (if com (viper-execute-com 'viper-backward-char val com)))))
2648
2649
2650 ;; Like forward-char, but doesn't move at end of buffer.
2651 ;; Returns distance traveled
2652 ;; (positive or 0, if arg positive; negative if arg negative).
2653 (defun viper-forward-char-carefully (&optional arg)
2654 (setq arg (or arg 1))
2655 (let ((pt (point)))
2656 (condition-case nil
2657 (forward-char arg)
2658 (error nil))
2659 (if (< (point) pt) ; arg was negative
2660 (- (viper-chars-in-region pt (point)))
2661 (viper-chars-in-region pt (point)))))
2662
2663
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).
2667 (defun viper-backward-char-carefully (&optional arg)
2668 (setq arg (or arg 1))
2669 (let ((pt (point)))
2670 (condition-case nil
2671 (backward-char arg)
2672 (error nil))
2673 (if (> (point) pt) ; arg was negative
2674 (viper-chars-in-region pt (point))
2675 (- (viper-chars-in-region pt (point))))))
2676
2677 (defun viper-next-line-carefully (arg)
2678 (condition-case nil
2679 (next-line arg)
2680 (error nil)))
2681
2682
2683 \f
2684 ;;; Word command
2685
2686 ;; Words are formed from alpha's and nonalphas - <sp>,\t\n are separators for
2687 ;; word movement. When executed with a destructive command, \n is usually left
2688 ;; untouched for the last word. Viper uses syntax table to determine what is a
2689 ;; word and what is a separator. However, \n is always a separator. Also, if
2690 ;; viper-syntax-preference is 'vi, then `_' is part of the word.
2691
2692 ;; skip only one \n
2693 (defun viper-skip-separators (forward)
2694 (if forward
2695 (progn
2696 (viper-skip-all-separators-forward 'within-line)
2697 (if (looking-at "\n")
2698 (progn
2699 (forward-char)
2700 (viper-skip-all-separators-forward 'within-line))))
2701 ;; check for eob and white space before it. move off of eob
2702 (if (and (eobp) (save-excursion
2703 (viper-backward-char-carefully)
2704 (viper-looking-at-separator)))
2705 (viper-backward-char-carefully))
2706 (viper-skip-all-separators-backward 'within-line)
2707 (viper-backward-char-carefully)
2708 (if (looking-at "\n")
2709 (viper-skip-all-separators-backward 'within-line)
2710 (or (bobp) (forward-char)))))
2711
2712
2713 (defun viper-forward-word-kernel (val)
2714 (while (> val 0)
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)))
2723 (setq val (1- val))))
2724
2725 ;; first skip non-newline separators backward, then skip \n. Then, if TWICE is
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
2731 (while (and (not (viper-memq-char prev-char '(nil \n)))
2732 (< lim (point))
2733 ;; must be non-newline separator
2734 (if (eq viper-syntax-preference 'strict-vi)
2735 (viper-memq-char prev-char '(?\ ?\t))
2736 (viper-memq-char (char-syntax prev-char) '(?\ ?-))))
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
2750 (while (and (not (viper-memq-char prev-char '(nil \n)))
2751 (< lim (point))
2752 ;; must be non-newline separator
2753 (if (eq viper-syntax-preference 'strict-vi)
2754 (viper-memq-char prev-char '(?\ ?\t))
2755 (viper-memq-char (char-syntax prev-char) '(?\ ?-))))
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 ))
2762
2763
2764 (defun viper-forward-word (arg)
2765 "Forward word."
2766 (interactive "P")
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)
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 ))
2783
2784
2785 (defun viper-forward-Word (arg)
2786 "Forward word delimited by white characters."
2787 (interactive "P")
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
2793 (viper-skip-nonseparators 'forward)
2794 (viper-skip-separators t))
2795 (if com (progn
2796 (cond ((viper-char-equal com ?c)
2797 (viper-separator-skipback-special 'twice viper-com-point))
2798 ;; Yank words including the whitespace, but not newline
2799 ((viper-char-equal com ?y)
2800 (viper-separator-skipback-special nil viper-com-point))
2801 ((viper-dotable-command-p com)
2802 (viper-separator-skipback-special nil viper-com-point)))
2803 (viper-execute-com 'viper-forward-Word val com)))))
2804
2805
2806 ;; this is a bit different from Vi, but Vi's end of word
2807 ;; makes no sense whatsoever
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))
2812
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))
2816
2817 (defun viper-end-of-word-p ()
2818 (or (eobp)
2819 (save-excursion
2820 (cond ((viper-looking-at-alpha)
2821 (forward-char)
2822 (not (viper-looking-at-alpha)))
2823 ((not (viper-looking-at-alphasep))
2824 (forward-char)
2825 (viper-looking-at-alphasep))))))
2826
2827
2828 (defun viper-end-of-word (arg &optional careful)
2829 "Move point to end of current word."
2830 (interactive "P")
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))
2836 (if com
2837 (progn
2838 (forward-char)
2839 (viper-execute-com 'viper-end-of-word val com)))))
2840
2841 (defun viper-end-of-Word (arg)
2842 "Forward to end of word delimited by white character."
2843 (interactive "P")
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
2849 (viper-end-of-word-kernel)
2850 (viper-skip-nonseparators 'forward)
2851 (backward-char))
2852 (if com
2853 (progn
2854 (forward-char)
2855 (viper-execute-com 'viper-end-of-Word val com)))))
2856
2857 (defun viper-backward-word-kernel (val)
2858 (while (> val 0)
2859 (viper-backward-char-carefully)
2860 (cond ((viper-looking-at-alpha)
2861 (viper-skip-alpha-backward "_"))
2862 ((viper-looking-at-separator)
2863 (forward-char)
2864 (viper-skip-separators nil)
2865 (viper-backward-char-carefully)
2866 (cond ((viper-looking-at-alpha)
2867 (viper-skip-alpha-backward "_"))
2868 ((not (viper-looking-at-alphasep))
2869 (viper-skip-nonalphasep-backward))
2870 ((bobp)) ; could still be at separator, but at beg of buffer
2871 (t (forward-char))))
2872 ((not (viper-looking-at-alphasep))
2873 (viper-skip-nonalphasep-backward)))
2874 (setq val (1- val))))
2875
2876 (defun viper-backward-word (arg)
2877 "Backward word."
2878 (interactive "P")
2879 (viper-leave-region-active)
2880 (let ((val (viper-p-val arg))
2881 (com (viper-getcom arg)))
2882 (if com
2883 (let (i)
2884 (if (setq i (save-excursion (backward-char) (looking-at "\n")))
2885 (backward-char))
2886 (viper-move-marker-locally 'viper-com-point (point))
2887 (if i (forward-char))))
2888 (viper-backward-word-kernel val)
2889 (if com (viper-execute-com 'viper-backward-word val com))))
2890
2891 (defun viper-backward-Word (arg)
2892 "Backward word delimited by white character."
2893 (interactive "P")
2894 (viper-leave-region-active)
2895 (let ((val (viper-p-val arg))
2896 (com (viper-getcom arg)))
2897 (if com
2898 (let (i)
2899 (if (setq i (save-excursion (backward-char) (looking-at "\n")))
2900 (backward-char))
2901 (viper-move-marker-locally 'viper-com-point (point))
2902 (if i (forward-char))))
2903 (viper-loop val
2904 (viper-skip-separators nil) ; nil means backward here
2905 (viper-skip-nonseparators 'backward))
2906 (if com (viper-execute-com 'viper-backward-Word val com))))
2907
2908
2909 \f
2910 ;; line commands
2911
2912 (defun viper-beginning-of-line (arg)
2913 "Go to beginning of line."
2914 (interactive "P")
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)))
2919 (beginning-of-line val)
2920 (if com (viper-execute-com 'viper-beginning-of-line val com))))
2921
2922 (defun viper-bol-and-skip-white (arg)
2923 "Beginning of line at first non-white character."
2924 (interactive "P")
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)))
2929 (forward-to-indentation (1- val))
2930 (if com (viper-execute-com 'viper-bol-and-skip-white val com))))
2931
2932 (defun viper-goto-eol (arg)
2933 "Go to end of line."
2934 (interactive "P")
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)))
2939 (end-of-line val)
2940 (if com (viper-execute-com 'viper-goto-eol val com))
2941 (if viper-ex-style-motion
2942 (if (and (eolp) (not (bolp))
2943 ;; a fix for viper-change-to-eol
2944 (not (equal viper-current-state 'insert-state)))
2945 (backward-char 1)
2946 ))))
2947
2948
2949 (defun viper-goto-col (arg)
2950 "Go to ARG's column."
2951 (interactive "P")
2952 (viper-leave-region-active)
2953 (let ((val (viper-p-val arg))
2954 (com (viper-getcom arg))
2955 line-len)
2956 (setq line-len
2957 (viper-chars-in-region
2958 (viper-line-pos 'start) (viper-line-pos 'end)))
2959 (if com (viper-move-marker-locally 'viper-com-point (point)))
2960 (beginning-of-line)
2961 (forward-char (1- (min line-len val)))
2962 (while (> (current-column) (1- val))
2963 (backward-char 1))
2964 (if com (viper-execute-com 'viper-goto-col val com))
2965 (save-excursion
2966 (end-of-line)
2967 (if (> val (current-column)) (error "")))
2968 ))
2969
2970
2971 (defun viper-next-line (arg)
2972 "Go to next line."
2973 (interactive "P")
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)))
2978 (next-line val)
2979 (if viper-ex-style-motion
2980 (if (and (eolp) (not (bolp))) (backward-char 1)))
2981 (setq this-command 'next-line)
2982 (if com (viper-execute-com 'viper-next-line val com))))
2983
2984 (defun viper-next-line-at-bol (arg)
2985 "Next line at beginning of line."
2986 (interactive "P")
2987 (viper-leave-region-active)
2988 (save-excursion
2989 (end-of-line)
2990 (if (eobp) (error "Last line in buffer")))
2991 (let ((val (viper-p-val arg))
2992 (com (viper-getCom arg)))
2993 (if com (viper-move-marker-locally 'viper-com-point (point)))
2994 (forward-line val)
2995 (back-to-indentation)
2996 (if com (viper-execute-com 'viper-next-line-at-bol val com))))
2997
2998
2999 (defun viper-previous-line (arg)
3000 "Go to previous line."
3001 (interactive "P")
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)))
3006 (previous-line val)
3007 (if viper-ex-style-motion
3008 (if (and (eolp) (not (bolp))) (backward-char 1)))
3009 (setq this-command 'previous-line)
3010 (if com (viper-execute-com 'viper-previous-line val com))))
3011
3012
3013 (defun viper-previous-line-at-bol (arg)
3014 "Previous line at beginning of line."
3015 (interactive "P")
3016 (viper-leave-region-active)
3017 (save-excursion
3018 (beginning-of-line)
3019 (if (bobp) (error "First line in buffer")))
3020 (let ((val (viper-p-val arg))
3021 (com (viper-getCom arg)))
3022 (if com (viper-move-marker-locally 'viper-com-point (point)))
3023 (forward-line (- val))
3024 (back-to-indentation)
3025 (if com (viper-execute-com 'viper-previous-line val com))))
3026
3027 (defun viper-change-to-eol (arg)
3028 "Change to end of line."
3029 (interactive "P")
3030 (viper-goto-eol (cons arg ?c)))
3031
3032 (defun viper-kill-line (arg)
3033 "Delete line."
3034 (interactive "P")
3035 (viper-goto-eol (cons arg ?d)))
3036
3037 (defun viper-erase-line (arg)
3038 "Erase line."
3039 (interactive "P")
3040 (viper-beginning-of-line (cons arg ?d)))
3041
3042 \f
3043 ;;; Moving around
3044
3045 (defun viper-goto-line (arg)
3046 "Go to ARG's line. Without ARG go to end of buffer."
3047 (interactive "P")
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)
3052 (push-mark nil t)
3053 (if (null val)
3054 (goto-char (point-max))
3055 (goto-char (point-min))
3056 (forward-line (1- val)))
3057
3058 ;; positioning is done twice: before and after command execution
3059 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3060 (back-to-indentation)
3061
3062 (if com (viper-execute-com 'viper-goto-line val com))
3063
3064 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3065 (back-to-indentation)
3066 ))
3067
3068 ;; Find ARG's occurrence of CHAR on the current line.
3069 ;; If FORWARD then search is forward, otherwise backward. OFFSET is used to
3070 ;; adjust point after search.
3071 (defun viper-find-char (arg char forward offset)
3072 (or (char-or-string-p char) (error ""))
3073 (let ((arg (if forward arg (- arg)))
3074 (cmd (if (eq viper-intermediate-command 'viper-repeat)
3075 (nth 5 viper-d-com)
3076 (viper-array-to-string (this-command-keys))))
3077 point region-beg region-end)
3078 (save-excursion
3079 (save-restriction
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)
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))))
3115 (goto-char point)
3116 (if (> arg 0)
3117 (backward-char (if offset 2 1))
3118 (forward-char (if offset 1 0)))))
3119
3120 (defun viper-find-char-forward (arg)
3121 "Find char on the line.
3122 If called interactively read the char to find from the terminal, and if
3123 called from viper-repeat, the char last used is used. This behaviour is
3124 controlled by the sign of prefix numeric value."
3125 (interactive "P")
3126 (let ((val (viper-p-val arg))
3127 (com (viper-getcom arg))
3128 (cmd-representation (nth 5 viper-d-com)))
3129 (if (> val 0)
3130 ;; this means that the function was called interactively
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)
3139 (setq val (- val)))
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)
3143 (setq val (- val))
3144 (if com
3145 (progn
3146 (setq viper-F-char viper-f-char) ; set new viper-F-char
3147 (forward-char)
3148 (viper-execute-com 'viper-find-char-forward val com)))))
3149
3150 (defun viper-goto-char-forward (arg)
3151 "Go up to char ARG forward on line."
3152 (interactive "P")
3153 (let ((val (viper-p-val arg))
3154 (com (viper-getcom arg))
3155 (cmd-representation (nth 5 viper-d-com)))
3156 (if (> val 0)
3157 ;; this means that the function was called interactively
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)
3166 (setq val (- val)))
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)
3170 (setq val (- val))
3171 (if com
3172 (progn
3173 (setq viper-F-char viper-f-char) ; set new viper-F-char
3174 (forward-char)
3175 (viper-execute-com 'viper-goto-char-forward val com)))))
3176
3177 (defun viper-find-char-backward (arg)
3178 "Find char ARG on line backward."
3179 (interactive "P")
3180 (let ((val (viper-p-val arg))
3181 (com (viper-getcom arg))
3182 (cmd-representation (nth 5 viper-d-com)))
3183 (if (> val 0)
3184 ;; this means that the function was called interactively
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)
3193 (setq val (- val)))
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)
3197 (setq val (- val))
3198 (if com
3199 (progn
3200 (setq viper-F-char viper-f-char) ; set new viper-F-char
3201 (viper-execute-com 'viper-find-char-backward val com)))))
3202
3203 (defun viper-goto-char-backward (arg)
3204 "Go up to char ARG backward on line."
3205 (interactive "P")
3206 (let ((val (viper-p-val arg))
3207 (com (viper-getcom arg))
3208 (cmd-representation (nth 5 viper-d-com)))
3209 (if (> val 0)
3210 ;; this means that the function was called interactively
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)
3219 (setq val (- val)))
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)
3223 (setq val (- val))
3224 (if com
3225 (progn
3226 (setq viper-F-char viper-f-char) ; set new viper-F-char
3227 (viper-execute-com 'viper-goto-char-backward val com)))))
3228
3229 (defun viper-repeat-find (arg)
3230 "Repeat previous find command."
3231 (interactive "P")
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)
3237 (if com
3238 (progn
3239 (if viper-f-forward (forward-char))
3240 (viper-execute-com 'viper-repeat-find val com)))))
3241
3242 (defun viper-repeat-find-opposite (arg)
3243 "Repeat previous find command in the opposite direction."
3244 (interactive "P")
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)
3250 (if com
3251 (progn
3252 (if viper-f-forward (forward-char))
3253 (viper-execute-com 'viper-repeat-find-opposite val com)))))
3254
3255 \f
3256 ;; window scrolling etc.
3257
3258 (defun viper-window-top (arg)
3259 "Go to home window line."
3260 (interactive "P")
3261 (let ((val (viper-p-val arg))
3262 (com (viper-getCom arg)))
3263 (viper-leave-region-active)
3264 (if com (viper-move-marker-locally 'viper-com-point (point)))
3265 (push-mark nil t)
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)
3271
3272 (if com (viper-execute-com 'viper-window-top val com))
3273
3274 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3275 (back-to-indentation)
3276 ))
3277
3278 (defun viper-window-middle (arg)
3279 "Go to middle window line."
3280 (interactive "P")
3281 (let ((val (viper-p-val arg))
3282 (com (viper-getCom arg)))
3283 (viper-leave-region-active)
3284 (if com (viper-move-marker-locally 'viper-com-point (point)))
3285 (push-mark nil t)
3286 (move-to-window-line (+ (/ (1- (window-height)) 2) (1- val)))
3287
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
3292 (if com (viper-execute-com 'viper-window-middle val com))
3293
3294 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3295 (back-to-indentation)
3296 ))
3297
3298 (defun viper-window-bottom (arg)
3299 "Go to last window line."
3300 (interactive "P")
3301 (let ((val (viper-p-val arg))
3302 (com (viper-getCom arg)))
3303 (viper-leave-region-active)
3304 (if com (viper-move-marker-locally 'viper-com-point (point)))
3305 (push-mark nil t)
3306 (move-to-window-line (- val))
3307
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
3312 (if com (viper-execute-com 'viper-window-bottom val com))
3313
3314 (if (and (eobp) (bolp) (not (bobp))) (forward-line -1))
3315 (back-to-indentation)
3316 ))
3317
3318 (defun viper-line-to-top (arg)
3319 "Put current line on the home line."
3320 (interactive "p")
3321 (recenter (1- arg)))
3322
3323 (defun viper-line-to-middle (arg)
3324 "Put current line on the middle line."
3325 (interactive "p")
3326 (recenter (+ (1- arg) (/ (1- (window-height)) 2))))
3327
3328 (defun viper-line-to-bottom (arg)
3329 "Put current line on the last line."
3330 (interactive "p")
3331 (recenter (- (window-height) (1+ arg))))
3332
3333 ;; If point is within viper-search-scroll-threshold of window top or bottom,
3334 ;; scroll up or down 1/7 of window height, depending on whether we are at the
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
3337 ;; viper-search-scroll-threshold is negative - don't scroll.
3338 (defun viper-adjust-window ()
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 ))
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))
3351 viper-search-scroll-threshold))
3352 (move-to-window-line -1) ; bottom
3353 (setq at-bottom-p
3354 (<= (count-lines pt (point)) viper-search-scroll-threshold))
3355 )
3356 (cond (at-top-p (setq min-scroll (1- viper-search-scroll-threshold)
3357 direction 1))
3358 (at-bottom-p (setq min-scroll (1+ viper-search-scroll-threshold)
3359 direction -1)))
3360 (if min-scroll
3361 (recenter
3362 (* (max min-scroll (/ win-height 7)) direction)))
3363 ))
3364
3365 \f
3366 ;; paren match
3367 ;; must correct this to only match ( to ) etc. On the other hand
3368 ;; it is good that paren match gets confused, because that way you
3369 ;; catch _all_ imbalances.
3370
3371 (defun viper-paren-match (arg)
3372 "Go to the matching parenthesis."
3373 (interactive "P")
3374 (viper-leave-region-active)
3375 (let ((com (viper-getcom arg))
3376 (parse-sexp-ignore-comments viper-parse-sexp-ignore-comments)
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)))
3395 (cond ((re-search-forward "[][(){}]" end-lim t)
3396 (backward-char) )
3397 ((re-search-backward "[][(){}]" beg-lim t))
3398 (t
3399 (error "No matching character on line"))))
3400 (cond ((looking-at "[\(\[{]")
3401 (if com (viper-move-marker-locally 'viper-com-point (point)))
3402 (forward-sexp 1)
3403 (if com
3404 (viper-execute-com 'viper-paren-match nil com)
3405 (backward-char)))
3406 (anchor-point
3407 (if com
3408 (progn
3409 (viper-move-marker-locally 'viper-com-point anchor-point)
3410 (forward-char 1)
3411 (viper-execute-com 'viper-paren-match nil com)
3412 )))
3413 ((looking-at "[])}]")
3414 (forward-char)
3415 (if com (viper-move-marker-locally 'viper-com-point (point)))
3416 (backward-sexp 1)
3417 (if com (viper-execute-com 'viper-paren-match nil com)))
3418 (t (error ""))))))
3419
3420 (defun viper-toggle-parse-sexp-ignore-comments ()
3421 (interactive)
3422 (setq viper-parse-sexp-ignore-comments
3423 (not viper-parse-sexp-ignore-comments))
3424 (princ (format
3425 "From now on, `%%' will %signore parentheses inside comment fields"
3426 (if viper-parse-sexp-ignore-comments "" "NOT "))))
3427
3428 \f
3429 ;; sentence, paragraph and heading
3430
3431 (defun viper-forward-sentence (arg)
3432 "Forward sentence."
3433 (interactive "P")
3434 (or (eq last-command this-command)
3435 (push-mark nil t))
3436 (let ((val (viper-p-val arg))
3437 (com (viper-getcom arg)))
3438 (if com (viper-move-marker-locally 'viper-com-point (point)))
3439 (forward-sentence val)
3440 (if com (viper-execute-com 'viper-forward-sentence nil com))))
3441
3442 (defun viper-backward-sentence (arg)
3443 "Backward sentence."
3444 (interactive "P")
3445 (or (eq last-command this-command)
3446 (push-mark nil t))
3447 (let ((val (viper-p-val arg))
3448 (com (viper-getcom arg)))
3449 (if com (viper-move-marker-locally 'viper-com-point (point)))
3450 (backward-sentence val)
3451 (if com (viper-execute-com 'viper-backward-sentence nil com))))
3452
3453 (defun viper-forward-paragraph (arg)
3454 "Forward paragraph."
3455 (interactive "P")
3456 (or (eq last-command this-command)
3457 (push-mark nil t))
3458 (let ((val (viper-p-val arg))
3459 ;; if you want d} operate on whole lines, change viper-getcom to
3460 ;; viper-getCom below
3461 (com (viper-getcom arg)))
3462 (if com (viper-move-marker-locally 'viper-com-point (point)))
3463 (forward-paragraph val)
3464 (if com
3465 (progn
3466 (backward-char 1)
3467 (viper-execute-com 'viper-forward-paragraph nil com)))))
3468
3469 (defun viper-backward-paragraph (arg)
3470 "Backward paragraph."
3471 (interactive "P")
3472 (or (eq last-command this-command)
3473 (push-mark nil t))
3474 (let ((val (viper-p-val arg))
3475 ;; if you want d{ operate on whole lines, change viper-getcom to
3476 ;; viper-getCom below
3477 (com (viper-getcom arg)))
3478 (if com (viper-move-marker-locally 'viper-com-point (point)))
3479 (backward-paragraph val)
3480 (if com
3481 (progn
3482 (forward-char 1)
3483 (viper-execute-com 'viper-backward-paragraph nil com)
3484 (backward-char 1)))))
3485
3486 ;; should be mode-specific
3487 (defun viper-prev-heading (arg)
3488 (interactive "P")
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)
3493 (goto-char (match-beginning 0))
3494 (if com (viper-execute-com 'viper-prev-heading nil com))))
3495
3496 (defun viper-heading-end (arg)
3497 (interactive "P")
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)
3502 (goto-char (match-beginning 0))
3503 (if com (viper-execute-com 'viper-heading-end nil com))))
3504
3505 (defun viper-next-heading (arg)
3506 (interactive "P")
3507 (let ((val (viper-p-val arg))
3508 (com (viper-getCom arg)))
3509 (if com (viper-move-marker-locally 'viper-com-point (point)))
3510 (end-of-line)
3511 (re-search-forward viper-heading-start nil t val)
3512 (goto-char (match-beginning 0))
3513 (if com (viper-execute-com 'viper-next-heading nil com))))
3514
3515 \f
3516 ;; scrolling
3517
3518 (defun viper-scroll-screen (arg)
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
3538 (defun viper-scroll-screen-back (arg)
3539 "Scroll to previous screen."
3540 (interactive "p")
3541 (viper-scroll-screen (- arg)))
3542
3543 (defun viper-scroll-down (arg)
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
3554 (defun viper-scroll-down-one (arg)
3555 "Scroll up one line."
3556 (interactive "p")
3557 (scroll-down arg))
3558
3559 (defun viper-scroll-up (arg)
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
3570 (defun viper-scroll-up-one (arg)
3571 "Scroll down one line."
3572 (interactive "p")
3573 (scroll-up arg))
3574
3575 \f
3576 ;; searching
3577
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
3583 prompt
3584 nil ; no initial
3585 'viper-search-history
3586 (car viper-search-history))))
3587 (if (not (string= s ""))
3588 (setq viper-s-string s))))
3589
3590
3591 (defun viper-toggle-search-style (arg)
3592 "Toggle the value of viper-case-fold-search/viper-re-search.
3593 Without prefix argument, will ask which search style to toggle. With prefix
3594 arg 1,toggles viper-case-fold-search; with arg 2 toggles viper-re-search.
3595
3596 Although this function is bound to \\[viper-toggle-search-style], the most
3597 convenient way to use it is to bind `//' to the macro
3598 `1 M-x viper-toggle-search-style' and `///' to
3599 `2 M-x viper-toggle-search-style'. In this way, hitting `//' quickly will
3600 toggle case-fold-search and hitting `/' three times witth toggle regexp
3601 search. Macros are more convenient in this case because they don't affect
3602 the Emacs binding of `/'."
3603 (interactive "P")
3604 (let (msg)
3605 (cond ((or (eq arg 1)
3606 (and (null arg)
3607 (y-or-n-p (format "Search style: '%s'. Want '%s'? "
3608 (if viper-case-fold-search
3609 "case-insensitive" "case-sensitive")
3610 (if viper-case-fold-search
3611 "case-sensitive"
3612 "case-insensitive")))))
3613 (setq viper-case-fold-search (null viper-case-fold-search))
3614 (if viper-case-fold-search
3615 (setq msg "Search becomes case-insensitive")
3616 (setq msg "Search becomes case-sensitive")))
3617 ((or (eq arg 2)
3618 (and (null arg)
3619 (y-or-n-p (format "Search style: '%s'. Want '%s'? "
3620 (if viper-re-search
3621 "regexp-search" "vanilla-search")
3622 (if viper-re-search
3623 "vanilla-search"
3624 "regexp-search")))))
3625 (setq viper-re-search (null viper-re-search))
3626 (if viper-re-search
3627 (setq msg "Search becomes regexp-style")
3628 (setq msg "Search becomes vanilla-style")))
3629 (t
3630 (setq msg "Search style remains unchanged")))
3631 (princ msg t)))
3632
3633 (defun viper-set-searchstyle-toggling-macros (unset)
3634 "Set the macros for toggling the search style in Viper's vi-state.
3635 The macro that toggles case sensitivity is bound to `//', and the one that
3636 toggles regexp search is bound to `///'.
3637 With 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
3643 (viper-record-kbd-macro
3644 "//" 'vi-state
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]
3646 't)
3647 ;; toggle regexp/vanila search
3648 (viper-record-kbd-macro
3649 "///" 'vi-state
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]
3651 't)
3652 (if (interactive-p)
3653 (message
3654 "// and /// now toggle case-sensitivity and regexp search")))
3655 (viper-unrecord-kbd-macro "//" 'vi-state)
3656 (sit-for 2)
3657 (viper-unrecord-kbd-macro "///" 'vi-state))))
3658
3659
3660 (defun viper-set-parsing-style-toggling-macro (unset)
3661 "Set `%%%' to be a macro that toggles whether comment fields should be parsed for matching parentheses.
3662 This is used in conjunction with the `%' command.
3663
3664 With 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
3670 (viper-record-kbd-macro
3671 "%%%" 'vi-state
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]
3673 't)
3674 (if (interactive-p)
3675 (message
3676 "%%%%%% now toggles whether comments should be parsed for matching parentheses")))
3677 (viper-unrecord-kbd-macro "%%%" 'vi-state))))
3678
3679
3680 (defun viper-set-emacs-state-searchstyle-macros (unset &optional arg-majormode)
3681 "Set the macros for toggling the search style in Viper's emacs-state.
3682 The macro that toggles case sensitivity is bound to `//', and the one that
3683 toggles regexp search is bound to `///'.
3684 With a prefix argument, this function unsets the macros.
3685 If the optional prefix argument is non-nil and specifies a valid major mode,
3686 this sets the macros only in the macros in that major mode. Otherwise,
3687 the 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
3694 (viper-record-kbd-macro
3695 "//" 'emacs-state
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]
3697 (or arg-majormode major-mode))
3698 ;; toggle regexp/vanila search
3699 (viper-record-kbd-macro
3700 "///" 'emacs-state
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]
3702 (or arg-majormode major-mode))
3703 (if (interactive-p)
3704 (message
3705 "// and /// now toggle case-sensitivity and regexp search.")))
3706 (viper-unrecord-kbd-macro "//" 'emacs-state)
3707 (sit-for 2)
3708 (viper-unrecord-kbd-macro "///" 'emacs-state))))
3709
3710
3711 (defun viper-search-forward (arg)
3712 "Search a string forward.
3713 ARG is used to find the ARG's occurrence of the string.
3714 Null string will repeat previous search."
3715 (interactive "P")
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 "/")
3721 ;; this is not used at present, but may be used later
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)
3727 (if com
3728 (progn
3729 (viper-move-marker-locally 'viper-com-point (mark t))
3730 (viper-execute-com 'viper-search-next val com)))))
3731
3732 (defun viper-search-backward (arg)
3733 "Search a string backward.
3734 ARG is used to find the ARG's occurrence of the string.
3735 Null string will repeat previous search."
3736 (interactive "P")
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 "?")
3742 ;; this is not used at present, but may be used later
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)
3748 (if com
3749 (progn
3750 (viper-move-marker-locally 'viper-com-point (mark t))
3751 (viper-execute-com 'viper-search-next val com)))))
3752
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)
3759 (defun viper-search (string forward arg
3760 &optional no-offset init-point fail-if-not-found)
3761 (if (not (equal string ""))
3762 (let ((val (viper-p-val arg))
3763 (com (viper-getcom arg))
3764 (offset (not no-offset))
3765 (case-fold-search viper-case-fold-search)
3766 (start-point (or init-point (point))))
3767 (viper-deactivate-mark)
3768 (if forward
3769 (condition-case nil
3770 (progn
3771 (if offset (viper-forward-char-carefully))
3772 (if viper-re-search
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)))
3779 (push-mark start-point t)))
3780 (search-failed
3781 (if (and (not fail-if-not-found) viper-search-wrap-around-t)
3782 (progn
3783 (message "Search wrapped around BOTTOM of buffer")
3784 (goto-char (point-min))
3785 (viper-search string forward (cons 1 com) t start-point 'fail)
3786 ;; don't wait in macros
3787 (or executing-kbd-macro
3788 (memq viper-intermediate-command
3789 '(viper-repeat
3790 viper-digit-argument
3791 viper-command-argument))
3792 (sit-for 2))
3793 ;; delete the wrap-around message
3794 (message "")
3795 )
3796 (goto-char start-point)
3797 (error "`%s': %s not found"
3798 string
3799 (if viper-re-search "Pattern" "String"))
3800 )))
3801 ;; backward
3802 (condition-case nil
3803 (progn
3804 (if viper-re-search
3805 (re-search-backward string nil nil val)
3806 (search-backward string nil nil val))
3807 (if (not (equal start-point (point)))
3808 (push-mark start-point t)))
3809 (search-failed
3810 (if (and (not fail-if-not-found) viper-search-wrap-around-t)
3811 (progn
3812 (message "Search wrapped around TOP of buffer")
3813 (goto-char (point-max))
3814 (viper-search string forward (cons 1 com) t start-point 'fail)
3815 ;; don't wait in macros
3816 (or executing-kbd-macro
3817 (memq viper-intermediate-command
3818 '(viper-repeat
3819 viper-digit-argument
3820 viper-command-argument))
3821 (sit-for 2))
3822 ;; delete the wrap-around message
3823 (message "")
3824 )
3825 (goto-char start-point)
3826 (error "`%s': %s not found"
3827 string
3828 (if viper-re-search "Pattern" "String"))
3829 ))))
3830 ;; pull up or down if at top/bottom of window
3831 (viper-adjust-window)
3832 ;; highlight the result of search
3833 ;; don't wait and don't highlight in macros
3834 (or executing-kbd-macro
3835 (memq viper-intermediate-command
3836 '(viper-repeat viper-digit-argument viper-command-argument))
3837 (viper-flash-search-pattern))
3838 )))
3839
3840 (defun viper-search-next (arg)
3841 "Repeat previous search."
3842 (interactive "P")
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)
3847 (if com
3848 (progn
3849 (viper-move-marker-locally 'viper-com-point (mark t))
3850 (viper-execute-com 'viper-search-next val com)))))
3851
3852 (defun viper-search-Next (arg)
3853 "Repeat previous search in the reverse direction."
3854 (interactive "P")
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)
3859 (if com
3860 (progn
3861 (viper-move-marker-locally 'viper-com-point (mark t))
3862 (viper-execute-com 'viper-search-Next val com)))))
3863
3864
3865 ;; Search contents of buffer defined by one of Viper's motion commands.
3866 ;; Repeatable via `n' and `N'.
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
3872 (cond ((viper-characterp viper-buffer-search-char)
3873 (char-to-string viper-buffer-search-char))
3874 (t (error "viper-buffer-search-char: wrong value type, %S"
3875 viper-buffer-search-char)))
3876 'viper-command-argument)
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)))
3880
3881 ;; This is a Viper wraper for isearch-forward.
3882 (defun viper-isearch-forward (arg)
3883 "Do incremental search forward."
3884 (interactive "P")
3885 ;; emacs bug workaround
3886 (if (listp arg) (setq arg (car arg)))
3887 (viper-exec-form-in-emacs (list 'isearch-forward arg)))
3888
3889 ;; This is a Viper wraper for isearch-backward."
3890 (defun viper-isearch-backward (arg)
3891 "Do incremental search backward."
3892 (interactive "P")
3893 ;; emacs bug workaround
3894 (if (listp arg) (setq arg (car arg)))
3895 (viper-exec-form-in-emacs (list 'isearch-backward arg)))
3896
3897 \f
3898 ;; visiting and killing files, buffers
3899
3900 (defun viper-switch-to-buffer ()
3901 "Switch to buffer in the current window."
3902 (interactive)
3903 (let ((other-buffer (other-buffer (current-buffer)))
3904 buffer)
3905 (setq buffer
3906 (funcall viper-read-buffer-function
3907 "Switch to buffer in this window: " other-buffer))
3908 (switch-to-buffer buffer)))
3909
3910 (defun viper-switch-to-buffer-other-window ()
3911 "Switch to buffer in another window."
3912 (interactive)
3913 (let ((other-buffer (other-buffer (current-buffer)))
3914 buffer)
3915 (setq buffer
3916 (funcall viper-read-buffer-function
3917 "Switch to buffer in another window: " other-buffer))
3918 (switch-to-buffer-other-window buffer)))
3919
3920 (defun viper-kill-buffer ()
3921 "Kill a buffer."
3922 (interactive)
3923 (let (buffer buffer-name)
3924 (setq buffer-name
3925 (funcall viper-read-buffer-function
3926 (format "Kill buffer \(%s\): "
3927 (buffer-name (current-buffer)))))
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))
3934 (y-or-n-p
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
3941
3942 \f
3943 ;; yank and pop
3944
3945 (defsubst viper-yank (text)
3946 "Yank TEXT silently. This works correctly with Emacs's yank-pop command."
3947 (insert text)
3948 (setq this-command 'yank))
3949
3950 (defun viper-put-back (arg)
3951 "Put back after point/below line."
3952 (interactive "P")
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)))
3961 (current-kill 0)))
3962 sv-point chars-inserted lines-inserted)
3963 (if (null text)
3964 (if viper-use-register
3965 (let ((reg viper-use-register))
3966 (setq viper-use-register nil)
3967 (error viper-EmptyRegister reg))
3968 (error "")))
3969 (setq viper-use-register nil)
3970 (if (viper-end-with-a-newline-p text)
3971 (progn
3972 (end-of-line)
3973 (if (eobp)
3974 (insert "\n")
3975 (forward-line 1))
3976 (beginning-of-line))
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))
3981 (setq sv-point (point))
3982 (viper-loop val (viper-yank text))
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)))
3989 ;; Vi puts cursor on the last char when the yanked text doesn't contain a
3990 ;; newline; it leaves the cursor at the beginning when the text contains
3991 ;; a newline
3992 (if (viper-same-line (point) (mark))
3993 (or (= (point) (mark)) (viper-backward-char-carefully))
3994 (exchange-point-and-mark)
3995 (if (bolp)
3996 (back-to-indentation)))
3997 (viper-deactivate-mark))
3998
3999 (defun viper-Put-back (arg)
4000 "Put back at point/above line."
4001 (interactive "P")
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)))
4010 (current-kill 0)))
4011 sv-point chars-inserted lines-inserted)
4012 (if (null text)
4013 (if viper-use-register
4014 (let ((reg viper-use-register))
4015 (setq viper-use-register nil)
4016 (error viper-EmptyRegister reg))
4017 (error "")))
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))
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)))
4031 ;; Vi puts cursor on the last char when the yanked text doesn't contain a
4032 ;; newline; it leaves the cursor at the beginning when the text contains
4033 ;; a newline
4034 (if (viper-same-line (point) (mark))
4035 (or (= (point) (mark)) (viper-backward-char-carefully))
4036 (exchange-point-and-mark)
4037 (if (bolp)
4038 (back-to-indentation)))
4039 (viper-deactivate-mark))
4040
4041
4042 ;; Copy region to kill-ring.
4043 ;; If BEG and END do not belong to the same buffer, copy empty region.
4044 (defun viper-copy-region-as-kill (beg end)
4045 (condition-case nil
4046 (copy-region-as-kill beg end)
4047 (error (copy-region-as-kill beg beg))))
4048
4049
4050 (defun viper-delete-char (arg)
4051 "Delete next character."
4052 (interactive "P")
4053 (let ((val (viper-p-val arg))
4054 end-del-pos)
4055 (viper-set-destructive-command
4056 (list 'viper-delete-char val nil nil nil nil))
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))))
4060 (if (and viper-ex-style-motion (eolp))
4061 (if (bolp) (error "") (setq val 0))) ; not bol---simply back 1 ch
4062 (save-excursion
4063 (viper-forward-char-carefully val)
4064 (setq end-del-pos (point)))
4065 (if viper-use-register
4066 (progn
4067 (cond ((viper-valid-register viper-use-register '((Letter)))
4068 (viper-append-to-register
4069 (downcase viper-use-register) (point) end-del-pos))
4070 ((viper-valid-register viper-use-register)
4071 (copy-to-register
4072 viper-use-register (point) end-del-pos nil))
4073 (t (error viper-InvalidRegister viper-use-register)))
4074 (setq viper-use-register nil)))
4075
4076 (delete-char val t)
4077 (if viper-ex-style-motion
4078 (if (and (eolp) (not (bolp))) (backward-char 1)))
4079 ))
4080
4081 (defun viper-delete-backward-char (arg)
4082 "Delete previous character. On reaching beginning of line, stop and beep."
4083 (interactive "P")
4084 (let ((val (viper-p-val arg))
4085 end-del-pos)
4086 (viper-set-destructive-command
4087 (list 'viper-delete-backward-char val nil nil nil nil))
4088 (if (and
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)))
4095 (if viper-use-register
4096 (progn
4097 (cond ((viper-valid-register viper-use-register '(Letter))
4098 (viper-append-to-register
4099 (downcase viper-use-register) end-del-pos (point)))
4100 ((viper-valid-register viper-use-register)
4101 (copy-to-register
4102 viper-use-register end-del-pos (point) nil))
4103 (t (error viper-InvalidRegister viper-use-register)))
4104 (setq viper-use-register nil)))
4105 (if (and (bolp) viper-ex-style-editing)
4106 (ding))
4107 (delete-backward-char val t)))
4108
4109
4110 (defun viper-del-backward-char-in-insert ()
4111 "Delete 1 char backwards while in insert mode."
4112 (interactive)
4113 (if (and viper-ex-style-editing (bolp))
4114 (beep 1)
4115 (delete-backward-char 1 t)))
4116
4117
4118 (defun viper-del-backward-char-in-replace ()
4119 "Delete one character in replace mode.
4120 If `viper-delete-backwards-in-replace' is t, then DEL key actually deletes
4121 charecters. If it is nil, then the cursor just moves backwards, similarly
4122 to Vi. The variable `viper-ex-style-editing', if t, doesn't let the
4123 cursor move past the beginning of line."
4124 (interactive)
4125 (cond (viper-delete-backwards-in-replace
4126 (cond ((not (bolp))
4127 (delete-backward-char 1 t))
4128 (viper-ex-style-editing
4129 (beep 1))
4130 ((bobp)
4131 (beep 1))
4132 (t
4133 (delete-backward-char 1 t))))
4134 (viper-ex-style-editing
4135 (if (bolp)
4136 (beep 1)
4137 (backward-char 1)))
4138 (t
4139 (backward-char 1))))
4140
4141
4142 \f
4143 ;; join lines.
4144
4145 (defun viper-join-lines (arg)
4146 "Join this line to next, if ARG is nil. Otherwise, join ARG lines."
4147 (interactive "*P")
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))
4152 (end-of-line)
4153 (if (not (eobp))
4154 (progn
4155 (forward-line 1)
4156 (delete-region (point) (1- (point)))
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))
4163 )))))
4164
4165 \f
4166 ;; Replace state
4167
4168 (defun viper-change (beg end)
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))
4173
4174 (viper-set-complex-command-for-undo)
4175 (if viper-use-register
4176 (progn
4177 (copy-to-register viper-use-register beg end nil)
4178 (setq viper-use-register nil)))
4179 (viper-set-replace-overlay beg end)
4180 (setq last-command nil) ; separate repl text from prev kills
4181
4182 (if (= (viper-replace-start) (point-max))
4183 (error "End of buffer"))
4184
4185 (setq viper-last-replace-region
4186 (buffer-substring (viper-replace-start)
4187 (viper-replace-end)))
4188
4189 ;; protect against error while inserting "@" and other disasters
4190 ;; (e.g., read-only buff)
4191 (condition-case conds
4192 (if (or viper-allow-multiline-replace-regions
4193 (viper-same-line (viper-replace-start)
4194 (viper-replace-end)))
4195 (progn
4196 ;; tabs cause problems in replace, so untabify
4197 (goto-char (viper-replace-end))
4198 (insert-before-markers "@") ; put placeholder after the TAB
4199 (untabify (viper-replace-start) (point))
4200 ;; del @, don't put on kill ring
4201 (delete-backward-char 1)
4202
4203 (viper-set-replace-overlay-glyphs
4204 viper-replace-region-start-delimiter
4205 viper-replace-region-end-delimiter)
4206 ;; this move takes care of the last posn in the overlay, which
4207 ;; has to be shifted because of insert. We can't simply insert
4208 ;; "$" before-markers because then overlay-start will shift the
4209 ;; beginning of the overlay in case we are replacing a single
4210 ;; character. This fixes the bug with `s' and `cl' commands.
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))
4218 (error ;; make sure that the overlay doesn't stay.
4219 ;; go back to the original point
4220 (goto-char (viper-replace-start))
4221 (viper-hide-replace-overlay)
4222 (viper-message-conditions conds))))
4223
4224
4225 (defun viper-change-subr (beg end)
4226 ;; beg is sometimes (mark t), which may be nil
4227 (or beg (setq beg end))
4228 (if viper-use-register
4229 (progn
4230 (copy-to-register viper-use-register beg end nil)
4231 (setq viper-use-register nil)))
4232 (kill-region beg end)
4233 (setq this-command 'viper-change)
4234 (viper-yank-last-insertion))
4235
4236 (defun viper-toggle-case (arg)
4237 "Toggle character case."
4238 (interactive "P")
4239 (let ((val (viper-p-val arg)) (c))
4240 (viper-set-destructive-command
4241 (list 'viper-toggle-case val nil nil nil nil))
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
4254 (defun viper-query-replace ()
4255 "Query replace.
4256 If a null string is suplied as the string to be replaced,
4257 the query replace mode will toggle between string replace
4258 and regexp replace."
4259 (interactive)
4260 (let (str)
4261 (setq str (viper-read-string-with-history
4262 (if viper-re-query-replace "Query replace regexp: "
4263 "Query replace: ")
4264 nil ; no initial
4265 'viper-replace1-history
4266 (car viper-replace1-history) ; default
4267 ))
4268 (if (string= str "")
4269 (progn
4270 (setq viper-re-query-replace (not viper-re-query-replace))
4271 (message "Query replace mode changed to %s"
4272 (if viper-re-query-replace "regexp replace"
4273 "string replace")))
4274 (if viper-re-query-replace
4275 (query-replace-regexp
4276 str
4277 (viper-read-string-with-history
4278 (format "Query replace regexp `%s' with: " str)
4279 nil ; no initial
4280 'viper-replace1-history
4281 (car viper-replace1-history) ; default
4282 ))
4283 (query-replace
4284 str
4285 (viper-read-string-with-history
4286 (format "Query replace `%s' with: " str)
4287 nil ; no initial
4288 'viper-replace1-history
4289 (car viper-replace1-history) ; default
4290 ))))))
4291
4292 \f
4293 ;; marking
4294
4295 (defun viper-mark-beginning-of-buffer ()
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
4303 (defun viper-mark-end-of-buffer ()
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
4311 (defun viper-mark-point ()
4312 "Set mark at point of buffer."
4313 (interactive)
4314 (let ((char (read-char)))
4315 (cond ((and (<= ?a char) (<= char ?z))
4316 (point-to-register (viper-int-to-char (1+ (- char ?a)))))
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))
4323 (t (error ""))
4324 )))
4325
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
4333 ;; jump there. This forgets the position, p1, and puts M1 back into mark.
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.
4337 (defun viper-cycle-through-mark-ring ()
4338 "Visit previous locations on the mark ring.
4339 One can use `` and '' to temporarily jump 1 step back."
4340 (let* ((sv-pt (point)))
4341 ;; if repeated `m,' command, pop the previously saved mark.
4342 ;; Prev saved mark is actually prev saved point. It is used if the
4343 ;; user types `` or '' and is discarded
4344 ;; from the mark ring by the next `m,' command.
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
4347 ;; point to be the mark. Current pt as mark is discarded by the next
4348 ;; m, command.
4349 (if (eq last-command 'viper-cycle-through-mark-ring)
4350 ()
4351 ;; save current mark if the first iteration
4352 (setq mark-ring (delete (viper-mark-marker) mark-ring))
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
4358 (setq mark-ring (delete (viper-mark-marker) mark-ring))
4359 (push-mark sv-pt t)
4360 (viper-deactivate-mark)
4361 (setq this-command 'viper-cycle-through-mark-ring)
4362 ))
4363
4364
4365 (defun viper-goto-mark (arg)
4366 "Go to mark."
4367 (interactive "P")
4368 (let ((char (read-char))
4369 (com (viper-getcom arg)))
4370 (viper-goto-mark-subr char com nil)))
4371
4372 (defun viper-goto-mark-and-skip-white (arg)
4373 "Go to mark and skip to first non-white character on line."
4374 (interactive "P")
4375 (let ((char (read-char))
4376 (com (viper-getCom arg)))
4377 (viper-goto-mark-subr char com t)))
4378
4379 (defun viper-goto-mark-subr (char com skip-white)
4380 (if (eobp)
4381 (if (bobp)
4382 (error "Empty buffer")
4383 (backward-char 1)))
4384 (cond ((viper-valid-register char '(letter))
4385 (let* ((buff (current-buffer))
4386 (reg (viper-int-to-char (1+ (- char ?a))))
4387 (text-marker (get-register reg)))
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))))
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))
4406 (push-mark viper-last-jump t)
4407 (push-mark nil t)) ; no msg
4408 (viper-register-to-point reg)
4409 (setq viper-last-jump (point-marker))
4410 (cond (skip-white
4411 (back-to-indentation)
4412 (setq viper-last-jump-ignore (point))))
4413 (if com
4414 (if (equal buff (current-buffer))
4415 (viper-execute-com (if skip-white
4416 'viper-goto-mark-and-skip-white
4417 'viper-goto-mark)
4418 nil com)
4419 (switch-to-buffer buff)
4420 (goto-char viper-com-point)
4421 (viper-change-state-to-vi)
4422 (error "")))))
4423 ((and (not skip-white) (viper= char ?`))
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))
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)
4431 (setq viper-last-jump (point-marker)
4432 viper-last-jump-ignore 0)
4433 (if com (viper-execute-com 'viper-goto-mark nil com)))
4434 ((and skip-white (viper= char ?'))
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))
4439 (if (= (point) (mark t)) (pop-mark))
4440 (exchange-point-and-mark)
4441 (setq viper-last-jump (point))
4442 (back-to-indentation)
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))))
4446
4447 (defun viper-insert-tab ()
4448 (interactive)
4449 (insert-tab))
4450
4451 (defun viper-exchange-point-and-mark ()
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.
4459 (defsubst viper-looking-back (str)
4460 (and (save-excursion (re-search-backward str nil t))
4461 (= (point) (match-end 0))))
4462
4463
4464 (defun viper-forward-indent ()
4465 "Indent forward -- `C-t' in Vi."
4466 (interactive)
4467 (setq viper-cted t)
4468 (indent-to (+ (current-column) viper-shift-width)))
4469
4470 (defun viper-backward-indent ()
4471 "Backtab, C-d in VI"
4472 (interactive)
4473 (if viper-cted
4474 (let ((p (point)) (c (current-column)) bol (indent t))
4475 (if (viper-looking-back "[0^]")
4476 (progn
4477 (if (eq ?^ (preceding-char))
4478 (setq viper-preserve-indent t))
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
4488 (indent-to (- c viper-shift-width)))
4489 (if (or (bolp) (viper-looking-back "[^ \t]"))
4490 (setq viper-cted nil)))))
4491
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
4505 (defun viper-autoindent ()
4506 "Auto Indentation, Vi-style."
4507 (interactive)
4508 (let ((col (current-indentation)))
4509 (if abbrev-mode (expand-abbrev))
4510 (if viper-preserve-indent
4511 (setq viper-preserve-indent nil)
4512 (setq viper-current-indent col))
4513 ;; don't leave whitespace lines around
4514 (if (memq last-command
4515 '(viper-autoindent
4516 viper-open-line viper-Open-line
4517 viper-replace-state-exit-cmd))
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")
4522 (viper-indent-line viper-current-indent)
4523 ))
4524
4525
4526 ;; Viewing registers
4527
4528 (defun viper-ket-function (arg)
4529 "Function called by \], the ket. View registers and call \]\]."
4530 (interactive "P")
4531 (let ((reg (read-char)))
4532 (cond ((viper-valid-register reg '(letter Letter))
4533 (view-register (downcase reg)))
4534 ((viper-valid-register reg '(digit))
4535 (let ((text (current-kill (- reg ?1) 'do-not-rotate)))
4536 (with-output-to-temp-buffer " *viper-info*"
4537 (princ (format "Register %c contains the string:\n" reg))
4538 (princ text))
4539 ))
4540 ((viper= ?\] reg)
4541 (viper-next-heading arg))
4542 (t (error
4543 viper-InvalidRegister reg)))))
4544
4545 (defun viper-brac-function (arg)
4546 "Function called by \[, the brac. View textmarkers and call \[\["
4547 (interactive "P")
4548 (let ((reg (read-char)))
4549 (cond ((viper= ?\[ reg)
4550 (viper-prev-heading arg))
4551 ((viper= ?\] reg)
4552 (viper-heading-end arg))
4553 ((viper-valid-register reg '(letter))
4554 (let* ((val (get-register (viper-int-to-char (1+ (- reg ?a)))))
4555 (buf (if (not (markerp val))
4556 (error viper-EmptyTextmarker reg)
4557 (marker-buffer val)))
4558 (pos (marker-position val))
4559 line-no text (s pos) (e pos))
4560 (with-output-to-temp-buffer " *viper-info*"
4561 (if (and buf pos)
4562 (progn
4563 (save-excursion
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))
4579 (setq text (format "%s<%c>%s"
4580 (substring text 0 (- pos s))
4581 reg (substring text (- pos s)))))
4582 (princ
4583 (format
4584 "Textmarker `%c' is in buffer `%s' at line %d.\n"
4585 reg (buffer-name buf) line-no))
4586 (princ (format "Here is some text around %c:\n\n %s"
4587 reg text)))
4588 (princ (format viper-EmptyTextmarker reg))))
4589 ))
4590 (t (error viper-InvalidTextmarker reg)))))
4591
4592
4593
4594 (defun viper-delete-backward-word (arg)
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
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
4614
4615 (defun viper-set-expert-level (&optional dont-change-unless)
4616 "Sets the expert level for a Viper user.
4617 Can be called interactively to change (temporarily or permanently) the
4618 current expert level.
4619
4620 The optional argument DONT-CHANGE-UNLESS, if not nil, says that
4621 the level should not be changed, unless its current value is
4622 meaningless (i.e., not one of 1,2,3,4,5).
4623
4624 User level determines the setting of Viper variables that are most
4625 sensitive for VI-style look-and-feel."
4626
4627 (interactive)
4628
4629 (if (not (natnump viper-expert-level)) (setq viper-expert-level 0))
4630
4631 (save-window-excursion
4632 (delete-other-windows)
4633 ;; if 0 < viper-expert-level < viper-max-expert-level
4634 ;; & dont-change-unless = t -- use it; else ask
4635 (viper-ask-level dont-change-unless))
4636
4637 (setq viper-always t
4638 viper-ex-style-motion t
4639 viper-ex-style-editing t
4640 viper-want-ctl-h-help nil)
4641
4642 (cond ((eq viper-expert-level 1) ; novice or beginner
4643 (global-set-key ; in emacs-state
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))
4653
4654 ((and (> viper-expert-level 1) (< viper-expert-level 5))
4655 ;; intermediate to guru
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
4664 (progn
4665 (setq-default
4666 viper-ex-style-editing
4667 (viper-standard-value 'viper-ex-style-editing)
4668 viper-ex-style-motion
4669 (viper-standard-value 'viper-ex-style-motion))
4670 (setq viper-ex-style-motion
4671 (viper-standard-value 'viper-ex-style-motion)
4672 viper-ex-style-editing
4673 (viper-standard-value 'viper-ex-style-editing)
4674 viper-re-search
4675 (viper-standard-value 'viper-re-search)
4676 viper-no-multiple-ESC
4677 (viper-standard-value 'viper-no-multiple-ESC)))))
4678
4679 ;; A wizard!!
4680 ;; Ideally, if 5 is selected, a buffer should pop up to let the
4681 ;; user toggle the values of variables.
4682 (t (setq-default viper-ex-style-editing
4683 (viper-standard-value 'viper-ex-style-editing)
4684 viper-ex-style-motion
4685 (viper-standard-value 'viper-ex-style-motion))
4686 (setq viper-want-ctl-h-help
4687 (viper-standard-value 'viper-want-ctl-h-help)
4688 viper-always
4689 (viper-standard-value 'viper-always)
4690 viper-no-multiple-ESC
4691 (viper-standard-value 'viper-no-multiple-ESC)
4692 viper-ex-style-motion
4693 (viper-standard-value 'viper-ex-style-motion)
4694 viper-ex-style-editing
4695 (viper-standard-value 'viper-ex-style-editing)
4696 viper-re-search
4697 (viper-standard-value 'viper-re-search)
4698 viper-electric-mode
4699 (viper-standard-value 'viper-electric-mode)
4700 viper-want-emacs-keys-in-vi
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))))
4704
4705 (viper-set-mode-vars-for viper-current-state)
4706 (if (or viper-always
4707 (and (> viper-expert-level 0) (> 5 viper-expert-level)))
4708 (viper-set-hooks)))
4709
4710
4711 ;; Ask user expert level.
4712 (defun viper-ask-level (dont-change-unless)
4713 (let ((ask-buffer " *viper-ask-level*")
4714 level-changed repeated)
4715 (save-window-excursion
4716 (switch-to-buffer ask-buffer)
4717
4718 (while (or (> viper-expert-level viper-max-expert-level)
4719 (< viper-expert-level 1)
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 "
4730 Please specify your level of familiarity with the venomous VI PERil
4731 (and the VI Plan for Emacs Rescue).
4732 You can change it at any time by typing `M-x viper-set-expert-level RET'
4733
4734 1 -- BEGINNER: Almost all Emacs features are suppressed.
4735 Feels almost like straight Vi. File name completion and
4736 command history in the minibuffer are thrown in as a bonus.
4737 To use Emacs productively, you must reach level 3 or higher.
4738 2 -- MASTER: C-c now has its standard Emacs meaning in Vi command state,
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!
4741 3 -- GRAND MASTER: Like 2, but most Emacs commands are available also
4742 in Viper's insert state.
4743 4 -- GURU: Like 3, but user settings are respected for viper-no-multiple-ESC,
4744 viper-ex-style-motion, viper-ex-style-editing, and
4745 viper-re-search variables. Adjust these settings to your taste.
4746 5 -- WIZARD: Like 4, but user settings are also respected for viper-always,
4747 viper-electric-mode, viper-want-ctl-h-help, viper-want-emacs-keys-in-vi,
4748 and viper-want-emacs-keys-in-insert. Adjust these to your taste.
4749
4750 Please, specify your level now: ")
4751
4752 (setq viper-expert-level (- (viper-read-char-exclusive) ?0))
4753 ) ; end while
4754
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"
4760 viper-expert-level))
4761 (if (y-or-n-p "Do you wish to make this change permanent? ")
4762 ;; save the setting for viper-expert-level
4763 (viper-save-setting
4764 'viper-expert-level
4765 (format "Saving user level %d ..." viper-expert-level)
4766 viper-custom-file-name))
4767 ))
4768 (bury-buffer) ; remove ask-buffer from screen
4769 (message "")
4770 )))
4771
4772
4773 (defun viper-nil ()
4774 (interactive)
4775 (beep 1))
4776
4777
4778 ;; if ENFORCE-BUFFER is not nil, error if CHAR is a marker in another buffer
4779 (defun viper-register-to-point (char &optional enforce-buffer)
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))
4789 ((viper-valid-marker val)
4790 (if (and enforce-buffer
4791 (not (equal (current-buffer) (marker-buffer val))))
4792 (error (concat viper-EmptyTextmarker " in this buffer")
4793 (viper-int-to-char (1- (+ char ?a)))))
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
4799 (error viper-EmptyTextmarker (viper-int-to-char (1- (+ char ?a))))))))
4800
4801
4802 (defun viper-save-kill-buffer ()
4803 "Save then kill current buffer."
4804 (interactive)
4805 (if (< viper-expert-level 2)
4806 (save-buffers-kill-emacs)
4807 (save-buffer)
4808 (kill-buffer (current-buffer))))
4809
4810
4811 \f
4812 ;;; Bug Report
4813
4814 (defun viper-submit-report ()
4815 "Submit bug report on Viper."
4816 (interactive)
4817 (let ((reporter-prompt-for-summary-p t)
4818 (viper-device-type (viper-device-type))
4819 color-display-p frame-parameters
4820 minibuffer-emacs-face minibuffer-vi-face minibuffer-insert-face
4821 varlist salutation window-config)
4822
4823 ;; If mode info is needed, add variable to `let' and then set it below,
4824 ;; like we did with color-display-p.
4825 (setq color-display-p (if (viper-window-display-p)
4826 (viper-color-display-p)
4827 'non-x)
4828 minibuffer-vi-face (if (viper-has-face-support-p)
4829 (viper-get-face viper-minibuffer-vi-face)
4830 'non-x)
4831 minibuffer-insert-face (if (viper-has-face-support-p)
4832 (viper-get-face
4833 viper-minibuffer-insert-face)
4834 'non-x)
4835 minibuffer-emacs-face (if (viper-has-face-support-p)
4836 (viper-get-face
4837 viper-minibuffer-emacs-face)
4838 'non-x)
4839 frame-parameters (if (fboundp 'frame-parameters)
4840 (frame-parameters (selected-frame))))
4841
4842 (setq varlist (list 'viper-vi-minibuffer-minor-mode
4843 'viper-insert-minibuffer-minor-mode
4844 'viper-vi-intercept-minor-mode
4845 'viper-vi-local-user-minor-mode
4846 'viper-vi-kbd-minor-mode
4847 'viper-vi-global-user-minor-mode
4848 'viper-vi-state-modifier-minor-mode
4849 'viper-vi-diehard-minor-mode
4850 'viper-vi-basic-minor-mode
4851 'viper-replace-minor-mode
4852 'viper-insert-intercept-minor-mode
4853 'viper-insert-local-user-minor-mode
4854 'viper-insert-kbd-minor-mode
4855 'viper-insert-global-user-minor-mode
4856 'viper-insert-state-modifier-minor-mode
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
4862 'viper-emacs-global-user-minor-mode
4863 'viper-emacs-state-modifier-minor-mode
4864 'viper-automatic-iso-accents
4865 'viper-special-input-method
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
4873 'viper-ex-style-editing
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
4880 'ex-cycle-other-window
4881 'ex-cycle-through-non-files
4882 'viper-expert-level
4883 'major-mode
4884 'viper-device-type
4885 'color-display-p
4886 'frame-parameters
4887 'minibuffer-vi-face
4888 'minibuffer-insert-face
4889 'minibuffer-emacs-face
4890 ))
4891 (setq salutation "
4892 Congratulations! You may have unearthed a bug in Viper!
4893 Please mail a concise, accurate summary of the problem to the address above.
4894
4895 -------------------------------------------------------------------")
4896 (setq window-config (current-window-configuration))
4897 (with-output-to-temp-buffer " *viper-info*"
4898 (switch-to-buffer " *viper-info*")
4899 (delete-other-windows)
4900 (princ "
4901 PLEASE FOLLOW THESE PROCEDURES
4902 ------------------------------
4903
4904 Before reporting a bug, please verify that it is related to Viper, and is
4905 not cause by other packages you are using.
4906
4907 Don't report compilation warnings, unless you are certain that there is a
4908 problem. These warnings are normal and unavoidable.
4909
4910 Please note that users should not modify variables and keymaps other than
4911 those advertised in the manual. Such `customization' is likely to crash
4912 Viper, as it would any other improperly customized Emacs package.
4913
4914 If you are reporting an error message received while executing one of the
4915 Viper commands, type:
4916
4917 M-x set-variable <Return> debug-on-error <Return> t <Return>
4918
4919 Then reproduce the error. The above command will cause Emacs to produce a
4920 back trace of the execution that leads to the error. Please include this
4921 trace in your bug report.
4922
4923 If 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>
4927
4928 Then reproduce the problem. Wait for a few seconds, then type C-g to abort
4929 the current command. Include the resulting back trace in the bug report.
4930
4931 Mail 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)
4939
4940 (reporter-submit-bug-report "kifer@cs.stonybrook.edu"
4941 (viper-version)
4942 varlist
4943 nil 'delete-other-windows
4944 salutation)
4945 ))
4946
4947
4948
4949
4950 ;;; arch-tag: 739a6450-5fda-44d0-88b0-325053d888c2
4951 ;;; viper-cmd.el ends here