viper.el 48.2 KB
Newer Older
1
;;; viper.el --- A full-featured Vi emulator for Emacs and XEmacs,  -*-lexical-binding:t -*-
Karl Heuer's avatar
Karl Heuer committed
2 3 4 5
;;		 a VI Plan for Emacs Rescue,
;;		 and a venomous VI PERil.
;;		 Viper Is also a Package for Emacs Rebels.

Paul Eggert's avatar
Paul Eggert committed
6
;; Copyright (C) 1994-2019 Free Software Foundation, Inc.
7

8
;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
9
;; Keywords: emulations
10
;; Version: 3.14.1
11

12 13 14 15 16
;; Yoni Rabkin <yoni@rabkins.net> contacted the maintainer of this
;; file on 20/3/2008, and the maintainer agreed that when a bug is
;; filed in the Emacs bug reporting system against this file, a copy
;; of the bug report be sent to the maintainer's email address.

17
(defconst viper-version "3.14.2 of July 4, 2013"
Michael Kifer's avatar
Michael Kifer committed
18 19
  "The current version of Viper")

Karl Heuer's avatar
Karl Heuer committed
20 21
;; This file is part of GNU Emacs.

22
;; GNU Emacs is free software: you can redistribute it and/or modify
Karl Heuer's avatar
Karl Heuer committed
23
;; it under the terms of the GNU General Public License as published by
24 25
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Karl Heuer's avatar
Karl Heuer committed
26 27 28 29 30 31 32

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
33
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
Karl Heuer's avatar
Karl Heuer committed
34 35 36

;;; Commentary:

Michael Kifer's avatar
Michael Kifer committed
37
;; Viper is a full-featured Vi emulator for Emacs and XEmacs.  It emulates and
Karl Heuer's avatar
Karl Heuer committed
38 39 40
;; improves upon the standard features of Vi and, at the same time, allows
;; full access to all Emacs facilities.  Viper supports multiple undo,
;; file name completion, command, file, and search history and it extends
Michael Kifer's avatar
Michael Kifer committed
41
;; Vi in many other ways.  Viper is highly customizable through the various
Karl Heuer's avatar
Karl Heuer committed
42 43 44 45
;; hooks, user variables, and keymaps.  It is implemented as a collection
;; of minor modes and it is designed to provide full access to all Emacs
;; major and minor modes.
;;
46
;;; History:
Karl Heuer's avatar
Karl Heuer committed
47 48 49 50
;;
;; Viper is a new name for a package formerly known as VIP-19,
;; which was a successor of VIP version 3.5 by Masahiko Sato
;; <ms@sail.stanford.edu> and VIP version 4.2 by Aamod Sane
Michael Kifer's avatar
Michael Kifer committed
51
;; <sane@cs.uiuc.edu>.  Some ideas from vip 4.4.2 by Aamod Sane
Karl Heuer's avatar
Karl Heuer committed
52 53 54
;; were also shamelessly plagiarized.
;;
;; Viper maintains some degree of compatibility with these older
Michael Kifer's avatar
Michael Kifer committed
55
;; packages.  See the documentation for customization.
Karl Heuer's avatar
Karl Heuer committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
;;
;; The main difference between Viper and these older packages are:
;;
;; 1. Viper emulates Vi at several levels, from almost complete conformity
;;    to a rather loose Vi-compliance.
;;
;; 2. Viper provides full access to all major and minor modes of Emacs
;;    without the need to type extra keys.
;;    The older versions of VIP (and other Vi emulators) do not work with
;;    some major and minor modes.
;;
;; 3. Viper supports vi-style undo.
;;
;; 4. Viper fully emulates (and improves upon) vi's replacement mode.
;;
;; 5. Viper has a better interface to ex, including command, variable, and
;;    file name completion.
;;
;; 6. Viper uses native Emacs history and completion features; it doesn't
;;    rely on other packages (such as gmhist.el and completer.el) to provide
;;    these features.
;;
;; 7. Viper supports Vi-style editing in the minibuffer, by allowing the
;;    user to switch from Insert state to Vi state to Replace state, etc.
;;
;; 8. Viper keeps history of recently inserted pieces of text and recently
;;    executed Vi-style destructive commands, such as `i', `d', etc.
;;    These pieces of text can be inserted in later insertion commands;
;;    the previous destructive commands can be re-executed.
;;
;; 9. Viper has Vi-style keyboard macros, which enhances the similar
;;    facility in the original Vi.
;;    First, one can execute any Emacs command while defining a
Michael Kifer's avatar
Michael Kifer committed
89
;;    macro, not just the Vi commands.  Second, macros are defined in a
90
;;    WYSYWYG mode, using an interface to Emacs's WYSIWYG style of defining
Michael Kifer's avatar
Michael Kifer committed
91
;;    macros.  Third, in Viper, one can define macros that are specific to
Karl Heuer's avatar
Karl Heuer committed
92 93 94 95
;;    a given buffer, a given major mode, or macros defined for all buffers.
;;    The same macro name can have several different definitions:
;;    one global, several definitions for various major modes, and
;;    definitions for specific buffers.
Karl Heuer's avatar
Karl Heuer committed
96
;;    Buffer-specific definitions override mode-specific
Karl Heuer's avatar
Karl Heuer committed
97 98 99 100 101 102 103 104 105
;;    definitions, which, in turn, override global definitions.
;;
;;
;;; Installation:
;;  -------------
;;
;;  (require 'viper)
;;

106 107
;;; Acknowledgments:
;;  ----------------
Karl Heuer's avatar
Karl Heuer committed
108 109
;;  Bug reports and ideas contributed by many users have helped
;;  improve Viper and the various versions of VIP.
110
;;  See the manual for a complete list of contributors.
Karl Heuer's avatar
Karl Heuer committed
111 112 113 114 115 116
;;
;;
;;; Notes:
;;
;; 1. Major modes.
;; In most cases, Viper handles major modes correctly, i.e., they come up
Michael Kifer's avatar
Michael Kifer committed
117
;; in the right state (either  vi-state or emacs-state).  For instance, text
Karl Heuer's avatar
Karl Heuer committed
118
;; files come up in vi-state, while, say, Dired appears in emacs-state by
119
;; default.
Karl Heuer's avatar
Karl Heuer committed
120 121
;; However, some modes do not appear in the right mode in the beginning,
;; usually because they neglect to follow Emacs conventions (e.g., they don't
Michael Kifer's avatar
Michael Kifer committed
122
;; use kill-all-local-variables when they start).  Some major modes
Karl Heuer's avatar
Karl Heuer committed
123
;; may fail to come up in emacs-state if they call hooks, such as
124 125
;; text-hook, for no good reason.
;;
Karl Heuer's avatar
Karl Heuer committed
126 127
;; As an immediate solution, you can hit C-z to bring about the right mode.
;; An interim solution is to add an appropriate hook to the mode like this:
128
;;
129
;;     (add-hook 'your-favorite-mode #'viper-mode)
130
;; or
131
;;     (add-hook 'your-favorite-mode #'viper-change-state-to-emacs)
132
;;
Michael Kifer's avatar
Michael Kifer committed
133
;; whichever applies.  The right thing to do, however, is to complain to the
Karl Heuer's avatar
Karl Heuer committed
134 135
;; author of the respective package. (Sometimes they also neglect to equip
;; their  modes with hooks, which is one more reason for complaining.)
136
;;
Karl Heuer's avatar
Karl Heuer committed
137
;; 2. Keymap handling
Michael Kifer's avatar
Michael Kifer committed
138 139
;;    Each Viper state (insert, vi, replace) is implemented as a collection of
;;    several minor modes, each with its own keymap.
Karl Heuer's avatar
Karl Heuer committed
140 141 142
;;
;; Viper's  Vi state consists of seven minor modes:
;;
Michael Kifer's avatar
Michael Kifer committed
143 144 145 146 147 148 149
;;  viper-vi-intercept-minor-mode
;;  viper-vi-local-user-minor-mode
;;  viper-vi-global-user-minor-mode
;;  viper-vi-kbd-minor-mode
;;  viper-vi-state-modifier-minor-mode
;;  viper-vi-diehard-minor-mode
;;  viper-vi-basic-minor-mode
Karl Heuer's avatar
Karl Heuer committed
150 151 152 153
;;
;;  Bindings done to the keymap of the first mode overshadow those done to
;;  the second, which, in turn, overshadows those done to the third, etc.
;;
Michael Kifer's avatar
Michael Kifer committed
154
;;  The last viper-vi-basic-minor-mode contains most of the usual Vi bindings
Michael Kifer's avatar
Michael Kifer committed
155
;;  in its edit mode.  This mode provides access to all Emacs facilities.
156 157 158
;;  Novice users, however, may want to set their viper-expert-level to 1 in
;;  their viper-custom-file-name.  This will enable viper-vi-diehard-minor-mode.
;;  This minor mode's bindings make Viper simulate the usual Vi very closely.
Karl Heuer's avatar
Karl Heuer committed
159 160 161
;;  For instance,  C-c will not have its standard Emacs binding
;;  and so many of the goodies of Emacs are not available.
;;
Michael Kifer's avatar
Michael Kifer committed
162
;;  A skilled user should set viper-expert-level to at least 3.  This will
Karl Heuer's avatar
Karl Heuer committed
163
;;  enable `C-c' and many Emacs facilities will become available.
Michael Kifer's avatar
Michael Kifer committed
164
;;  In this case, viper-vi-diehard-minor-mode is inactive.
Karl Heuer's avatar
Karl Heuer committed
165 166
;;
;;  Viper gurus should have at least
Michael Kifer's avatar
Michael Kifer committed
167
;;      (setq viper-expert-level 4)
168 169
;;  in their viper-custom-file-name.  This will unsuppress all Emacs keys
;;  that are not essential for VI-style editing.
Karl Heuer's avatar
Karl Heuer committed
170
;;  Pick-and-choose users may want to put
Michael Kifer's avatar
Michael Kifer committed
171
;;      (setq viper-expert-level 5)
172 173
;;  in viper-custom-file-name.  Viper will then leave it up to the user to
;;  set the variables viper-want-*  See viper-set-expert-level for details.
Karl Heuer's avatar
Karl Heuer committed
174
;;
Michael Kifer's avatar
Michael Kifer committed
175
;;  The very first minor mode, viper-vi-intercept-minor-mode, is of no
Michael Kifer's avatar
Michael Kifer committed
176
;;  concern for the user.  It is needed to bind Viper's vital keys, such as
Karl Heuer's avatar
Karl Heuer committed
177 178
;;  ESC and C-z.
;;
Michael Kifer's avatar
Michael Kifer committed
179
;;  The second mode,  viper-vi-local-user-minor-mode, usually has an
Michael Kifer's avatar
Michael Kifer committed
180
;;  empty keymap.  However, the user can set bindings in this keymap, which
Karl Heuer's avatar
Karl Heuer committed
181
;;  will overshadow the corresponding bindings in the other two minor
Michael Kifer's avatar
Michael Kifer committed
182
;;  modes.  This is useful, for example, for setting up ZZ in gnus,
Karl Heuer's avatar
Karl Heuer committed
183 184 185
;;  rmail, mh-e, etc., to send  message instead of saving it in a file.
;;  Likewise, in Dired mode, you may want to bind ZN and ZP to commands
;;  that would visit the next or the previous file in the Dired buffer.
Michael Kifer's avatar
Michael Kifer committed
186
;;  Setting local keys is tricky, so don't do it directly.  Instead, use
Michael Kifer's avatar
Michael Kifer committed
187
;;  viper-add-local-keys function (see its doc).
Karl Heuer's avatar
Karl Heuer committed
188
;;
Michael Kifer's avatar
Michael Kifer committed
189 190
;;  The third minor mode, viper-vi-global-user-minor-mode, is also intended
;;  for the users but, unlike viper-vi-local-user-minor-mode, its key
Michael Kifer's avatar
Michael Kifer committed
191
;;  bindings are seen in all Viper buffers.  This mode keys can be done
Karl Heuer's avatar
Karl Heuer committed
192 193
;;  with define-key command.
;;
Michael Kifer's avatar
Michael Kifer committed
194
;;  The fourth minor mode, viper-vi-kbd-minor-mode, is used by keyboard
Michael Kifer's avatar
Michael Kifer committed
195
;;  macros.  Users are NOT supposed to modify this keymap directly.
Karl Heuer's avatar
Karl Heuer committed
196
;;
Michael Kifer's avatar
Michael Kifer committed
197
;;  The fifth mode, viper-vi-state-modifier-minor-mode, can be used to set
Karl Heuer's avatar
Karl Heuer committed
198 199 200
;;  key bindings that are visible in some major modes but not in others.
;;
;;  Users are allowed to modify keymaps that belong to
Michael Kifer's avatar
Michael Kifer committed
201 202
;;  viper-vi-local-user-minor-mode, viper-vi-global-user-minor-mode,
;;  and viper-vi-state-modifier-minor-mode only.
Karl Heuer's avatar
Karl Heuer committed
203 204 205
;;
;;  Viper's Insert state also has seven minor modes:
;;
Michael Kifer's avatar
Michael Kifer committed
206 207 208 209 210 211 212
;;      viper-insert-intercept-minor-mode
;;  	viper-insert-local-user-minor-mode
;;  	viper-insert-global-user-minor-mode
;;  	viper-insert-kbd-minor-mode
;;      viper-insert-state-modifier-minor-mode
;;  	viper-insert-diehard-minor-mode
;;  	viper-insert-basic-minor-mode
Karl Heuer's avatar
Karl Heuer committed
213
;;
Michael Kifer's avatar
Michael Kifer committed
214 215 216
;;  As with VI's editing modes, the first mode,
;;  viper-insert-intercept-minor-mode is used to bind vital keys that are not
;;  to be changed by the user.
Karl Heuer's avatar
Karl Heuer committed
217
;;
Michael Kifer's avatar
Michael Kifer committed
218
;;  The next mode, viper-insert-local-user-minor-mode, is used to customize
Michael Kifer's avatar
Michael Kifer committed
219
;;  bindings in the insert state of Viper.  The third mode,
Michael Kifer's avatar
Michael Kifer committed
220 221
;;  viper-insert-global-user-minor-mode is like
;;  viper-insert-local-user-minor-mode, except that its bindings are seen in
Michael Kifer's avatar
Michael Kifer committed
222 223
;;  all Viper buffers.  As with viper-vi-local-user-minor-mode, its bindings
;;  should be done via the function viper-add-local-keys.  Bindings for
Michael Kifer's avatar
Michael Kifer committed
224
;;  viper-insert-global-user-minor-mode can be set with the define-key command.
Karl Heuer's avatar
Karl Heuer committed
225
;;
Michael Kifer's avatar
Michael Kifer committed
226
;;  The next minor mode, viper-insert-kbd-minor-mode,
227
;;  is used for keyboard VI-style macros defined with :map!.
Karl Heuer's avatar
Karl Heuer committed
228
;;
Michael Kifer's avatar
Michael Kifer committed
229 230
;;  The fifth minor mode, viper-insert-state-modifier-minor-mode, is like
;;  viper-vi-state-modifier-minor-mode, except that it is used in the Insert
231
;;  state; it can be used to modify keys in a mode-specific fashion.
Karl Heuer's avatar
Karl Heuer committed
232
;;
Michael Kifer's avatar
Michael Kifer committed
233
;;  The minor mode viper-insert-diehard-minor-mode is in effect when
Karl Heuer's avatar
Karl Heuer committed
234
;;  the user wants a high degree of Vi compatibility (a bad idea, really!).
Michael Kifer's avatar
Michael Kifer committed
235
;;  The last minor mode, viper-insert-basic-minor-mode, is always in effect
Michael Kifer's avatar
Michael Kifer committed
236
;;  when Viper is in insert state.  It binds a small number of keys needed for
237
;;  Viper's operation.
Karl Heuer's avatar
Karl Heuer committed
238 239 240 241
;;
;;  Finally, Viper provides minor modes for overriding bindings set by Emacs
;;  modes when Viper is in Emacs state:
;;
Michael Kifer's avatar
Michael Kifer committed
242 243 244 245
;; 	viper-emacs-local-user-minor-mode
;;  	viper-emacs-global-user-minor-mode
;;      viper-emacs-kbd-minor-mode
;;      viper-emacs-state-modifier-minor-mode
Karl Heuer's avatar
Karl Heuer committed
246
;;
Michael Kifer's avatar
Michael Kifer committed
247
;;  These minor modes are in effect when Viper is in Emacs state.  The keymap
Michael Kifer's avatar
Michael Kifer committed
248 249
;;  associated with viper-emacs-global-user-minor-mode,
;;  viper-emacs-global-user-map, overrides the global and local keymaps as
Michael Kifer's avatar
Michael Kifer committed
250
;;  well as the minor mode keymaps set by other modes.  The keymap of
Michael Kifer's avatar
Michael Kifer committed
251
;;  viper-emacs-local-user-minor-mode, viper-emacs-local-user-map, overrides
Karl Heuer's avatar
Karl Heuer committed
252
;;  everything, but it is used on a per buffer basis.
Michael Kifer's avatar
Michael Kifer committed
253
;;  The keymap associated with viper-emacs-state-modifier-minor-mode
Michael Kifer's avatar
Michael Kifer committed
254
;;  overrides keys on a per-major-mode basis.  The mode
Michael Kifer's avatar
Michael Kifer committed
255
;;  viper-emacs-kbd-minor-mode is used to define Vi-style macros in Emacs
Karl Heuer's avatar
Karl Heuer committed
256 257 258
;;  state.
;;
;;  3. There is also one minor mode that is used when Viper is in its
Michael Kifer's avatar
Michael Kifer committed
259
;;     replace-state (used for commands like cw, C, etc.).  This mode is
Karl Heuer's avatar
Karl Heuer committed
260 261
;;     called
;;
Michael Kifer's avatar
Michael Kifer committed
262
;;       viper-replace-minor-mode
Karl Heuer's avatar
Karl Heuer committed
263
;;
Michael Kifer's avatar
Michael Kifer committed
264
;;     and its keymap is viper-replace-map.  Replace minor mode is always
Karl Heuer's avatar
Karl Heuer committed
265 266 267
;;     used in conjunction with the minor modes for insert-state, and its
;;     keymap overshadows the keymaps for insert minor modes.
;;
268
;;  4. Defining buffer-local bindings in Vi and Insert modes.
Karl Heuer's avatar
Karl Heuer committed
269 270
;;  As mentioned before, sometimes, it is convenient to have
;;  buffer-specific of mode-specific key bindings in Vi and insert modes.
Michael Kifer's avatar
Michael Kifer committed
271
;;  Viper provides a special function, viper-add-local-keys, to do precisely
Michael Kifer's avatar
Michael Kifer committed
272
;;  this.  For instance, is you need to add couple of mode-specific bindings
273
;;  to Insert mode, you can put
Karl Heuer's avatar
Karl Heuer committed
274
;;
275
;;       (viper-add-local-keys 'insert-state '((key1 . func1) (key2 .func2)))
Karl Heuer's avatar
Karl Heuer committed
276
;;
Michael Kifer's avatar
Michael Kifer committed
277
;;  somewhere in a hook of this major mode.  If you put something like this
Karl Heuer's avatar
Karl Heuer committed
278
;;  in your own elisp function, this will define bindings specific to the
Michael Kifer's avatar
Michael Kifer committed
279
;;  buffer that was current at the time of the call to viper-add-local-keys.
Karl Heuer's avatar
Karl Heuer committed
280 281
;;  The only thing to make sure here is that the major mode of this buffer
;;  is written according to Emacs conventions, which includes a call to
Michael Kifer's avatar
Michael Kifer committed
282
;;  (kill-all-local-variables).  See viper-add-local-keys for more details.
Karl Heuer's avatar
Karl Heuer committed
283 284 285 286 287
;;
;;
;;  TO DO (volunteers?):
;;
;; 1. Some of the code that is inherited from VIP-3.5 is rather
Michael Kifer's avatar
Michael Kifer committed
288 289
;;    convoluted.  Instead of viper-command-argument, keymaps should bind the
;;    actual commands.  E.g., "dw" should be bound to a generic command
Michael Kifer's avatar
Michael Kifer committed
290
;;    viper-delete that will delete things based on the value of
291
;;    last-command-event.  This would greatly simplify the logic and the code.
Karl Heuer's avatar
Karl Heuer committed
292 293 294 295
;;
;; 2. Somebody should venture to write a customization package a la
;;    options.el that would allow the user to change values of variables
;;    that meet certain specs (e.g., match a regexp) and whose doc string
Michael Kifer's avatar
Michael Kifer committed
296 297
;;    starts with a '*'.  Then, the user should be offered to save
;;    variables that were changed.  This will make user's customization job
Karl Heuer's avatar
Karl Heuer committed
298 299 300
;;    much easier.
;;

301
;;; Code:
Karl Heuer's avatar
Karl Heuer committed
302

303 304
(require 'cl-lib)

Michael Kifer's avatar
Michael Kifer committed
305 306
;; compiler pacifier
(defvar mark-even-if-inactive)
Michael Kifer's avatar
Michael Kifer committed
307
(defvar quail-mode)
Michael Kifer's avatar
Michael Kifer committed
308
(defvar viper-expert-level)
Michael Kifer's avatar
Michael Kifer committed
309 310
(defvar viper-mode-string)
(defvar viper-major-mode-modifier-list)
Michael Kifer's avatar
Michael Kifer committed
311
;; end pacifier
Karl Heuer's avatar
Karl Heuer committed
312

Richard M. Stallman's avatar
Richard M. Stallman committed
313
(require 'viper-init)
314
(require 'viper-keym)
Richard M. Stallman's avatar
Richard M. Stallman committed
315

Michael Kifer's avatar
Michael Kifer committed
316
;; better be defined before Viper custom group.
Stefan Monnier's avatar
Stefan Monnier committed
317
(defvar viper-custom-file-name (locate-user-emacs-file "viper" ".viper")
318
  "Viper customization file.
Michael Kifer's avatar
Michael Kifer committed
319 320 321 322
If set by the user, this must be done _before_ Viper is loaded in `~/.emacs'.")

(defgroup viper nil
  "Vi emulation within Emacs.
323
NOTE: Viper customization should be saved in `viper-custom-file-name'."
Michael Kifer's avatar
Michael Kifer committed
324
  :prefix "viper-"
Michael Kifer's avatar
Michael Kifer committed
325 326
  :group 'emulations)

Michael Kifer's avatar
Michael Kifer committed
327
(require 'viper-cmd)
Karl Heuer's avatar
Karl Heuer committed
328

Michael Kifer's avatar
Michael Kifer committed
329 330 331 332 333 334
(defgroup viper-misc nil
  "Miscellaneous Viper customization."
  :prefix "viper-"
  :group 'viper)


Michael Kifer's avatar
Michael Kifer committed
335
(defcustom viper-always t
Michael Kifer's avatar
Michael Kifer committed
336 337 338
  "Non-nil means, arrange for vi-state to be a default when appropriate.
This is different from `viper-mode' variable in that `viper-mode' determines
whether to use Viper in the first place, while `viper-always', if nil, lets
Michael Kifer's avatar
Michael Kifer committed
339 340
user decide when to invoke Viper in a major mode."
  :type 'boolean
341
  :tag "Always Invoke Viper")
Michael Kifer's avatar
Michael Kifer committed
342 343 344 345

;; Non-viper variables that need to be saved in case the user decides to
;; de-viperize emacs.
(defvar viper-saved-non-viper-variables nil)
346

Michael Kifer's avatar
Michael Kifer committed
347 348 349
(defcustom viper-mode (cond (noninteractive nil)
			    (t 'ask))
  "To Viperize or not to Viperize.
350
If t, viperize Emacs.  If nil -- don't.  If `ask', ask the user.
351
This variable is used primarily when Viper is being loaded.
Karl Heuer's avatar
Karl Heuer committed
352

353
Must be set in your init file before Viper is loaded.
Michael Kifer's avatar
Michael Kifer committed
354 355
DO NOT set this variable interactively, unless you are using the customization
widget."
Michael Kifer's avatar
Michael Kifer committed
356
  :type '(choice (const nil) (const t) (const ask))
357
  :tag "Set Viper Mode on Loading")
Michael Kifer's avatar
Michael Kifer committed
358

Michael Kifer's avatar
Michael Kifer committed
359 360 361
(defcustom viper-vi-state-mode-list
  '(fundamental-mode
    makefile-mode
362

Michael Kifer's avatar
Michael Kifer committed
363 364
    awk-mode
    m4-mode
Michael Kifer's avatar
Michael Kifer committed
365 366 367 368
    xrdb-mode
    winmgr-mode
    autoconf-mode
    cvs-edit-mode
369

Michael Kifer's avatar
Michael Kifer committed
370 371
    html-mode html-helper-mode
    emacs-lisp-mode lisp-mode lisp-interaction-mode
372 373

    jde-mode java-mode
Michael Kifer's avatar
Michael Kifer committed
374
    cc-mode c-mode c++-mode objc-mode
Michael Kifer's avatar
Michael Kifer committed
375 376 377 378 379
    fortran-mode f90-mode
    basic-mode
    bat-mode
    asm-mode
    prolog-mode
Michael Kifer's avatar
Michael Kifer committed
380
    flora-mode
381
    sql-mode
Michael Kifer's avatar
Michael Kifer committed
382 383 384

    text-mode indented-text-mode
    tex-mode latex-mode bibtex-mode
385
    ps-mode
386

387
    ;; completion-list-mode
388
    diff-mode
389
    idl-mode
390

391 392
    perl-mode
    cperl-mode
Michael Kifer's avatar
Michael Kifer committed
393 394 395
    javascript-mode
    tcl-mode
    python-mode
396

Michael Kifer's avatar
Michael Kifer committed
397
    sh-mode ksh-mode csh-mode
398

Michael Kifer's avatar
Michael Kifer committed
399 400 401 402
    gnus-article-mode
    mh-show-mode
    )
  "Major modes that require Vi command state."
403
  :type '(repeat symbol))
Michael Kifer's avatar
Michael Kifer committed
404

Michael Kifer's avatar
Michael Kifer committed
405
(defcustom viper-emacs-state-mode-list
406
  '(Custom-mode
Michael Kifer's avatar
Michael Kifer committed
407 408 409 410

    dired-mode
    efs-mode
    tar-mode
411
    egg-status-buffer-mode
Michael Kifer's avatar
Michael Kifer committed
412

413 414
    browse-kill-ring-mode
    recentf-mode
415
    recentf-dialog-mode
416 417
    occur-mode

Michael Kifer's avatar
Michael Kifer committed
418 419 420
    mh-folder-mode
    gnus-group-mode
    gnus-summary-mode
421

422 423 424
    completion-list-mode
    help-mode

Michael Kifer's avatar
Michael Kifer committed
425 426
    Info-mode
    Buffer-menu-mode
Michael Kifer's avatar
Michael Kifer committed
427
    compilation-mode
428

429 430
    rcirc-mode

431 432
    jde-javadoc-checker-report-mode

Michael Kifer's avatar
Michael Kifer committed
433 434 435
    view-mode
    vm-mode
    vm-summary-mode)
436
  "A list of major modes that should come up in Emacs state.
Michael Kifer's avatar
Michael Kifer committed
437 438
Normally, Viper would bring buffers up in Emacs state, unless the corresponding
major mode has been placed on `viper-vi-state-mode-list' or
Michael Kifer's avatar
Michael Kifer committed
439 440
`viper-insert-state-mode-list'.  So, don't place a new mode on this list,
unless it is coming up in a wrong Viper state."
441
  :type '(repeat symbol))
Michael Kifer's avatar
Michael Kifer committed
442 443

(defcustom viper-insert-state-mode-list
444 445
  '(internal-ange-ftp-mode
    comint-mode
446
    gud-mode
447
    inferior-emacs-lisp-mode
448
    erc-mode
449 450
    eshell-mode
    shell-mode)
451
  "A list of major modes that should come up in Vi Insert state."
452
  :type '(repeat symbol))
Michael Kifer's avatar
Michael Kifer committed
453 454 455 456 457 458


;; used to set viper-major-mode-modifier-list in defcustom
(defun viper-apply-major-mode-modifiers (&optional symbol value)
  (if symbol
      (set symbol value))
459 460 461 462
  (mapc (lambda (triple)
          (viper-modify-major-mode
           (nth 0 triple) (nth 1 triple) (symbol-value (nth 2 triple))))
        viper-major-mode-modifier-list))
Michael Kifer's avatar
Michael Kifer committed
463

464
;; We change standard bindings in some major modes, making them slightly
465
;; different than in "normal" vi/insert/emacs states
Michael Kifer's avatar
Michael Kifer committed
466 467 468 469
(defcustom viper-major-mode-modifier-list
  '((help-mode emacs-state viper-slash-and-colon-map)
    (comint-mode insert-state viper-comint-mode-modifier-map)
    (comint-mode vi-state viper-comint-mode-modifier-map)
470
    (gud-mode insert-state viper-comint-mode-modifier-map)
Michael Kifer's avatar
Michael Kifer committed
471
    (shell-mode insert-state viper-comint-mode-modifier-map)
472
    (inferior-emacs-lisp-mode insert-state viper-comint-mode-modifier-map)
Michael Kifer's avatar
Michael Kifer committed
473 474 475 476 477 478 479 480
    (shell-mode vi-state viper-comint-mode-modifier-map)
    (ange-ftp-shell-mode insert-state viper-comint-mode-modifier-map)
    (ange-ftp-shell-mode vi-state viper-comint-mode-modifier-map)
    (internal-ange-ftp-mode insert-state viper-comint-mode-modifier-map)
    (internal-ange-ftp-mode vi-state viper-comint-mode-modifier-map)
    (dired-mode emacs-state viper-dired-modifier-map)
    (tar-mode emacs-state viper-slash-and-colon-map)
    (mh-folder-mode emacs-state viper-slash-and-colon-map)
481 482
    (gnus-group-mode emacs-state viper-gnus-modifier-map)
    (gnus-summary-mode emacs-state viper-gnus-modifier-map)
Michael Kifer's avatar
Michael Kifer committed
483 484
    (Info-mode emacs-state viper-slash-and-colon-map)
    (Buffer-menu-mode emacs-state viper-slash-and-colon-map)
485 486
    (erc-mode insert-state viper-comint-mode-modifier-map)
    (erc-mode vi-state viper-comint-mode-modifier-map)
Michael Kifer's avatar
Michael Kifer committed
487 488 489
    )
  "List specifying how to modify the various major modes to enable some Viperisms.
The list has the structure: ((mode viper-state keymap) (mode viper-state
Paul Eggert's avatar
Paul Eggert committed
490
keymap) ...).  If `mode' is on the list, the `keymap' will be made active (on
Michael Kifer's avatar
Michael Kifer committed
491
the minor-mode-map-alist) in the specified viper state.
492 493
If you change this list, have to restart Emacs for the change to take effect.
However, if you did the change through the customization widget, then Emacs
Michael Kifer's avatar
Michael Kifer committed
494
needs to be restarted only if you deleted a triple mode-state-keymap from the
495
list.  No need to restart Emacs in case of insertion or modification of an
Michael Kifer's avatar
Michael Kifer committed
496 497 498 499 500 501 502
existing triple."
  :type '(repeat
	  (list symbol
		(choice (const emacs-state)
			(const vi-state)
			(const insert-state))
		symbol))
503
  :set #'viper-apply-major-mode-modifiers)
Michael Kifer's avatar
Michael Kifer committed
504

Michael Kifer's avatar
Michael Kifer committed
505

Michael Kifer's avatar
Michael Kifer committed
506

Michael Kifer's avatar
Michael Kifer committed
507
;;;###autoload
Michael Kifer's avatar
Michael Kifer committed
508
(defun toggle-viper-mode ()
Michael Kifer's avatar
Michael Kifer committed
509
  "Toggle Viper on/off.
Michael Kifer's avatar
Michael Kifer committed
510
If Viper is enabled, turn it off.  Otherwise, turn it on."
Michael Kifer's avatar
Michael Kifer committed
511 512 513 514 515
  (interactive)
  (if (eq viper-mode t)
      (viper-go-away)
    (setq viper-mode nil)
    (viper-mode)))
Karl Heuer's avatar
Karl Heuer committed
516

Michael Kifer's avatar
Michael Kifer committed
517 518
;;;###autoload
(defun viper-mode ()
519
  "Turn on Viper emulation of Vi in Emacs. See Info node `(viper)Top'."
Michael Kifer's avatar
Michael Kifer committed
520 521 522 523 524 525
  (interactive)
  (if (not noninteractive)
      (progn
	;; if the user requested viper-mode explicitly
	(if viper-mode
	    ()
Michael Kifer's avatar
Michael Kifer committed
526
	  (setq viper-mode t)
527
          ;; FIXME: Don't reload!
Michael Kifer's avatar
Michael Kifer committed
528 529
	  (load-library "viper"))

530
	(if viper-first-time ; Important check.  Prevents mix-up of startup
Michael Kifer's avatar
Michael Kifer committed
531
	    (progn	     ; and expert-level msgs when viper-mode recurses
Michael Kifer's avatar
Michael Kifer committed
532 533
	      (setq viper-first-time nil)
	      (if (not viper-inhibit-startup-message)
Michael Kifer's avatar
Michael Kifer committed
534
		  (save-window-excursion
Michael Kifer's avatar
Michael Kifer committed
535
		    (setq viper-inhibit-startup-message t)
Michael Kifer's avatar
Michael Kifer committed
536 537 538 539 540
		    (delete-other-windows)
		    (switch-to-buffer "Viper Startup Message")
		    (erase-buffer)
		    (insert
		     (substitute-command-keys
Michael Kifer's avatar
Michael Kifer committed
541 542
		      "Viper Is a Package for Emacs Rebels,
a VI Plan for Emacs Rescue, and a venomous VI PERil.
Michael Kifer's avatar
Michael Kifer committed
543

544
Incidentally, Viper emulates Vi under Emacs/XEmacs 20.
Michael Kifer's avatar
Michael Kifer committed
545
It supports all of what is good in Vi and Ex, while extending
Michael Kifer's avatar
Michael Kifer committed
546 547
and improving upon much of it.

Michael Kifer's avatar
Michael Kifer committed
548
   1. Viper supports Vi at several levels.  Level 1 is the closest to Vi,
Michael Kifer's avatar
Michael Kifer committed
549
      level 5 provides the most flexibility to depart from many Vi conventions.
550

Michael Kifer's avatar
Michael Kifer committed
551
      You will be asked to specify your user level in a following screen.
552

Michael Kifer's avatar
Michael Kifer committed
553
      If you select user level 1 then the keys ^X, ^C, ^Z, and ^G will behave
Michael Kifer's avatar
Michael Kifer committed
554
      as in VI, to smooth transition to Viper for the beginners.  However, to
555 556
      use Emacs productively, you are advised to reach user level 3 or higher.

Michael Kifer's avatar
Michael Kifer committed
557
      At user level 2 or higher, ^X and ^C have Emacs, not Vi, bindings;
558
      ^Z toggles Vi/Emacs states; ^G is Emacs's keyboard-quit (like ^C in Vi).
559

Michael Kifer's avatar
Michael Kifer committed
560
   2. Vi exit functions (e.g., :wq, ZZ) work on INDIVIDUAL files -- they
Michael Kifer's avatar
Michael Kifer committed
561
      do not cause Emacs to quit, except at user level 1 (for a novice).
Michael Kifer's avatar
Michael Kifer committed
562
   3. ^X^C EXITS EMACS.
Michael Kifer's avatar
Michael Kifer committed
563 564
   4. Viper supports multiple undo: `u' will undo.  Typing `.' will repeat
      undo.  Another `u' changes direction.
565

Michael Kifer's avatar
Michael Kifer committed
566 567
   6. Emacs Meta key is `C-\\' (in all modes) or `\\ ESC' (in Vi command mode).
      On a window system, the best way is to use the Meta-key on your keyboard.
Michael Kifer's avatar
Michael Kifer committed
568
   7. Try \\[keyboard-quit] and \\[abort-recursive-edit] repeatedly,if
569 570
      something funny happens.  This would abort the current editing command.

Michael Kifer's avatar
Michael Kifer committed
571
For more information on Viper:
Michael Kifer's avatar
Michael Kifer committed
572

Michael Kifer's avatar
Michael Kifer committed
573 574 575 576 577
   a. Type `:help' in Vi command mode
   b. Print Viper manual, found in ./etc/viper.dvi
   c. Print the Quick Reference, found in ./etc/viperCard.dvi

To submit a bug report or to contact the author, type :submitReport in Vi
Michael Kifer's avatar
Michael Kifer committed
578
command mode.  To shoo Viper away and return to pure Emacs (horror!), type:
Michael Kifer's avatar
Michael Kifer committed
579 580

   M-x viper-go-away
581

Michael Kifer's avatar
Michael Kifer committed
582 583 584 585
This startup message appears whenever you load Viper, unless you type `y' now."
		      ))
		    (goto-char (point-min))
		    (if (y-or-n-p "Inhibit Viper startup message? ")
Michael Kifer's avatar
Michael Kifer committed
586 587
			(viper-save-setting
			 'viper-inhibit-startup-message
Michael Kifer's avatar
Michael Kifer committed
588
			 "Viper startup message inhibited"
Michael Kifer's avatar
Michael Kifer committed
589
			 viper-custom-file-name t))
Michael Kifer's avatar
Michael Kifer committed
590 591 592 593 594
		    ;;(kill-buffer (current-buffer))
		    (message
		     "The last message is in buffer `Viper Startup Message'")
		    (sit-for 4)
		    ))
Michael Kifer's avatar
Michael Kifer committed
595
	      (viper-set-expert-level 'dont-change-unless)))
Michael Kifer's avatar
Michael Kifer committed
596

597 598
	(or (apply #'derived-mode-p viper-emacs-state-mode-list) ; don't switch to Vi
	    (apply #'derived-mode-p viper-insert-state-mode-list) ; don't switch
599
	    (viper-change-state-to-vi))
600 601 602 603 604
	))

  (if (eq major-mode 'viper-mode)
      (setq major-mode 'fundamental-mode))
  )
605

606 607 608 609

;; Apply a little heuristic to invoke vi state on major-modes
;; that are not listed in viper-vi-state-mode-list
(defun this-major-mode-requires-vi-state (mode)
610 611 612 613
  (let ((major-mode mode))
    (cond ((apply #'derived-mode-p viper-vi-state-mode-list) t)
          ((apply #'derived-mode-p viper-emacs-state-mode-list) nil)
          ((apply #'derived-mode-p viper-insert-state-mode-list) nil)
614 615
          (t (and (eq (key-binding "a") 'self-insert-command)
                  (eq (key-binding " ") 'self-insert-command))))))
616

Karl Heuer's avatar
Karl Heuer committed
617

618
;; This hook designed to enable Vi-style editing in comint-based modes."
Michael Kifer's avatar
Michael Kifer committed
619
(defun viper-comint-mode-hook ()
620 621
  (set (make-local-variable 'require-final-newline) nil)
  (setq viper-ex-style-editing nil
Michael Kifer's avatar
Michael Kifer committed
622 623
	viper-ex-style-motion nil)
  (viper-change-state-to-insert))
Michael Kifer's avatar
Michael Kifer committed
624

Karl Heuer's avatar
Karl Heuer committed
625

Michael Kifer's avatar
Michael Kifer committed
626 627 628
;; remove viper hooks from SYMBOL
(defun viper-remove-hooks (symbol)
  (cond ((not (boundp symbol)) nil)
629
	((not (listp (symbol-value symbol))) nil)
Michael Kifer's avatar
Michael Kifer committed
630
	((string-match "-hook" (symbol-name symbol))
631 632 633 634 635 636 637 638 639
	 (remove-hook symbol #'viper-mode)
	 (remove-hook symbol #'viper-change-state-to-emacs)
	 (remove-hook symbol #'viper-change-state-to-insert)
	 (remove-hook symbol #'viper-change-state-to-vi)
	 (remove-hook symbol #'viper-minibuffer-post-command-hook)
	 (remove-hook symbol #'viper-minibuffer-setup-sentinel)
	 (remove-hook symbol #'viper-major-mode-change-sentinel)
	 (remove-hook symbol #'set-viper-state-in-major-mode)
	 (remove-hook symbol #'viper-post-command-sentinel)
Michael Kifer's avatar
Michael Kifer committed
640 641 642 643 644
	 )))

;; Remove local value in all existing buffers
;; This doesn't delocalize vars (which would have been desirable)
(defun viper-delocalize-var (symbol)
645 646 647
  (dolist (buf (buffer-list))
    (with-current-buffer buf
      (kill-local-variable symbol))))
Michael Kifer's avatar
Michael Kifer committed
648

649 650 651 652
(defvar viper--advice-list nil)

(defun viper--advice-add (function where advice)
  (advice-add function where advice)
Phillip Lord's avatar
Phillip Lord committed
653
  (push (cons function advice) viper--advice-list))
654 655

(defun viper--deactivate-advice-list ()
Phillip Lord's avatar
Phillip Lord committed
656 657 658 659 660
  (mapc (lambda (n)
          (advice-remove
           (car n)
           (cdr n)))
        viper--advice-list)
661
  (setq viper--advice-list nil))
Michael Kifer's avatar
Michael Kifer committed
662 663 664

(defun viper-go-away ()
  "De-Viperize Emacs.
Michael Kifer's avatar
Michael Kifer committed
665 666
This function tries to do as good a job as possible.  However, it may undo some
user customization, unrelated to Viper.  For instance, if the user advised
Michael Kifer's avatar
Michael Kifer committed
667 668
`read-file-name', `describe-key', and some others, then this advice will be
undone.
669
It also can't undo some Viper settings."
Michael Kifer's avatar
Michael Kifer committed
670
  (interactive)
671
  (viper-setup-ESC-to-escape nil)
Michael Kifer's avatar
Michael Kifer committed
672 673 674
  ;; restore non-viper vars
  (setq-default
   next-line-add-newlines
675 676
   (viper-standard-value
    'next-line-add-newlines viper-saved-non-viper-variables)
Michael Kifer's avatar
Michael Kifer committed
677
   require-final-newline
678 679
   (viper-standard-value
    'require-final-newline viper-saved-non-viper-variables)
Michael Kifer's avatar
Michael Kifer committed
680
   scroll-step
681
   (viper-standard-value 'scroll-step viper-saved-non-viper-variables)
Michael Kifer's avatar
Michael Kifer committed
682 683
   mode-line-buffer-identification
   (viper-standard-value
Michael Kifer's avatar
Michael Kifer committed
684 685
    'mode-line-buffer-identification viper-saved-non-viper-variables)
   global-mode-string
Michael Kifer's avatar
Michael Kifer committed
686
   (delq 'viper-mode-string global-mode-string))
Michael Kifer's avatar
Michael Kifer committed
687

688
  (setq-default major-mode
689
                (viper-standard-value 'major-mode
690
                                      viper-saved-non-viper-variables))
691

692
  (if (featurep 'emacs)
Michael Kifer's avatar
Michael Kifer committed
693 694 695 696
      (setq-default
       mark-even-if-inactive
       (viper-standard-value
	'mark-even-if-inactive viper-saved-non-viper-variables)))
Michael Kifer's avatar
Michael Kifer committed
697

698
  ;; Ideally, we would like to be able to de-localize local variables
699 700 701
  (unless
      (and (fboundp 'add-to-ordered-list) (boundp 'emulation-mode-map-alists))
    (viper-delocalize-var 'minor-mode-map-alist))
Michael Kifer's avatar
Michael Kifer committed
702
  (viper-delocalize-var 'require-final-newline)
703

Michael Kifer's avatar
Michael Kifer committed
704
  ;; deactivate all advices done by Viper.
705
  (viper--deactivate-advice-list)
Michael Kifer's avatar
Michael Kifer committed
706 707 708

  (setq viper-mode nil)

709 710 711 712 713 714
  (when (and (fboundp 'add-to-ordered-list) (boundp 'emulation-mode-map-alists))
    (setq emulation-mode-map-alists
	  (delq 'viper--intercept-key-maps
		(delq 'viper--key-maps emulation-mode-map-alists))
	  ))

Michael Kifer's avatar
Michael Kifer committed
715 716 717 718
  (viper-delocalize-var 'viper-vi-minibuffer-minor-mode)
  (viper-delocalize-var 'viper-insert-minibuffer-minor-mode)
  (viper-delocalize-var 'viper-vi-intercept-minor-mode)
  (viper-delocalize-var 'viper-insert-intercept-minor-mode)
719

Michael Kifer's avatar
Michael Kifer committed
720 721 722 723 724 725
  (viper-delocalize-var 'viper-vi-local-user-minor-mode)
  (viper-delocalize-var 'viper-vi-kbd-minor-mode)
  (viper-delocalize-var 'viper-vi-global-user-minor-mode)
  (viper-delocalize-var 'viper-vi-state-modifier-minor-mode)
  (viper-delocalize-var 'viper-vi-diehard-minor-mode)
  (viper-delocalize-var 'viper-vi-basic-minor-mode)
726

Michael Kifer's avatar
Michael Kifer committed
727
  (viper-delocalize-var 'viper-replace-minor-mode)
728

Michael Kifer's avatar
Michael Kifer committed
729 730 731 732 733 734
  (viper-delocalize-var 'viper-insert-local-user-minor-mode)
  (viper-delocalize-var 'viper-insert-kbd-minor-mode)
  (viper-delocalize-var 'viper-insert-global-user-minor-mode)
  (viper-delocalize-var 'viper-insert-state-modifier-minor-mode)
  (viper-delocalize-var 'viper-insert-diehard-minor-mode)
  (viper-delocalize-var 'viper-insert-basic-minor-mode)
735

Michael Kifer's avatar
Michael Kifer committed
736 737 738 739 740 741
  (viper-delocalize-var 'viper-emacs-intercept-minor-mode)
  (viper-delocalize-var 'viper-emacs-local-user-minor-mode)
  (viper-delocalize-var 'viper-emacs-kbd-minor-mode)
  (viper-delocalize-var 'viper-emacs-global-user-minor-mode)
  (viper-delocalize-var 'viper-emacs-state-modifier-minor-mode)

Michael Kifer's avatar
Michael Kifer committed
742 743 744
  (viper-delocalize-var 'viper-current-state)
  (viper-delocalize-var 'viper-mode-string)

Michael Kifer's avatar
Michael Kifer committed
745 746 747 748
  (setq-default viper-vi-minibuffer-minor-mode	       nil
		viper-insert-minibuffer-minor-mode     nil
		viper-vi-intercept-minor-mode	       nil
		viper-insert-intercept-minor-mode      nil
749

Michael Kifer's avatar
Michael Kifer committed
750 751 752 753 754 755
		viper-vi-local-user-minor-mode         nil
		viper-vi-kbd-minor-mode        	       nil
		viper-vi-global-user-minor-mode        nil
		viper-vi-state-modifier-minor-mode     nil
		viper-vi-diehard-minor-mode            nil
		viper-vi-basic-minor-mode              nil
756

Michael Kifer's avatar
Michael Kifer committed
757
		viper-replace-minor-mode 	       nil
758

Michael Kifer's avatar
Michael Kifer committed
759 760 761 762 763 764 765 766 767 768 769 770
		viper-insert-local-user-minor-mode     nil
		viper-insert-kbd-minor-mode            nil
		viper-insert-global-user-minor-mode    nil
		viper-insert-state-modifier-minor-mode nil
		viper-insert-diehard-minor-mode        nil
		viper-insert-basic-minor-mode          nil

		viper-emacs-intercept-minor-mode       nil
		viper-emacs-local-user-minor-mode      nil
		viper-emacs-kbd-minor-mode             nil
		viper-emacs-global-user-minor-mode     nil
		viper-emacs-state-modifier-minor-mode  nil
Michael Kifer's avatar
Michael Kifer committed
771 772 773

		viper-current-state		       'emacs-state
		viper-mode-string		       viper-emacs-state-id
Michael Kifer's avatar
Michael Kifer committed
774 775 776
		)

  ;; remove all hooks set by viper
777 778 779 780
  (mapatoms #'viper-remove-hooks)
  (remove-hook 'comint-mode-hook #'viper-comint-mode-hook)
  (remove-hook 'erc-mode-hook #'viper-comint-mode-hook)
  (remove-hook 'change-major-mode-hook #'viper-major-mode-change-sentinel)
Michael Kifer's avatar
Michael Kifer committed
781 782 783 784

  ;; unbind Viper mouse bindings
  (viper-unbind-mouse-search-key)
  (viper-unbind-mouse-insert-key)
Michael Kifer's avatar
Michael Kifer committed
785
  ;; In emacs, we have to advice handle-switch-frame
Paul Eggert's avatar
Paul Eggert committed
786
  ;; This advice is undone earlier, when all advices matching "viper-" are
Michael Kifer's avatar
Michael Kifer committed
787 788
  ;; deactivated.
  ) ; end viper-go-away
Michael Kifer's avatar
Michael Kifer committed
789 790


Michael Kifer's avatar
Michael Kifer committed
791 792 793 794 795 796
;; list of buffers that just changed their major mode
;; used in a hack that triggers vi command mode whenever needed
(defvar viper-new-major-mode-buffer-list nil)

;; set appropriate Viper state in buffers that changed major mode
(defun set-viper-state-in-major-mode ()
797
  (mapc
Michael Kifer's avatar
Michael Kifer committed
798 799 800
   (lambda (buf)
     (if (viper-buffer-live-p buf)
	 (with-current-buffer buf
801
	   (cond ((and (this-major-mode-requires-vi-state major-mode)
Michael Kifer's avatar
Michael Kifer committed
802 803
		       (eq viper-current-state 'emacs-state))
		  (viper-mode))
804
		 ((cl-member-if #'derived-mode-p viper-emacs-state-mode-list)
Michael Kifer's avatar
Michael Kifer committed
805 806 807 808 809
		  ;; not checking (eq viper-current-state 'emacs-state)
		  ;; because viper-current-state could have gotten it by
		  ;; default.  We need viper-change-state-to-emacs here to have
		  ;; the keymaps take effect.
		  (viper-change-state-to-emacs))
810 811
		 ((and (cl-member-if #'derived-mode-p
                                     viper-insert-state-mode-list)
Michael Kifer's avatar
Michael Kifer committed
812 813 814 815
		       (not (eq viper-current-state 'insert-state)))
		  (viper-change-state-to-insert))
		 )) ; with-current-buffer
       )) ; function
Michael Kifer's avatar
Michael Kifer committed
816 817 818 819
   viper-new-major-mode-buffer-list)
  ;; clear the list of bufs that changed major mode
  (setq viper-new-major-mode-buffer-list nil)
  ;; change the global value of hook
820
  (remove-hook 'viper-post-command-hooks #'set-viper-state-in-major-mode))
Michael Kifer's avatar
Michael Kifer committed
821 822 823 824 825

;; sets up post-command-hook to turn viper-mode, if the current mode is
;; fundamental
(defun viper-major-mode-change-sentinel ()
  (save-match-data
826
    (or (string-match "\\*Minibuf-" (buffer-name))
827
	(setq viper-new-major-mode-buffer-list
Michael Kifer's avatar
Michael Kifer committed
828 829
	      (cons (current-buffer) viper-new-major-mode-buffer-list))))
  ;; change the global value of hook
830
  (add-hook 'viper-post-command-hooks #'set-viper-state-in-major-mode t))
Michael Kifer's avatar
Michael Kifer committed
831

Michael Kifer's avatar
Michael Kifer committed
832

833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
;;; Handling of tty's ESC event

;; On a tty, an ESC event can either be the user hitting the escape key, or
;; some element of a byte sequence used to encode for example cursor keys.
;; So we try to recognize those events that correspond to the escape key and
;; turn them into `escape' events (same as used under GUIs).  The heuristic we
;; use to distinguish the two cases is based, as usual, on a timeout, and on
;; the fact that the special ESC=>escape mapping only takes place if the whole
;; last key-sequence so far is just [?\e], i.e. either we're still in
;; read-key-sequence, or the last read-key-sequence only read [?\e], which
;; should ideally never happen because it should have been mapped to [escape].

(defun viper--tty-ESC-filter (map)
  (if (and (equal (this-single-command-keys) [?\e])
           (sit-for (/ viper-fast-keyseq-timeout 1000)))
      [escape] map))

(defun viper--lookup-key (map key)
  "Kind of like `lookup-key'.
Two differences:
- KEY is a single key, not a sequence.
- the result is the \"raw\" binding, so it can be a `menu-item', rather than the
  binding contained in that menu item."
  (catch 'found
    (map-keymap (lambda (k b) (if (equal key k) (throw 'found b))) map)))

(defun viper-catch-tty-ESC ()
  "Setup key mappings of current terminal to turn a tty's ESC into `escape'."
  (when (memq (terminal-live-p (frame-terminal)) '(t pc))
    (let ((esc-binding (viper-uncatch-tty-ESC)))
      (define-key input-decode-map
        [?\e] `(menu-item "" ,esc-binding :filter viper--tty-ESC-filter)))))

(defun viper-uncatch-tty-ESC ()
  "Don't hack ESC into `escape' any more."
  (let ((b (viper--lookup-key input-decode-map ?\e)))
    (and (eq 'menu-item (car-safe b))
         (eq 'viper--tty-ESC-filter (nth 4 b))
         (define-key input-decode-map [?\e] (setq b (nth 2 b))))
    b))

(defun viper-setup-ESC-to-escape (enable)
  (if enable
876 877
      (add-hook 'tty-setup-hook #'viper-catch-tty-ESC)
    (remove-hook 'tty-setup-hook #'viper-catch-tty-ESC))
878 879 880 881 882 883 884
  (let ((seen ()))
    (dolist (frame (frame-list))
      (let ((terminal (frame-terminal frame)))
        (unless (memq terminal seen)
          (push terminal seen)
          (with-selected-frame frame
            (if enable (viper-catch-tty-ESC) (viper-uncatch-tty-ESC))))))))
Michael Kifer's avatar
Michael Kifer committed
885

886
;; This sets major mode hooks to make them come up in vi-state.
Michael Kifer's avatar
Michael Kifer committed
887
(defun viper-set-hooks ()
Karl Heuer's avatar
Karl Heuer committed
888 889 890 891
  ;; It is of course a misnomer to call viper-mode a `major mode'.
  ;; However, this has the effect that if the user didn't specify the
  ;; default mode, new buffers that fall back on the default will come up
  ;; in Fundamental Mode and Vi state.
892 893
  ;; When viper-mode is executed in such a case, it will set the major mode
  ;; back to fundamental-mode.
894
  (if (eq (default-value 'major-mode) #'fundamental-mode)
895
      ;; FIXME: We should use after-change-major-mode-hook instead!
896
      (setq-default major-mode #'viper-mode))