root/trunk/lisp/emulation/viper-cmd.el
| Revision 4220, 172.9 kB (checked in by miyoshi, 9 months ago) | |
|---|---|
| |
| 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") |
