root/trunk/lisp/emulation/viper-cmd.el

Revision 4220, 172.9 kB (checked in by miyoshi, 9 months ago)

Sync up with Emacs22.2.

  • Property svn:eol-style set to LF
Line 
1 ;;; viper-cmd.el --- Vi command support for Viper
2
3 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 ;;   2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
6 ;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 3, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 ;; Boston, MA 02110-1301, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 (provide 'viper-cmd)
30 (require 'advice)
31
32 ;; Compiler pacifier
33 (defvar viper-minibuffer-current-face)
34 (defvar viper-minibuffer-insert-face)
35 (defvar viper-minibuffer-vi-face)
36 (defvar viper-minibuffer-emacs-face)
37 (defvar viper-always)
38 (defvar viper-mode-string)
39 (defvar viper-custom-file-name)
40 (defvar viper--key-maps)
41 (defvar viper--intercept-key-maps)
42 (defvar iso-accents-mode)
43 (defvar quail-mode)
44 (defvar quail-current-str)
45 (defvar zmacs-region-stays)
46 (defvar mark-even-if-inactive)
47 (defvar init-message)
48 (defvar initial)
49 (defvar undo-beg-posn)
50 (defvar undo-end-posn)
51
52 ;; loading happens only in non-interactive compilation
53 ;; in order to spare non-viperized emacs from being viperized
54 (if noninteractive
55     (eval-when-compile
56       (let ((load-path (cons (expand-file-name ".") load-path)))
57         (or (featurep 'viper-util)
58             (load "viper-util.el" nil nil 'nosuffix))
59         (or (featurep 'viper-keym)
60             (load "viper-keym.el" nil nil 'nosuffix))
61         (or (featurep 'viper-mous)
62             (load "viper-mous.el" nil nil 'nosuffix))
63         (or (featurep 'viper-macs)
64             (load "viper-macs.el" nil nil 'nosuffix))
65         (or (featurep 'viper-ex)
66             (load "viper-ex.el" nil nil 'nosuffix))
67         )))
68 ;; end pacifier
69
70
71 (require 'viper-util)
72 (require 'viper-keym)
73 (require 'viper-mous)
74 (require 'viper-macs)
75 (require 'viper-ex)
76
77
78
79 ;; Generic predicates
80
81 ;; These test functions are shamelessly lifted from vip 4.4.2 by Aamod Sane
82
83 ;; generate test functions
84 ;; given symbol foo, foo-p is the test function, foos is the set of
85 ;; Viper command keys
86 ;; (macroexpand '(viper-test-com-defun foo))
87 ;; (defun foo-p (com) (consp (memq com foos)))
88
89 (defmacro viper-test-com-defun (name)
90   (let* ((snm (symbol-name name))
91          (nm-p (intern (concat snm "-p")))
92          (nms (intern (concat snm "s"))))
93     `(defun ,nm-p (com)
94        (consp (viper-memq-char com ,nms)
95               ))))
96
97 ;; Variables for defining VI commands
98
99 ;; Modifying commands that can be prefixes to movement commands
100 (defvar viper-prefix-commands '(?c ?d ?y ?! ?= ?# ?< ?> ?\"))
101 ;; define viper-prefix-command-p
102 (viper-test-com-defun viper-prefix-command)
103
104 ;; Commands that are pairs eg. dd. r and R here are a hack
105 (defconst viper-charpair-commands '(?c ?d ?y ?! ?= ?< ?> ?r ?R))
106 ;; define viper-charpair-command-p
107 (viper-test-com-defun viper-charpair-command)
108
109 (defconst viper-movement-commands '(?b ?B ?e ?E ?f ?F ?G ?h ?H ?j ?k ?l
110                                      ?H ?M ?L ?n ?t ?T ?w ?W ?$ ?%
111                                      ?^ ?( ?) ?- ?+ ?| ?{ ?} ?[ ?] ?' ?`
112                                      ?\; ?, ?0 ?? ?/ ?\  ?\C-m
113                                      space return
114                                      delete backspace
115                                      )
116                                      "Movement commands")
117 ;; define viper-movement-command-p
118 (viper-test-com-defun viper-movement-command)
119
120 ;; Vi digit commands
121 (defconst viper-digit-commands '(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9))
122
123 ;; define viper-digit-command-p
124 (viper-test-com-defun viper-digit-command)
125
126 ;; Commands that can be repeated by . (dotted)
127 (defconst viper-dotable-commands '(?c ?d ?C ?s ?S ?D ?> ?<))
128 ;; define viper-dotable-command-p
129 (viper-test-com-defun viper-dotable-command)
130
131 ;; Commands that can follow a #
132 (defconst viper-hash-commands '(?c ?C ?g ?q ?s))
133 ;; define viper-hash-command-p
134 (viper-test-com-defun viper-hash-command)
135
136 ;; Commands that may have registers as prefix
137 (defconst viper-regsuffix-commands '(?d ?y ?Y ?D ?p ?P ?x ?X))
138 ;; define viper-regsuffix-command-p
139 (viper-test-com-defun viper-regsuffix-command)
140
141 (defconst viper-vi-commands (append viper-movement-commands
142                                   viper-digit-commands
143                                   viper-dotable-commands
144                                   viper-charpair-commands
145                                   viper-hash-commands
146                                   viper-prefix-commands
147                                   viper-regsuffix-commands)
148   "The list of all commands in Vi-state.")
149 ;; define viper-vi-command-p
150 (viper-test-com-defun viper-vi-command)
151
152 ;; Where viper saves mark. This mark is resurrected by m^
153 (defvar viper-saved-mark nil)
154
155 ;; Contains user settings for vars affected by viper-set-expert-level function.
156 ;; Not a user option.
157 (defvar viper-saved-user-settings nil)
158
159
160
161 ;;; CODE
162
163 ;; sentinels
164
165 ;; Runs viper-after-change-functions inside after-change-functions
166 (defun viper-after-change-sentinel (beg end len)
167   (run-hook-with-args 'viper-after-change-functions beg end len))
168
169 ;; Runs viper-before-change-functions inside before-change-functions
170 (defun viper-before-change-sentinel (beg end)
171   (run-hook-with-args 'viper-before-change-functions beg end))
172
173 (defsubst viper-post-command-sentinel ()
174   (condition-case conds
175       (run-hooks 'viper-post-command-hooks)
176     (error (viper-message-conditions conds)))
177   (if (eq viper-current-state 'vi-state)
178       (viper-restore-cursor-color 'after-insert-mode)))
179
180 (defsubst viper-pre-command-sentinel ()
181   (run-hooks 'viper-pre-command-hooks))
182
183 ;; Needed so that Viper will be able to figure the last inserted
184 ;; chunk of text with reasonable accuracy.
185 (defsubst viper-insert-state-post-command-sentinel ()
186   (if (and (memq viper-current-state '(insert-state replace-state))
187            viper-insert-point
188            (>= (point) viper-insert-point))
189       (setq viper-last-posn-while-in-insert-state (point-marker)))
190   (or (viper-overlay-p viper-replace-overlay)
191       (progn
192         (viper-set-replace-overlay (point-min) (point-min))
193         (viper-hide-replace-overlay)))
194   (if (eq viper-current-state 'insert-state)
195       (let ((has-saved-cursor-color-in-insert-mode
196              (stringp (viper-get-saved-cursor-color-in-insert-mode))))
197         (or has-saved-cursor-color-in-insert-mode
198             (string= (viper-get-cursor-color) viper-insert-state-cursor-color)
199             ;; save current color, if not already saved
200             (viper-save-cursor-color 'before-insert-mode))
201         ;; set insert mode cursor color
202         (viper-change-cursor-color viper-insert-state-cursor-color)))
203   (if (and viper-emacs-state-cursor-color (eq viper-current-state 'emacs-state))
204       (let ((has-saved-cursor-color-in-emacs-mode
205              (stringp (viper-get-saved-cursor-color-in-emacs-mode))))
206         (or has-saved-cursor-color-in-emacs-mode
207             (string= (viper-get-cursor-color) viper-emacs-state-cursor-color)
208             ;; save current color, if not already saved
209             (viper-save-cursor-color 'before-emacs-mode))
210         ;; set emacs mode cursor color
211         (viper-change-cursor-color viper-emacs-state-cursor-color)))
212
213   (if (and (memq this-command '(dabbrev-expand hippie-expand))
214            (integerp viper-pre-command-point)
215            (markerp viper-insert-point)
216            (marker-position viper-insert-point)
217            (> viper-insert-point viper-pre-command-point))
218       (viper-move-marker-locally viper-insert-point viper-pre-command-point))
219   )
220
221 (defsubst viper-preserve-cursor-color ()
222   (or (memq this-command '(self-insert-command
223                            viper-del-backward-char-in-insert
224                            viper-del-backward-char-in-replace
225                            viper-delete-backward-char
226                            viper-join-lines
227                            viper-delete-char))
228       (memq (viper-event-key last-command-event)
229             '(up down left right (meta f) (meta b)
230                  (control n) (control p) (control f) (control b)))))
231
232 (defsubst viper-insert-state-pre-command-sentinel ()
233   (or (viper-preserve-cursor-color)
234       (viper-restore-cursor-color 'after-insert-mode))
235   (if (and (memq this-command '(dabbrev-expand hippie-expand))
236            (markerp viper-insert-point)
237            (marker-position viper-insert-point))
238       (setq viper-pre-command-point (marker-position viper-insert-point))))
239
240 (defsubst viper-R-state-post-command-sentinel ()
241   ;; Restoring cursor color is needed despite
242   ;; viper-replace-state-pre-command-sentinel: When you jump to another buffer
243   ;; in another frame, the pre-command hook won't change cursor color to
244   ;; default in that other frame.  So, if the second frame cursor was red and
245   ;; we set the point outside the replacement region, then the cursor color
246   ;; will remain red.  Restoring the default, below, prevents this.
247   (if (and (<= (viper-replace-start) (point))
248            (<=  (point) (viper-replace-end)))
249       (viper-change-cursor-color viper-replace-overlay-cursor-color)
250     (viper-restore-cursor-color 'after-replace-mode)
251     ))
252
253 ;; to speed up, don't change cursor color before self-insert
254 ;; and common move commands
255 (defsubst viper-replace-state-pre-command-sentinel ()
256   (or (viper-preserve-cursor-color)
257       (viper-restore-cursor-color 'after-replace-mode)))
258
259
260 ;; Make sure we don't delete more than needed.
261 ;; This is executed at viper-last-posn-in-replace-region
262 (defsubst viper-trim-replace-chars-to-delete-if-necessary ()
263   (setq viper-replace-chars-to-delete
264         (max 0
265              (min viper-replace-chars-to-delete
266                   ;; Don't delete more than to the end of repl overlay
267                   (viper-chars-in-region
268                    (viper-replace-end) viper-last-posn-in-replace-region)
269                   ;; point is viper-last-posn-in-replace-region now
270                   ;; So, this limits deletion to the end of line
271                   (viper-chars-in-region (point) (viper-line-pos 'end))
272                   ))))
273
274
275 (defun viper-replace-state-post-command-sentinel ()
276   ;; Restoring cursor color is needed despite
277   ;; viper-replace-state-pre-command-sentinel: When one jumps to another buffer
278   ;; in another frame, the pre-command hook won't change cursor color to
279   ;; default in that other frame.  So, if the second frame cursor was red and
280   ;; we set the point outside the replacement region, then the cursor color
281   ;; will remain red.  Restoring the default, below, fixes this problem.
282   ;;
283   ;; We optimize for some commands, like self-insert-command,
284   ;; viper-delete-backward-char, etc., since they either don't change
285   ;; cursor color or, if they terminate replace mode, the color will be changed
286   ;; in viper-finish-change
287   (or (viper-preserve-cursor-color)
288       (viper-restore-cursor-color 'after-replace-mode))
289   (cond
290    ((eq viper-current-state 'replace-state)
291     ;; delete characters to compensate for inserted chars.
292     (let ((replace-boundary (viper-replace-end)))
293       (save-excursion
294         (goto-char viper-last-posn-in-replace-region)
295         (viper-trim-replace-chars-to-delete-if-necessary)
296         (delete-char viper-replace-chars-to-delete)
297         (setq viper-replace-chars-to-delete 0)
298         ;; terminate replace mode if reached replace limit
299         (if (= viper-last-posn-in-replace-region (viper-replace-end))
300             (viper-finish-change)))
301
302       (if (viper-pos-within-region
303            (point) (viper-replace-start) replace-boundary)
304           (progn
305             ;; the state may have changed in viper-finish-change above
306             (if (eq viper-current-state 'replace-state)
307                 (viper-change-cursor-color viper-replace-overlay-cursor-color))
308             (setq viper-last-posn-in-replace-region (point-marker))))
309       ))
310    ;; terminate replace mode if changed Viper states.
311    (t (viper-finish-change))))
312
313
314 ;; changing mode
315
316 ;; Change state to NEW-STATE---either emacs-state, vi-state, or insert-state.
317 (defun viper-change-state (new-state)
318   ;; Keep viper-post/pre-command-hooks fresh.
319   ;; We remove then add viper-post/pre-command-sentinel since it is very
320   ;; desirable that viper-pre-command-sentinel is the last hook and
321   ;; viper-post-command-sentinel is the first hook.
322
323   (viper-cond-compile-for-xemacs-or-emacs
324    ;; xemacs
325    (progn
326      (make-local-hook 'viper-after-change-functions)
327      (make-local-hook 'viper-before-change-functions)
328      (make-local-hook 'viper-post-command-hooks)
329      (make-local-hook 'viper-pre-command-hooks))
330    nil ; emacs
331    )
332
333   (remove-hook 'post-command-hook 'viper-post-command-sentinel)
334   (add-hook 'post-command-hook 'viper-post-command-sentinel)
335   (remove-hook 'pre-command-hook 'viper-pre-command-sentinel)
336   (add-hook 'pre-command-hook 'viper-pre-command-sentinel t)
337   ;; These hooks will be added back if switching to insert/replace mode
338   (remove-hook 'viper-post-command-hooks
339                'viper-insert-state-post-command-sentinel 'local)
340   (remove-hook 'viper-pre-command-hooks
341                'viper-insert-state-pre-command-sentinel 'local)
342   (setq viper-intermediate-command nil)
343   (cond ((eq new-state 'vi-state)
344          (cond ((member viper-current-state '(insert-state replace-state))
345
346                 ;; move viper-last-posn-while-in-insert-state
347                 ;; This is a normal hook that is executed in insert/replace
348                 ;; states after each command.  In Vi/Emacs state, it does
349                 ;; nothing.  We need to execute it here to make sure that
350                 ;; the last posn was recorded when we hit ESC.
351                 ;; It may be left unrecorded if the last thing done in
352                 ;; insert/repl state was dabbrev-expansion or abbrev
353                 ;; expansion caused by hitting ESC
354                 (viper-insert-state-post-command-sentinel)
355
356                 (condition-case conds
357                     (progn
358                       (viper-save-last-insertion
359                        viper-insert-point
360                        viper-last-posn-while-in-insert-state)
361                       (if viper-began-as-replace
362                           (setq viper-began-as-replace nil)
363                         ;; repeat insert commands if numerical arg > 1
364                         (save-excursion
365                           (viper-repeat-insert-command))))
366                   (error
367                    (viper-message-conditions conds)))
368
369                 (if (> (length viper-last-insertion) 0)
370                     (viper-push-onto-ring viper-last-insertion
371                                           'viper-insertion-ring))
372
373                 (if viper-ESC-moves-cursor-back
374                     (or (bolp) (viper-beginning-of-field) (backward-char 1))))
375                ))
376
377         ;; insert or replace
378         ((memq new-state '(insert-state replace-state))
379          (if (memq viper-current-state '(emacs-state vi-state))
380              (viper-move-marker-locally 'viper-insert-point (point)))
381          (viper-move-marker-locally
382           'viper-last-posn-while-in-insert-state (point))
383          (add-hook 'viper-post-command-hooks
384                    'viper-insert-state-post-command-sentinel t 'local)
385          (add-hook 'viper-pre-command-hooks
386                    'viper-insert-state-pre-command-sentinel t 'local))
387         ) ; outermost cond
388
389   ;; Nothing needs to be done to switch to emacs mode! Just set some
390   ;; variables, which is already done in viper-change-state-to-emacs!
391
392   ;; ISO accents
393   ;; always turn off iso-accents-mode in vi-state, or else we won't be able to
394   ;; use the keys `,',^ , as they will do accents instead of Vi actions.
395   (cond ((eq new-state 'vi-state) (viper-set-iso-accents-mode nil));accents off
396         (viper-automatic-iso-accents (viper-set-iso-accents-mode t));accents on
397         (t (viper-set-iso-accents-mode nil)))
398   ;; Always turn off quail mode in vi state
399   (cond ((eq new-state 'vi-state) (viper-set-input-method nil)) ;intl input off
400         (viper-special-input-method (viper-set-input-method t)) ;intl input on
401         (t (viper-set-input-method nil)))
402
403   (setq viper-current-state new-state)
404
405   (viper-update-syntax-classes)
406   (viper-normalize-minor-mode-map-alist)
407   (viper-adjust-keys-for new-state)
408   (viper-set-mode-vars-for new-state)
409   (viper-refresh-mode-line)
410   )
411
412
413 (defun viper-adjust-keys-for (state)
414   "Make necessary adjustments to keymaps before entering STATE."
415   (cond ((memq state '(insert-state replace-state))
416          (if viper-auto-indent
417              (progn
418                (define-key viper-insert-basic-map "\C-m" 'viper-autoindent)
419                (if viper-want-emacs-keys-in-insert
420                    ;; expert
421                    (define-key viper-insert-basic-map "\C-j" nil)
422                  ;; novice
423                  (define-key viper-insert-basic-map "\C-j" 'viper-autoindent)))
424            (define-key viper-insert-basic-map "\C-m" nil)
425            (define-key viper-insert-basic-map "\C-j" nil))
426
427          (setq viper-insert-diehard-minor-mode
428                (not viper-want-emacs-keys-in-insert))
429
430          (if viper-want-ctl-h-help
431              (progn
432                (define-key viper-insert-basic-map "\C-h" 'help-command)
433                (define-key viper-replace-map "\C-h" 'help-command))
434            (define-key viper-insert-basic-map
435              "\C-h" 'viper-del-backward-char-in-insert)
436            (define-key viper-replace-map
437              "\C-h" 'viper-del-backward-char-in-replace))
438          ;; In XEmacs, C-h overrides backspace, so we make sure it doesn't.
439          (define-key viper-insert-basic-map
440            [backspace] 'viper-del-backward-char-in-insert)
441          (define-key viper-replace-map
442            [backspace] 'viper-del-backward-char-in-replace)
443          ) ; end insert/replace case
444         (t ; Vi state
445          (setq viper-vi-diehard-minor-mode (not viper-want-emacs-keys-in-vi))
446          (if viper-want-ctl-h-help
447              (define-key viper-vi-basic-map "\C-h" 'help-command)
448            (define-key viper-vi-basic-map "\C-h" 'viper-backward-char))
449          ;; In XEmacs, C-h overrides backspace, so we make sure it doesn't.
450          (define-key viper-vi-basic-map [backspace] 'viper-backward-char))
451         ))
452
453
454 ;; Normalizes minor-mode-map-alist by putting Viper keymaps first.
455 ;; This ensures that Viper bindings are in effect, regardless of which minor
456 ;; modes were turned on by the user or by other packages.
457 (defun viper-normalize-minor-mode-map-alist ()
458   (setq viper--intercept-key-maps
459         (list
460          (cons 'viper-vi-intercept-minor-mode viper-vi-intercept-map)
461          (cons 'viper-insert-intercept-minor-mode viper-insert-intercept-map)
462          (cons 'viper-emacs-intercept-minor-mode viper-emacs-intercept-map)
463          ))
464   (setq viper--key-maps
465         (list (cons 'viper-vi-minibuffer-minor-mode viper-minibuffer-map)
466               (cons 'viper-vi-local-user-minor-mode viper-vi-local-user-map)
467               (cons 'viper-vi-kbd-minor-mode viper-vi-kbd-map)
468               (cons 'viper-vi-global-user-minor-mode viper-vi-global-user-map)
469               (cons 'viper-vi-state-modifier-minor-mode
470                     (if (keymapp
471                          (cdr (assoc major-mode viper-vi-state-modifier-alist)))
472                         (cdr (assoc major-mode viper-vi-state-modifier-alist))
473                       viper-empty-keymap))
474               (cons 'viper-vi-diehard-minor-mode  viper-vi-diehard-map)
475               (cons 'viper-vi-basic-minor-mode     viper-vi-basic-map)
476                (cons 'viper-replace-minor-mode  viper-replace-map)
477                ;; viper-insert-minibuffer-minor-mode must come after
478                ;; viper-replace-minor-mode
479                (cons 'viper-insert-minibuffer-minor-mode
480                      viper-minibuffer-map)
481                (cons 'viper-insert-local-user-minor-mode
482                      viper-insert-local-user-map)
483                (cons 'viper-insert-kbd-minor-mode viper-insert-kbd-map)
484                (cons 'viper-insert-global-user-minor-mode
485                      viper-insert-global-user-map)
486                (cons 'viper-insert-state-modifier-minor-mode
487                      (if (keymapp
488                           (cdr (assoc major-mode
489                                       viper-insert-state-modifier-alist)))
490                          (cdr (assoc major-mode
491                                      viper-insert-state-modifier-alist))
492                        viper-empty-keymap))
493                (cons 'viper-insert-diehard-minor-mode viper-insert-diehard-map)
494                (cons 'viper-insert-basic-minor-mode viper-insert-basic-map)
495                (cons 'viper-emacs-local-user-minor-mode
496                      viper-emacs-local-user-map)
497                (cons 'viper-emacs-kbd-minor-mode viper-emacs-kbd-map)
498                (cons 'viper-emacs-global-user-minor-mode
499                      viper-emacs-global-user-map)
500                (cons 'viper-emacs-state-modifier-minor-mode
501                      (if (keymapp
502                           (cdr
503                            (assoc major-mode viper-emacs-state-modifier-alist)))
504                          (cdr
505                           (assoc major-mode viper-emacs-state-modifier-alist))
506                        viper-empty-keymap))
507                ))
508         
509   ;; This var is not local in Emacs, so we make it local.  It must be local
510   ;; because although the stack of minor modes can be the same for all buffers,
511   ;; the associated *keymaps* can be different.  In Viper,
512   ;; viper-vi-local-user-map, viper-insert-local-user-map, and others can have
513   ;; different keymaps for different buffers.  Also, the keymaps associated
514   ;; with viper-vi/insert-state-modifier-minor-mode can be different.
515   ;; ***This is needed only in case emulation-mode-map-alists is not defined.
516   ;; In emacs with emulation-mode-map-alists, nothing needs to be done
517   (unless
518       (and (fboundp 'add-to-ordered-list) (boundp 'emulation-mode-map-alists))
519     (set (make-local-variable 'minor-mode-map-alist)
520          (viper-append-filter-alist
521           (append viper--intercept-key-maps viper--key-maps)
522           minor-mode-map-alist)))
523   )
524
525
526
527 ;; Viper mode-changing commands and utilities
528
529 ;; Modifies mode-line-buffer-identification.
530 (defun viper-refresh-mode-line ()
531   (set (make-local-variable 'viper-mode-string)
532         (cond ((eq viper-current-state 'emacs-state) viper-emacs-state-id)
533               ((eq viper-current-state 'vi-state) viper-vi-state-id)
534               ((eq viper-current-state 'replace-state) viper-replace-state-id)
535               ((eq viper-current-state 'insert-state) viper-insert-state-id)))
536
537   ;; Sets Viper mode string in global-mode-string
538   (force-mode-line-update))
539
540
541 ;; Switch from Insert state to Vi state.
542 (defun viper-exit-insert-state ()
543   (interactive)
544   (viper-change-state-to-vi))
545
546 (defun viper-set-mode-vars-for (state)
547   "Sets Viper minor mode variables to put Viper's state STATE in effect."
548
549   ;; Emacs state
550   (setq viper-vi-minibuffer-minor-mode       nil
551         viper-insert-minibuffer-minor-mode   nil
552         viper-vi-intercept-minor-mode        nil
553         viper-insert-intercept-minor-mode    nil
554
555         viper-vi-local-user-minor-mode       nil
556         viper-vi-kbd-minor-mode              nil
557         viper-vi-global-user-minor-mode      nil
558         viper-vi-state-modifier-minor-mode   nil
559         viper-vi-diehard-minor-mode          nil
560         viper-vi-basic-minor-mode            nil
561
562         viper-replace-minor-mode               nil
563
564         viper-insert-local-user-minor-mode     nil
565         viper-insert-kbd-minor-mode            nil
566         viper-insert-global-user-minor-mode    nil
567         viper-insert-state-modifier-minor-mode nil
568         viper-insert-diehard-minor-mode        nil
569         viper-insert-basic-minor-mode          nil
570         viper-emacs-intercept-minor-mode       t
571         viper-emacs-local-user-minor-mode      t
572         viper-emacs-kbd-minor-mode             (not (viper-is-in-minibuffer))
573         viper-emacs-global-user-minor-mode     t
574         viper-emacs-state-modifier-minor-mode  t
575         )
576
577   ;; Vi state
578   (if (eq state 'vi-state) ; adjust for vi-state
579       (setq
580        viper-vi-intercept-minor-mode       t
581        viper-vi-minibuffer-minor-mode      (viper-is-in-minibuffer)
582        viper-vi-local-user-minor-mode      t
583        viper-vi-kbd-minor-mode             (not (viper-is-in-minibuffer))
584        viper-vi-global-user-minor-mode     t
585        viper-vi-state-modifier-minor-mode    t
586        ;; don't let the diehard keymap block command completion
587        ;; and other things in the minibuffer
588        viper-vi-diehard-minor-mode         (not
589                                             (or viper-want-emacs-keys-in-vi
590                                                 (viper-is-in-minibuffer)))
591        viper-vi-basic-minor-mode              t
592        viper-emacs-intercept-minor-mode       nil
593        viper-emacs-local-user-minor-mode      nil
594        viper-emacs-kbd-minor-mode             nil
595        viper-emacs-global-user-minor-mode     nil
596        viper-emacs-state-modifier-minor-mode  nil
597        ))
598
599   ;; Insert and Replace states
600   (if (member state '(insert-state replace-state))
601       (setq
602        viper-insert-intercept-minor-mode      t
603        viper-replace-minor-mode               (eq state 'replace-state)
604        viper-insert-minibuffer-minor-mode     (viper-is-in-minibuffer)
605        viper-insert-local-user-minor-mode     t
606        viper-insert-kbd-minor-mode            (not (viper-is-in-minibuffer))
607        viper-insert-global-user-minor-mode     t
608        viper-insert-state-modifier-minor-mode  t
609        ;; don't let the diehard keymap block command completion
610        ;; and other things in the minibuffer
611        viper-insert-diehard-minor-mode        (not
612                                                (or
613                                                 viper-want-emacs-keys-in-insert
614                                                 (viper-is-in-minibuffer)))
615        viper-insert-basic-minor-mode          t
616        viper-emacs-intercept-minor-mode       nil
617        viper-emacs-local-user-minor-mode      nil
618        viper-emacs-kbd-minor-mode             nil
619        viper-emacs-global-user-minor-mode     nil
620        viper-emacs-state-modifier-minor-mode  nil
621        ))
622
623   ;; minibuffer faces
624   (if (viper-has-face-support-p)
625       (setq viper-minibuffer-current-face
626             (cond ((eq state 'emacs-state) viper-minibuffer-emacs-face)
627                   ((eq state 'vi-state) viper-minibuffer-vi-face)
628                   ((memq state '(insert-state replace-state))
629                    viper-minibuffer-insert-face))))
630
631   (if (viper-is-in-minibuffer)
632       (viper-set-minibuffer-overlay))
633   )
634
635 ;; This also takes care of the annoying incomplete lines in files.
636 ;; Also, this fixes `undo' to work vi-style for complex commands.
637 (defun viper-change-state-to-vi ()
638   "Change Viper state to Vi."
639   (interactive)
640   (if (and viper-first-time (not (viper-is-in-minibuffer)))
641       (viper-mode)
642     (if overwrite-mode (overwrite-mode -1))
643     (or (viper-overlay-p viper-replace-overlay)
644       (viper-set-replace-overlay (point-min) (point-min)))
645     (viper-hide-replace-overlay)
646     (if abbrev-mode (expand-abbrev))
647     (if (and auto-fill-function (> (current-column) fill-column))
648         (funcall auto-fill-function))
649     ;; don't leave whitespace lines around
650     (if (and (memq last-command
651                    '(viper-autoindent
652                      viper-open-line viper-Open-line
653                      viper-replace-state-exit-cmd))
654              (viper-over-whitespace-line))
655         (indent-to-left-margin))
656     (viper-add-newline-at-eob-if-necessary)
657     (viper-adjust-undo)
658
659     (if (eq viper-current-state 'emacs-state)
660         (viper-restore-cursor-color 'after-emacs-mode)
661       (viper-restore-cursor-color 'after-insert-mode))
662
663     (viper-change-state 'vi-state)
664
665     ;; Protect against user errors in hooks
666     (condition-case conds
667         (run-hooks 'viper-vi-state-hook)
668       (error
669        (viper-message-conditions conds)))))
670
671 (defun viper-change-state-to-insert ()
672   "Change Viper state to Insert."
673   (interactive)
674   (viper-change-state 'insert-state)
675
676   (or (viper-overlay-p viper-replace-overlay)
677       (viper-set-replace-overlay (point-min) (point-min)))
678   (viper-hide-replace-overlay)
679
680   (let ((has-saved-cursor-color-in-insert-mode
681          (stringp (viper-get-saved-cursor-color-in-insert-mode))))
682     (or has-saved-cursor-color-in-insert-mode
683         (string= (viper-get-cursor-color) viper-insert-state-cursor-color)
684         (viper-save-cursor-color 'before-insert-mode))
685     (viper-change-cursor-color viper-insert-state-cursor-color))
686
687   ;; Protect against user errors in hooks
688   (condition-case conds
689       (run-hooks 'viper-insert-state-hook)
690     (error
691      (viper-message-conditions conds))))
692
693 (defsubst viper-downgrade-to-insert ()
694   ;; Protect against user errors in hooks
695   (condition-case conds
696       (run-hooks 'viper-insert-state-hook)
697     (error
698      (viper-message-conditions conds)))
699   (setq viper-current-state 'insert-state
700         viper-replace-minor-mode nil))
701
702
703
704 ;; Change to replace state.  When the end of replacement region is reached,
705 ;; replace state changes to insert state.
706 (defun viper-change-state-to-replace (&optional non-R-cmd)
707   (viper-change-state 'replace-state)
708   ;; Run insert-state-hook
709   (condition-case conds
710       (run-hooks 'viper-insert-state-hook 'viper-replace-state-hook)
711     (error
712      (viper-message-conditions conds)))
713
714   (if non-R-cmd
715       (viper-start-replace)
716     ;; 'R' is implemented using Emacs's overwrite-mode
717     (viper-start-R-mode))
718   )
719
720
721 (defun viper-change-state-to-emacs ()
722   "Change Viper state to Emacs."
723   (interactive)
724   (or (viper-overlay-p viper-replace-overlay)
725       (viper-set-replace-overlay (point-min) (point-min)))
726   (viper-hide-replace-overlay)
727
728   (if viper-emacs-state-cursor-color
729       (let ((has-saved-cursor-color-in-emacs-mode
730              (stringp (viper-get-saved-cursor-color-in-emacs-mode))))
731         (or has-saved-cursor-color-in-emacs-mode
732             (string= (viper-get-cursor-color) viper-emacs-state-cursor-color)
733             (viper-save-cursor-color 'before-emacs-mode))
734         (viper-change-cursor-color viper-emacs-state-cursor-color)))
735
736   (viper-change-state 'emacs-state)
737
738   ;; Protect against user errors in hooks
739   (condition-case conds
740       (run-hooks 'viper-emacs-state-hook)
741     (error
742      (viper-message-conditions conds))))
743
744 ;; escape to emacs mode termporarily
745 (defun viper-escape-to-emacs (arg &optional events)
746   "Escape to Emacs state from Vi state for one Emacs command.
747 ARG is used as the prefix value for the executed command.  If
748 EVENTS is a list of events, which become the beginning of the command."
749   (interactive "P")
750   (if (viper= last-command-char ?\\)
751       (message "Switched to EMACS state for the next command..."))
752   (viper-escape-to-state arg events 'emacs-state))
753
754 ;; escape to Vi mode termporarily
755 (defun viper-escape-to-vi (arg)
756   "Escape from Emacs state to Vi state for one Vi 1-character command.
757 If the Vi command that the user types has a prefix argument, e.g., `d2w', then
758 Vi's prefix argument will be used.  Otherwise, the prefix argument passed to
759 `viper-escape-to-vi' is used."
760   (interactive "P")</