vi.el 56.9 KB
Newer Older
1
;;; vi.el --- major mode for emulating "vi" editor under GNU Emacs
Eric S. Raymond's avatar
Eric S. Raymond committed
2

3 4
;; This file is in the public domain because the authors distributed it
;; without a copyright notice before the US signed the Bern Convention.
5

6 7
;; This file is part of GNU Emacs.

Eric S. Raymond's avatar
Eric S. Raymond committed
8 9
;; Author: Neal Ziring <nz@rsch.wisc.edu>
;;	Felix S. T. Wu <wu@crys.wisc.edu>
Eric S. Raymond's avatar
Eric S. Raymond committed
10
;; Keywords: emulations
Eric S. Raymond's avatar
Eric S. Raymond committed
11

12
;;; Commentary:
Eric S. Raymond's avatar
Eric S. Raymond committed
13

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
;; Originally written by : seismo!wucs!nz@rsch.wisc.edu (Neal Ziring)
;; Extensively redesigned and rewritten by wu@crys.wisc.edu (Felix S.T. Wu)
;; Last revision: 01/07/87 Wed (for GNU Emacs 18.33)

;; INSTALLATION PROCEDURE:
;; 1) Add a global key binding for command "vi-mode" (I use ESC ESC instead of
;;    the single ESC used in real "vi", so I can access other ESC prefixed emacs
;;    commands while I'm in "vi"), say, by putting the following line in your
;;    ".emacs" file:
;;    (define-key global-map "\e\e" 'vi-mode) ;quick switch into vi-mode
;; 2) If you wish you can define "find-file-hook" to enter "vi" automatically
;;    after a file is loaded into the buffer. For example, I defined it as:
;;    (setq find-file-hook (list
;;                           (function (lambda ()
;;                               (if (not (or (eq major-mode 'Info-mode)
;; 	                                     (eq major-mode 'vi-mode)))
;;                                   (vi-mode))))))
;; 3) In your .emacs file you can define the command "vi-mode" to be "autoload"
;;    or you can execute the "load" command to load "vi" directly.
;; 4) Read the comments for command "vi-mode" before you start using it.

;; COULD DO
;; 1). A general 'define-operator' function to replace current hack
;; 2). In operator handling, should allow other point moving Emacs commands
;;     (such as ESC <, ESC >) to be used as arguments.

Eric S. Raymond's avatar
Eric S. Raymond committed
40
;;; Code:
Richard M. Stallman's avatar
Richard M. Stallman committed
41

42 43 44 45
(defvar vi-mode-old-major-mode)
(defvar vi-mode-old-mode-name)
(defvar vi-mode-old-local-map)
(defvar vi-mode-old-case-fold)
Richard M. Stallman's avatar
Richard M. Stallman committed
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

(if (null (where-is-internal 'vi-switch-mode (current-local-map)))
    (define-key ctl-x-map "~" 'vi-switch-mode))

(defvar vi-tilde-map nil
  "Keymap used for \\[vi-switch-mode] prefix key.  Link to various major modes.")

(if vi-tilde-map
    nil
  (setq vi-tilde-map (make-keymap))
  (define-key vi-tilde-map "a" 'abbrev-mode)
  (define-key vi-tilde-map "c" 'c-mode)
  (define-key vi-tilde-map "d" 'vi-debugging)
  (define-key vi-tilde-map "e" 'emacs-lisp-mode)
  (define-key vi-tilde-map "f" 'auto-fill-mode)
  (define-key vi-tilde-map "g" 'prolog-mode)
  (define-key vi-tilde-map "h" 'hanoi)
  (define-key vi-tilde-map "i" 'info-mode)
  (define-key vi-tilde-map "l" 'lisp-mode)
  (define-key vi-tilde-map "n" 'nroff-mode)
  (define-key vi-tilde-map "o" 'overwrite-mode)
  (define-key vi-tilde-map "O" 'outline-mode)
  (define-key vi-tilde-map "P" 'picture-mode)
  (define-key vi-tilde-map "r" 'vi-readonly-mode)
  (define-key vi-tilde-map "t" 'text-mode)
  (define-key vi-tilde-map "v" 'vi-mode)
  (define-key vi-tilde-map "x" 'tex-mode)
  (define-key vi-tilde-map "~" 'vi-back-to-old-mode))
74 75 76 77 78 79 80 81 82 83 84 85 86 87

(defun vi-switch-mode (arg mode-char)
  "Switch the major mode of current buffer as specified by the following char \\{vi-tilde-map}"
  (interactive "P\nc")
  (let ((mode-cmd (lookup-key vi-tilde-map (char-to-string mode-char))))
    (if (null mode-cmd)
	(with-output-to-temp-buffer "*Help*"
	  (princ (substitute-command-keys "Possible major modes to switch to: \\{vi-tilde-map}"))
	  (save-excursion
	    (set-buffer standard-output)
	    (help-mode)))
      (setq prefix-arg arg)		; prefix arg will be passed down
      (command-execute mode-cmd nil)	; may need to save mode-line-format etc
      (force-mode-line-update))))	; just in case
Richard M. Stallman's avatar
Richard M. Stallman committed
88

89

Richard M. Stallman's avatar
Richard M. Stallman committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
(defun vi-debugging (arg)
  "Toggle debug-on-error flag.  If prefix arg is given, set t."
  (interactive "P")
  (if arg
      (setq debug-on-error t)
    (setq debug-on-error (not debug-on-error)))
  (if debug-on-error
      (message "Debug-on-error ...")
    (message "NO more debug-on-error")))

(defun vi-back-to-old-mode ()
  "Go back to the previous mode without setting up for insertion."
  (interactive)
  (if vi-mode-old-major-mode
      (progn
	(setq mode-name vi-mode-old-mode-name)
	(use-local-map vi-mode-old-local-map)
	(setq major-mode vi-mode-old-major-mode)
	(setq case-fold-search vi-mode-old-case-fold)
109
	(force-mode-line-update))))
Richard M. Stallman's avatar
Richard M. Stallman committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

(defun vi-readonly-mode ()
  "Toggle current buffer's readonly flag."
  (interactive)
  (setq buffer-read-only (not buffer-read-only)))

(defvar vi-com-map nil
   "Keymap used in Evi's command state
Command state includes most of the vi editing commands, with some Emacs
command extensions.")

(put 'vi-undefined 'suppress-keymap t)
(if vi-com-map nil
  (setq vi-com-map (make-keymap))
;;(fillarray vi-com-map 'vi-undefined)
  (define-key vi-com-map "\C-@" 'vi-mark-region) ; extension
  (define-key vi-com-map "\C-a" 'vi-ask-for-info)  ; extension
127
  (define-key vi-com-map "\C-b" 'vi-backward-windowful)
Richard M. Stallman's avatar
Richard M. Stallman committed
128 129 130
  (define-key vi-com-map "\C-c" 'vi-do-old-mode-C-c-command) ; extension
  (define-key vi-com-map "\C-d" 'vi-scroll-down-window)
  (define-key vi-com-map "\C-e" 'vi-expose-line-below)
131
  (define-key vi-com-map "\C-f" 'vi-forward-windowful)
Richard M. Stallman's avatar
Richard M. Stallman committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
  (define-key vi-com-map "\C-g" 'keyboard-quit)
  (define-key vi-com-map "\C-i" 'indent-relative-maybe) ; TAB
  (define-key vi-com-map "\C-j" 'vi-next-line) ; LFD
  (define-key vi-com-map "\C-k" 'vi-kill-line) ; extension
  (define-key vi-com-map "\C-l" 'recenter)
  (define-key vi-com-map "\C-m" 'vi-next-line-first-nonwhite) ; RET
  (define-key vi-com-map "\C-n" 'vi-next-line)
  (define-key vi-com-map "\C-o" 'vi-split-open-line)
  (define-key vi-com-map "\C-p" 'previous-line)
  (define-key vi-com-map "\C-q" 'vi-query-replace) ; extension
  (define-key vi-com-map "\C-r" 'vi-isearch-backward) ; modification
  (define-key vi-com-map "\C-s" 'vi-isearch-forward)  ; extension
  (define-key vi-com-map "\C-t" 'vi-transpose-objects) ; extension
  (define-key vi-com-map "\C-u" 'vi-scroll-up-window)
  (define-key vi-com-map "\C-v" 'scroll-up) ; extension
  (define-key vi-com-map "\C-w" 'vi-kill-region)   ; extension
  (define-key vi-com-map "\C-x" 'Control-X-prefix) ; extension
  (define-key vi-com-map "\C-y" 'vi-expose-line-above)
  (define-key vi-com-map "\C-z" 'suspend-emacs)

  (define-key vi-com-map "\e"   'ESC-prefix); C-[ (ESC)
  (define-key vi-com-map "\C-\\" 'vi-unimplemented)
  (define-key vi-com-map "\C-]" 'find-tag)
  (define-key vi-com-map "\C-^" 'vi-locate-def)  ; extension
156
  (define-key vi-com-map "\C-_" 'vi-undefined)
Richard M. Stallman's avatar
Richard M. Stallman committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

  (define-key vi-com-map " " 'forward-char)
  (define-key vi-com-map "!"  'vi-operator)
  (define-key vi-com-map "\"" 'vi-char-argument)
  (define-key vi-com-map "#"  'universal-argument) ; extension
  (define-key vi-com-map "$"  'end-of-line)
  (define-key vi-com-map "%"  'vi-find-matching-paren)
  (define-key vi-com-map "&"  'vi-unimplemented)
  (define-key vi-com-map "'"  'vi-goto-line-mark)
  (define-key vi-com-map "("  'backward-sexp)
  (define-key vi-com-map ")"  'forward-sexp)
  (define-key vi-com-map "*"  'vi-name-last-change-or-macro) ; extension
  (define-key vi-com-map "+"  'vi-next-line-first-nonwhite)
  (define-key vi-com-map ","  'vi-reverse-last-find-char)
  (define-key vi-com-map "-"  'vi-previous-line-first-nonwhite)
  (define-key vi-com-map "."  'vi-redo-last-change-command)
  (define-key vi-com-map "/"  'vi-search-forward)
  (define-key vi-com-map "0"  'beginning-of-line)

  (define-key vi-com-map "1"  'vi-digit-argument)
  (define-key vi-com-map "2"  'vi-digit-argument)
  (define-key vi-com-map "3"  'vi-digit-argument)
  (define-key vi-com-map "4"  'vi-digit-argument)
  (define-key vi-com-map "5"  'vi-digit-argument)
  (define-key vi-com-map "6"  'vi-digit-argument)
  (define-key vi-com-map "7"  'vi-digit-argument)
  (define-key vi-com-map "8"  'vi-digit-argument)
  (define-key vi-com-map "9"  'vi-digit-argument)

  (define-key vi-com-map ":"  'vi-ex-cmd)
  (define-key vi-com-map ";"  'vi-repeat-last-find-char)
  (define-key vi-com-map "<"  'vi-operator)
  (define-key vi-com-map "="  'vi-operator)
  (define-key vi-com-map ">"  'vi-operator)
  (define-key vi-com-map "?"  'vi-search-backward)
  (define-key vi-com-map "@"  'vi-call-named-change-or-macro) ; extension

  (define-key vi-com-map "A"  'vi-append-at-end-of-line)
  (define-key vi-com-map "B"  'vi-backward-blank-delimited-word)
  (define-key vi-com-map "C"  'vi-change-rest-of-line)
  (define-key vi-com-map "D"  'vi-kill-line)
  (define-key vi-com-map "E"  'vi-end-of-blank-delimited-word)
  (define-key vi-com-map "F"  'vi-backward-find-char)
  (define-key vi-com-map "G"  'vi-goto-line)
  (define-key vi-com-map "H"  'vi-home-window-line)
  (define-key vi-com-map "I"  'vi-insert-before-first-nonwhite)
  (define-key vi-com-map "J"  'vi-join-lines)
204
  (define-key vi-com-map "K"  'vi-undefined)
Richard M. Stallman's avatar
Richard M. Stallman committed
205 206 207 208 209 210 211 212 213 214
  (define-key vi-com-map "L"  'vi-last-window-line)
  (define-key vi-com-map "M"  'vi-middle-window-line)
  (define-key vi-com-map "N"  'vi-reverse-last-search)
  (define-key vi-com-map "O"  'vi-open-above)
  (define-key vi-com-map "P"  'vi-put-before)
  (define-key vi-com-map "Q"  'vi-quote-words) ; extension
  (define-key vi-com-map "R"  'vi-replace-chars)
  (define-key vi-com-map "S"  'vi-substitute-lines)
  (define-key vi-com-map "T"  'vi-backward-upto-char)
  (define-key vi-com-map "U"  'vi-unimplemented)
215
  (define-key vi-com-map "V"  'vi-undefined)
Richard M. Stallman's avatar
Richard M. Stallman committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
  (define-key vi-com-map "W"  'vi-forward-blank-delimited-word)
  (define-key vi-com-map "X"  'call-last-kbd-macro) ; modification/extension
  (define-key vi-com-map "Y"  'vi-yank-line)
  (define-key vi-com-map "Z" (make-sparse-keymap)) ;allow below prefix command
  (define-key vi-com-map "ZZ" 'vi-save-all-and-exit)

  (define-key vi-com-map "["  'vi-unimplemented)
  (define-key vi-com-map "\\" 'vi-operator) ; extension for vi-narrow-op
  (define-key vi-com-map "]"  'vi-unimplemented)
  (define-key vi-com-map "^"  'back-to-indentation)
  (define-key vi-com-map "_"  'vi-undefined)
  (define-key vi-com-map "`"  'vi-goto-char-mark)

  (define-key vi-com-map "a"  'vi-insert-after)
  (define-key vi-com-map "b"  'backward-word)
  (define-key vi-com-map "c"  'vi-operator)
  (define-key vi-com-map "d"  'vi-operator)
  (define-key vi-com-map "e"  'vi-end-of-word)
  (define-key vi-com-map "f"  'vi-forward-find-char)
  (define-key vi-com-map "g"  'vi-beginning-of-buffer) ; extension
  (define-key vi-com-map "h"  'backward-char)
  (define-key vi-com-map "i"  'vi-insert-before)
  (define-key vi-com-map "j"  'vi-next-line)
  (define-key vi-com-map "k"  'previous-line)
  (define-key vi-com-map "l"  'forward-char)
  (define-key vi-com-map "m"  'vi-set-mark)
  (define-key vi-com-map "n"  'vi-repeat-last-search)
  (define-key vi-com-map "o"  'vi-open-below)
  (define-key vi-com-map "p"  'vi-put-after)
  (define-key vi-com-map "q"  'vi-replace)
  (define-key vi-com-map "r"  'vi-replace-1-char)
  (define-key vi-com-map "s"  'vi-substitute-chars)
  (define-key vi-com-map "t"  'vi-forward-upto-char)
  (define-key vi-com-map "u"  'undo)
250
  (define-key vi-com-map "v"  'vi-verify-spelling)
Richard M. Stallman's avatar
Richard M. Stallman committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
  (define-key vi-com-map "w"  'vi-forward-word)
  (define-key vi-com-map "x"  'vi-kill-char)
  (define-key vi-com-map "y"  'vi-operator)
  (define-key vi-com-map "z"  'vi-adjust-window)

  (define-key vi-com-map "{"  'backward-paragraph)
  (define-key vi-com-map "|"  'vi-goto-column)
  (define-key vi-com-map "}"  'forward-paragraph)
  (define-key vi-com-map "~"  'vi-change-case)
  (define-key vi-com-map "\177" 'delete-backward-char))

(put 'backward-char 'point-moving-unit 'char)
(put 'vi-next-line 'point-moving-unit 'line)
(put 'next-line 'point-moving-unit 'line)
(put 'forward-line 'point-moving-unit 'line)
(put 'previous-line 'point-moving-unit 'line)
(put 'vi-isearch-backward 'point-moving-unit 'search)
(put 'vi-search-backward 'point-moving-unit 'search)
(put 'vi-isearch-forward 'point-moving-unit 'search)
(put 'vi-search-forward 'point-moving-unit 'search)
(put 'forward-char 'point-moving-unit 'char)
(put 'end-of-line 'point-moving-unit 'char)
(put 'vi-find-matching-paren 'point-moving-unit 'match)
(put 'vi-goto-line-mark 'point-moving-unit 'line)
(put 'backward-sexp 'point-moving-unit 'sexp)
(put 'forward-sexp 'point-moving-unit 'sexp)
(put 'vi-next-line-first-nonwhite 'point-moving-unit 'line)
(put 'vi-previous-line-first-nonwhite 'point-moving-unit 'line)
(put 'vi-reverse-last-find-char 'point-moving-unit 'rev-find)
(put 'vi-re-search-forward 'point-moving-unit 'search)
(put 'beginning-of-line 'point-moving-unit 'char)
(put 'vi-beginning-of-buffer 'point-moving-unit 'char)
(put 'vi-repeat-last-find-char 'point-moving-unit 'find)
(put 'vi-re-search-backward 'point-moving-unit 'search)
(put 'vi-backward-blank-delimited-word 'point-moving-unit 'WORD)
(put 'vi-end-of-blank-delimited-word 'point-moving-unit 'match)
(put 'vi-backward-find-char 'point-moving-unit 'find)
(put 'vi-goto-line 'point-moving-unit 'line)
(put 'vi-home-window-line 'point-moving-unit 'line)
(put 'vi-last-window-line 'point-moving-unit 'line)
(put 'vi-middle-window-line 'point-moving-unit 'line)
(put 'vi-reverse-last-search 'point-moving-unit 'rev-search)
(put 'vi-backward-upto-char 'point-moving-unit 'find)
(put 'vi-forward-blank-delimited-word 'point-moving-unit 'WORD)
(put 'back-to-indentation 'point-moving-unit 'char)
(put 'vi-goto-char-mark 'point-moving-unit 'char)
(put 'backward-word 'point-moving-unit 'word)
(put 'vi-end-of-word 'point-moving-unit 'match)
(put 'vi-forward-find-char 'point-moving-unit 'find)
(put 'backward-char 'point-moving-unit 'char)
(put 'vi-forward-char 'point-moving-unit 'char)
(put 'vi-repeat-last-search 'point-moving-unit 'search)
(put 'vi-forward-upto-char 'point-moving-unit 'find)
(put 'vi-forward-word 'point-moving-unit 'word)
(put 'vi-goto-column 'point-moving-unit 'match)
(put 'forward-paragraph 'point-moving-unit 'paragraph)
(put 'backward-paragraph 'point-moving-unit 'paragraph)

;;; region mark commands
(put 'mark-page 'point-moving-unit 'region)
(put 'mark-paragraph 'point-moving-unit 'region)
(put 'mark-word 'point-moving-unit 'region)
(put 'mark-sexp 'point-moving-unit 'region)
(put 'mark-defun 'point-moving-unit 'region)
(put 'mark-whole-buffer 'point-moving-unit 'region)
(put 'mark-end-of-sentence 'point-moving-unit 'region)
317
(put 'c-mark-function 'point-moving-unit 'region)
Richard M. Stallman's avatar
Richard M. Stallman committed
318 319 320 321 322 323
;;;

(defvar vi-mark-alist nil
  "Alist of (NAME . MARK), marks are local to each buffer.")

(defvar vi-scroll-amount (/ (window-height) 2)
324
  "Default amount of lines for scrolling (used by \"^D\"/\"^U\").")
Richard M. Stallman's avatar
Richard M. Stallman committed
325 326

(defvar vi-shift-width 4
327
  "Shift amount for \"<\"/\">\" operators.")
Richard M. Stallman's avatar
Richard M. Stallman committed
328 329

(defvar vi-ins-point nil		; integer
330
  "Last insertion point.  Should use `mark' instead.")
Richard M. Stallman's avatar
Richard M. Stallman committed
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347

(defvar vi-ins-length nil		; integer
  "Length of last insertion.")

(defvar vi-ins-repetition nil		; integer
  "The repetition required for last insertion.")

(defvar vi-ins-overwrt-p nil		; boolean
  "T if last insertion was a replace actually.")

(defvar vi-ins-prefix-code nil		; ready-to-eval sexp
  "Code to be eval'ed before (redo-)insertion begins.")

(defvar vi-last-find-char nil		; cons cell
  "Save last direction, char and upto-flag used for char finding.")

(defvar vi-last-change-command nil	; cons cell
348
  "Save commands for redoing last changes.  Each command is in (FUNC . ARGS)
349
form that is ready to be `apply'ed.")
Richard M. Stallman's avatar
Richard M. Stallman committed
350 351 352 353 354

(defvar vi-last-shell-command nil	; last shell op command line
  "Save last shell command given for \"!\" operator.")

(defvar vi-insert-state nil             ; boolean
355
  "Non-nil if it is in insert state.")
Richard M. Stallman's avatar
Richard M. Stallman committed
356 357

; in "loaddefs.el"
358
;(defvar search-last-string ""
Richard M. Stallman's avatar
Richard M. Stallman committed
359 360 361 362 363 364 365 366 367 368
;  "Last string search for by a search command.")

(defvar vi-search-last-command nil	; (re-)search-forward(backward)
  "Save last search command for possible redo.")

(defvar vi-mode-old-local-map nil
  "Save the local-map used before entering vi-mode.")

(defvar vi-mode-old-mode-name nil
  "Save the mode-name before entering vi-mode.")
369

Richard M. Stallman's avatar
Richard M. Stallman committed
370 371 372 373
(defvar vi-mode-old-major-mode nil
  "Save the major-mode before entering vi-mode.")

(defvar vi-mode-old-case-fold nil)
374

Richard M. Stallman's avatar
Richard M. Stallman committed
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
;(defconst vi-add-to-mode-line-1
;  '(overwrite-mode nil " Insert"))

;; Value is same as vi-add-to-mode-line-1 when in vi mode,
;; but nil in other buffers.
;(defvar vi-add-to-mode-line nil)

(defun vi-mode-setup ()
  "Setup a buffer for vi-mode by creating necessary buffer-local variables."
;  (make-local-variable 'vi-add-to-mode-line)
;  (setq vi-add-to-mode-line vi-add-to-mode-line-1)
;  (or (memq vi-add-to-mode-line minor-mode-alist)
;      (setq minor-mode-alist (cons vi-add-to-mode-line minor-mode-alist)))
  (make-local-variable 'vi-scroll-amount)
  (setq vi-scroll-amount (/ (window-height) 2))
  (make-local-variable 'vi-shift-width)
  (setq vi-shift-width 4)
  (make-local-variable 'vi-ins-point)
  (make-local-variable 'vi-ins-length)
  (make-local-variable 'vi-ins-repetition)
  (make-local-variable 'vi-ins-overwrt-p)
  (make-local-variable 'vi-ins-prefix-code)
  (make-local-variable 'vi-last-change-command)
  (make-local-variable 'vi-last-shell-command)
  (make-local-variable 'vi-last-find-char)
  (make-local-variable 'vi-mark-alist)
  (make-local-variable 'vi-insert-state)
  (make-local-variable 'vi-mode-old-local-map)
  (make-local-variable 'vi-mode-old-mode-name)
  (make-local-variable 'vi-mode-old-major-mode)
  (make-local-variable 'vi-mode-old-case-fold)
406
  (run-mode-hooks 'vi-mode-hook))
407

Jim Blandy's avatar
Jim Blandy committed
408
;;;###autoload
Richard M. Stallman's avatar
Richard M. Stallman committed
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
(defun vi-mode ()
  "Major mode that acts like the `vi' editor.
The purpose of this mode is to provide you the combined power of vi (namely,
the \"cross product\" effect of commands and repeat last changes) and Emacs.

This command redefines nearly all keys to look like vi commands.
It records the previous major mode, and any vi command for input
\(`i', `a', `s', etc.) switches back to that mode.
Thus, ordinary Emacs (in whatever major mode you had been using)
is \"input\" mode as far as vi is concerned.

To get back into vi from \"input\" mode, you must issue this command again.
Therefore, it is recommended that you assign it to a key.

Major differences between this mode and real vi :

* Limitations and unsupported features
  - Search patterns with line offset (e.g. /pat/+3 or /pat/z.) are
    not supported.
  - Ex commands are not implemented; try ':' to get some hints.
  - No line undo (i.e. the 'U' command), but multi-undo is a standard feature.

* Modifications
  - The stopping positions for some point motion commands (word boundary,
    pattern search) are slightly different from standard 'vi'.
    Also, no automatic wrap around at end of buffer for pattern searching.
  - Since changes are done in two steps (deletion then insertion), you need
    to undo twice to completely undo a change command.  But this is not needed
    for undoing a repeated change command.
  - No need to set/unset 'magic', to search for a string with regular expr
    in it just put a prefix arg for the search commands.  Replace cmds too.
  - ^R is bound to incremental backward search, so use ^L to redraw screen.

* Extensions
  - Some standard (or modified) Emacs commands were integrated, such as
    incremental search, query replace, transpose objects, and keyboard macros.
  - In command state, ^X links to the 'ctl-x-map', and ESC can be linked to
    esc-map or set undefined.  These can give you the full power of Emacs.
  - See vi-com-map for those keys that are extensions to standard vi, e.g.
    `vi-name-last-change-or-macro', `vi-verify-spelling', `vi-locate-def',
    `vi-mark-region', and 'vi-quote-words'.  Some of them are quite handy.
  - Use \\[vi-switch-mode] to switch among different modes quickly.
451

Richard M. Stallman's avatar
Richard M. Stallman committed
452 453 454 455 456 457
Syntax table and abbrevs while in vi mode remain as they were in Emacs."
   (interactive)
   (if (null vi-mode-old-major-mode)	; very first call for current buffer
       (vi-mode-setup))

   (if (eq major-mode 'vi-mode)
458
       (progn (ding) (message "Already in vi-mode."))
Richard M. Stallman's avatar
Richard M. Stallman committed
459 460 461 462 463 464 465 466
     (setq vi-mode-old-local-map (current-local-map))
     (setq vi-mode-old-mode-name mode-name)
     (setq vi-mode-old-major-mode major-mode)
     (setq vi-mode-old-case-fold case-fold-search) ; this is needed !!
     (setq case-fold-search nil)	; exact case match in searching
     (use-local-map vi-com-map)
     (setq major-mode 'vi-mode)
     (setq mode-name "VI")
467
     (force-mode-line-update)		; force mode line update
Richard M. Stallman's avatar
Richard M. Stallman committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
     (if vi-insert-state	        ; this is a return from insertion
         (vi-end-of-insert-state))))

(defun vi-ding()
  "Ding !"
  (interactive)
  (ding))

(defun vi-save-all-and-exit ()
  "Save all modified buffers without asking, then exits emacs."
  (interactive)
  (save-some-buffers t)
  (kill-emacs))

;; to be used by "ex" commands
(defvar vi-replaced-string nil)
(defvar vi-replacing-string nil)

(defun vi-ex-cmd ()
  "Ex commands are not implemented in Evi mode.  For some commonly used ex
commands, you can use the following alternatives for similar effect :
w            C-x C-s (save-buffer)
wq           C-x C-c (save-buffers-kill-emacs)
w fname      C-x C-w (write-file)
e fname      C-x C-f (find-file)
r fname      C-x i   (insert-file)
s/old/new    use q (vi-replace) to do unconditional replace
             use C-q (vi-query-replace) to do query replace
set sw=n     M-x set-variable vi-shift-width n "
  (interactive)
;; (let ((cmd (read-string ":")) (lines 1))
;;  (cond ((string-match "s"))))
  (with-output-to-temp-buffer "*Help*"
501 502 503 504
    (princ (documentation 'vi-ex-cmd))
    (save-excursion
      (set-buffer standard-output)
      (help-mode))))
Richard M. Stallman's avatar
Richard M. Stallman committed
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522

(defun vi-undefined ()
  (interactive)
  (message "Command key \"%s\" is undefined in Evi."
	   (single-key-description last-command-char))
  (ding))

(defun vi-unimplemented ()
  (interactive)
  (message "Command key \"%s\" is not implemented in Evi."
	   (single-key-description last-command-char))
  (ding))

;;;;;
(defun vi-goto-insert-state (repetition &optional prefix-code do-it-now-p)
  "Go into insert state, the text entered will be repeated if REPETITION > 1.
If PREFIX-CODE is given, do it before insertion begins if DO-IT-NOW-P is T.
In any case, the prefix-code will be done before each 'redo-insert'.
523
This function expects `overwrite-mode' being set properly beforehand."
Richard M. Stallman's avatar
Richard M. Stallman committed
524 525 526 527 528 529 530 531
  (if do-it-now-p (apply (car prefix-code) (cdr prefix-code)))
  (setq vi-ins-point (point))
  (setq vi-ins-repetition repetition)
  (setq vi-ins-prefix-code prefix-code)
  (setq mode-name vi-mode-old-mode-name)
  (setq case-fold-search vi-mode-old-case-fold)
  (use-local-map vi-mode-old-local-map)
  (setq major-mode vi-mode-old-major-mode)
532
  (force-mode-line-update)
Richard M. Stallman's avatar
Richard M. Stallman committed
533 534 535 536 537 538 539 540 541
  (setq vi-insert-state t))

(defun vi-end-of-insert-state ()
  "Terminate insertion and set up last change command."
  (if (or (< (point) vi-ins-point)    ;Check if there is any effective change
	  (and (= (point) vi-ins-point) (null vi-ins-prefix-code))
	  (<= vi-ins-repetition 0))
      (vi-goto-command-state t)
    (if (> vi-ins-repetition 1)
542
	(progn
Richard M. Stallman's avatar
Richard M. Stallman committed
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
	  (let ((str (buffer-substring vi-ins-point (point))))
	    (while (> vi-ins-repetition 1)
	      (insert str)
	      (setq vi-ins-repetition (1- vi-ins-repetition))))))
    (vi-set-last-change-command 'vi-first-redo-insertion vi-ins-point (point)
			     overwrite-mode vi-ins-prefix-code)
    (vi-goto-command-state t)))

(defun vi-first-redo-insertion (begin end &optional overwrite-p prefix-code)
  "Redo last insertion the first time.  Extract the string and save it for
future redoes.  Do prefix-code if it's given, use overwrite mode if asked."
  (let ((str (buffer-substring begin end)))
    (if prefix-code (apply (car prefix-code) (cdr prefix-code)))
    (if overwrite-p (delete-region (point) (+ (point) (length str))))
    (insert str)
    (vi-set-last-change-command 'vi-more-redo-insertion str overwrite-p prefix-code)))

(defun vi-more-redo-insertion (str &optional overwrite-p prefix-code)
  "Redo more insertion : copy string from STR to point, use overwrite mode
if overwrite-p is T; apply prefix-code first if it's non-nil."
  (if prefix-code (apply (car prefix-code) (cdr prefix-code)))
  (if overwrite-p (delete-region (point) (+ (point) (length str))))
  (insert str))

(defun vi-goto-command-state (&optional from-insert-state-p)
  "Go to vi-mode command state.  If optional arg exists, means return from
insert state."
  (use-local-map vi-com-map)
  (setq vi-insert-state nil)
  (if from-insert-state-p
      (if overwrite-mode
	  (overwrite-mode 0)
;	(set-minor-mode 'ins "Insert" nil)
	)))

(defun vi-kill-line (arg)
   "kill specified number of lines (=d$), text saved in the kill ring."
   (interactive "*P")
   (kill-line arg)
   (vi-set-last-change-command 'kill-line arg))

584 585 586
(defun vi-kill-region (start end)
  (interactive "*r")
  (kill-region start end)
Richard M. Stallman's avatar
Richard M. Stallman committed
587
  (vi-set-last-change-command 'kill-region))
588

Richard M. Stallman's avatar
Richard M. Stallman committed
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
(defun vi-append-at-end-of-line (arg)
   "go to end of line and then go into vi insert state."
   (interactive "*p")
   (vi-goto-insert-state arg '(end-of-line) t))

(defun vi-change-rest-of-line (arg)
  "Change the rest of (ARG) lines (= c$ in vi)."
  (interactive "*P")
  (vi-goto-insert-state 1 (list 'kill-line arg) t))

(defun vi-insert-before-first-nonwhite (arg)
  "(= ^i in vi)"
  (interactive "*p")
  (vi-goto-insert-state arg '(back-to-indentation) t))

(defun vi-open-above (arg)
  "open new line(s) above current line and enter insert state."
  (interactive "*p")
  (vi-goto-insert-state 1
			(list (function (lambda (x)
					  (or (beginning-of-line)
					      (open-line x)))) arg)
			t))

(defun vi-open-below (arg)
  "open new line(s) and go into insert mode on the last line."
  (interactive "*p")
  (vi-goto-insert-state 1
			(list (function (lambda (x)
					  (or (end-of-line)
					    (open-line x)
					    (forward-line x)))) arg)
			t))

(defun vi-insert-after (arg)
   "start vi insert state after cursor."
   (interactive "*p")
   (vi-goto-insert-state arg
			 (list (function (lambda ()
					   (if (not (eolp)) (forward-char)))))
			 t))

(defun vi-insert-before (arg)
  "enter insert state before the cursor."
  (interactive "*p")
  (vi-goto-insert-state arg))

(defun vi-goto-line (arg)
   "Go to ARGth line."
   (interactive "P")
   (if (null (vi-raw-numeric-prefix arg))
640 641
       (with-no-warnings
	 (end-of-buffer))
Richard M. Stallman's avatar
Richard M. Stallman committed
642 643 644 645 646 647 648 649 650 651 652 653 654
     (goto-line (vi-prefix-numeric-value arg))))

(defun vi-beginning-of-buffer ()
  "Move point to the beginning of current buffer."
  (interactive)
  (goto-char (point-min)))

;;;;; not used now
;;(defvar regexp-search t		; string
;;  "*T if search string can contain regular expressions. (= set magic in vi)")
;;;;;

(defun vi-isearch-forward (arg)
655
  "Incremental search forward.  Use regexp version if ARG is non-nil."
Richard M. Stallman's avatar
Richard M. Stallman committed
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
  (interactive "P")
  (let ((scmd (if arg 'isearch-forward-regexp 'isearch-forward))
	(opoint (point)))
    (call-interactively scmd)
    (if (= opoint (point))
	nil
      (setq vi-search-last-command (if arg 're-search-forward 'search-forward)))))

(defun vi-isearch-backward (arg)
  "Incremental search backward.  Use regexp version if ARG is non-nil."
  (interactive "P")
  (let ((scmd (if arg 'isearch-backward-regexp 'isearch-backward))
	(opoint (point)))
    (call-interactively scmd)
    (if (= opoint (point))
	nil
      (setq vi-search-last-command (if arg 're-search-backward 'search-backward)))))

(defun vi-search-forward (arg string)
   "Nonincremental search forward. Use regexp version if ARG is non-nil."
   (interactive (if current-prefix-arg
		    (list t (read-string "regexp/" nil))
		  (list nil (read-string "/" nil))))
   (setq vi-search-last-command (if arg 're-search-forward 'search-forward))
680 681 682
   (if (> (length string) 0)
       (isearch-update-ring string arg))
   (funcall vi-search-last-command string nil nil 1))
Richard M. Stallman's avatar
Richard M. Stallman committed
683 684 685 686 687 688 689

(defun vi-search-backward (arg string)
   "Nonincremental search backward.  Use regexp version if ARG is non-nil."
   (interactive (if current-prefix-arg
		    (list t (read-string "regexp?" nil))
		  (list nil (read-string "?" nil))))
   (setq vi-search-last-command (if arg 're-search-backward 'search-backward))
690 691 692
   (if (> (length string) 0)
       (isearch-update-ring string arg))
   (funcall vi-search-last-command string nil nil 1))
Richard M. Stallman's avatar
Richard M. Stallman committed
693 694

(defun vi-repeat-last-search (arg &optional search-command search-string)
695 696
  "Repeat last search command.
If optional search-command/string are given,
Richard M. Stallman's avatar
Richard M. Stallman committed
697
use those instead of the ones saved."
698 699 700 701 702 703 704 705 706
  (interactive "p")
  (if (null search-command) (setq search-command vi-search-last-command))
  (if (null search-string)
      (setq search-string
	    (car (if (memq search-command
			   '(re-search-forward re-search-backward))
		     regexp-search-ring
		   search-ring))))
  (if (null search-command)
707
      (progn (ding) (message "No last search command to repeat."))
708
    (funcall search-command search-string nil nil arg)))
Richard M. Stallman's avatar
Richard M. Stallman committed
709 710

(defun vi-reverse-last-search (arg &optional search-command search-string)
711 712
  "Redo last search command in reverse direction.
If the optional search args are given, use those instead of the ones saved."
Richard M. Stallman's avatar
Richard M. Stallman committed
713 714
  (interactive "p")
  (if (null search-command) (setq search-command vi-search-last-command))
715 716 717 718 719 720
  (if (null search-string)
      (setq search-string
	    (car (if (memq search-command
			   '(re-search-forward re-search-backward))
		     regexp-search-ring
		   search-ring))))
Richard M. Stallman's avatar
Richard M. Stallman committed
721
  (if (null search-command)
722
      (progn (ding) (message "No last search command to repeat."))
Richard M. Stallman's avatar
Richard M. Stallman committed
723 724 725 726 727
    (funcall (cond ((eq search-command 're-search-forward) 're-search-backward)
		   ((eq search-command 're-search-backward) 're-search-forward)
		   ((eq search-command 'search-forward) 'search-backward)
		   ((eq search-command 'search-backward) 'search-forward))
	     search-string nil nil arg)))
728

Richard M. Stallman's avatar
Richard M. Stallman committed
729 730 731 732 733
(defun vi-join-lines (arg)
   "join ARG lines from current line (default 2), cleaning up white space."
   (interactive "P")
   (if (null (vi-raw-numeric-prefix arg))
       (delete-indentation t)
734 735 736 737
     (let ((count (vi-prefix-numeric-value arg)))
       (while (>= count 2)
	 (delete-indentation t)
	 (setq count (1- count)))))
Richard M. Stallman's avatar
Richard M. Stallman committed
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
   (vi-set-last-change-command 'vi-join-lines arg))

(defun vi-backward-kill-line ()
   "kill the current line.  Only works in insert state."
   (interactive)
   (if (not vi-insert-state)
       nil
     (beginning-of-line 1)
     (kill-line nil)))

(defun vi-abort-ins ()
  "abort insert state, kill inserted text and go back to command state."
  (interactive)
  (if (not vi-insert-state)
      nil
    (if (> (point) vi-ins-point)
	   (kill-region vi-ins-point (point)))
    (vi-goto-command-state t)))

757 758
(defun vi-backward-windowful (count)
  "Backward COUNT windowfuls. Default is one."
Richard M. Stallman's avatar
Richard M. Stallman committed
759 760 761 762 763 764 765
  (interactive "p")
; (set-mark-command nil)
  (while (> count 0)
    (scroll-down nil)
    (setq count (1- count))))

(defun vi-scroll-down-window (count)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
766 767 768
  "Scrolls down window COUNT lines.
If COUNT is nil (actually, non-integer), scrolls default amount.
The given COUNT is remembered for future scrollings."
Richard M. Stallman's avatar
Richard M. Stallman committed
769 770 771 772 773 774 775 776 777 778
  (interactive "P")
  (if (integerp count)
      (setq vi-scroll-amount count))
  (scroll-up vi-scroll-amount))

(defun vi-expose-line-below (count)
  "Expose COUNT more lines below the current window.  Default COUNT is 1."
  (interactive "p")
  (scroll-up count))

779 780
(defun vi-forward-windowful (count)
  "Forward COUNT windowfuls. Default is one."
Richard M. Stallman's avatar
Richard M. Stallman committed
781 782 783 784 785 786 787 788 789 790
  (interactive "p")
; (set-mark-command nil)
  (while (> count 0)
    (scroll-up nil)
    (setq count (1- count))))

(defun vi-next-line (count)
  "Go down count lines, try to keep at the same column."
  (interactive "p")
  (setq this-command 'next-line)	; this is a needed trick
791
  (if (= (point) (progn (line-move count) (point)))
Richard M. Stallman's avatar
Richard M. Stallman committed
792 793 794 795 796 797 798 799 800 801 802 803
      (ding)				; no moving, already at end of buffer
    (setq last-command 'next-line)))

(defun vi-next-line-first-nonwhite (count)
  "Go down COUNT lines.  Stop at first non-white."
  (interactive "p")
  (if (= (point) (progn (forward-line count) (back-to-indentation) (point)))
      (ding)))				; no moving, already at end of buffer

(defun vi-previous-line-first-nonwhite (count)
  "Go up COUNT lines.  Stop at first non-white."
  (interactive "p")
804
  (forward-line (- count))
Richard M. Stallman's avatar
Richard M. Stallman committed
805 806 807
  (back-to-indentation))

(defun vi-scroll-up-window (count)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
808 809 810
  "Scrolls up window COUNT lines.
If COUNT is nil (actually, non-integer), scrolls default amount.
The given COUNT is remembered for future scrollings."
Richard M. Stallman's avatar
Richard M. Stallman committed
811 812 813 814 815 816 817 818 819 820 821 822
  (interactive "P")
  (if (integerp count)
      (setq vi-scroll-amount count))
  (scroll-down vi-scroll-amount))

(defun vi-expose-line-above (count)
  "Expose COUNT more lines above the current window.  Default COUNT is 1."
  (interactive "p")
  (scroll-down count))

(defun vi-char-argument (arg)
  "Get following character (could be any CHAR) as part of the prefix argument.
823
Possible prefix-arg cases are nil, INTEGER, (nil . CHAR) or (INTEGER . CHAR)."
Richard M. Stallman's avatar
Richard M. Stallman committed
824 825 826 827 828 829 830 831 832
  (interactive "P")
  (let ((char (read-char)))
    (cond ((null arg) (setq prefix-arg (cons nil char)))
	  ((integerp arg) (setq prefix-arg (cons arg char)))
	  ; This can happen only if the user changed his/her mind for CHAR,
	  ; Or there are some leading "universal-argument"s
	  (t (setq prefix-arg (cons (car arg) char))))))

(defun vi-goto-mark (mark-char &optional line-flag)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
833 834
  "Go to marked position or line (if line-flag is given).
Goto mark '@' means jump into and pop the top mark on the mark ring."
Richard M. Stallman's avatar
Richard M. Stallman committed
835 836 837 838 839 840 841
  (cond ((char-equal mark-char last-command-char)	; `` or ''
	 (exchange-point-and-mark) (if line-flag (back-to-indentation)))
	((char-equal mark-char ?@)	; jump and pop mark
	 (set-mark-command t) (if line-flag (back-to-indentation)))
	(t
	 (let ((mark (vi-get-mark mark-char)))
	   (if (null mark)
842
	       (progn (vi-ding) (message "Mark register undefined."))
Richard M. Stallman's avatar
Richard M. Stallman committed
843 844 845
	     (set-mark-command nil)
	     (goto-char mark)
	     (if line-flag (back-to-indentation)))))))
846

Richard M. Stallman's avatar
Richard M. Stallman committed
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 876 877 878 879 880 881 882 883 884
(defun vi-goto-line-mark (char)
  "Go to the line (at first non-white) marked by next char."
  (interactive "c")
  (vi-goto-mark char t))

(defun vi-goto-char-mark (char)
  "Go to the char position marked by next mark-char."
  (interactive "c")
  (vi-goto-mark char))

(defun vi-digit-argument (arg)
  "Set numeric prefix argument."
  (interactive "P")
  (cond ((null arg) (digit-argument arg))
	((integerp arg) (digit-argument nil)
	                 (setq prefix-arg (* prefix-arg arg)))
	(t (digit-argument nil)		; in (NIL . CHAR) or (NUM . CHAR) form
	   (setq prefix-arg (cons (* prefix-arg
				     (if (null (car arg)) 1 (car arg)))
				  (cdr arg))))))

(defun vi-raw-numeric-prefix (arg)
  "Return the raw value of numeric part prefix argument."
  (if (consp arg) (car arg) arg))

(defun vi-prefix-numeric-value (arg)
  "Return numeric meaning of the raw prefix argument.  This is a modification
to the standard one provided in `callint.c' to handle (_ . CHAR) cases."
  (cond ((null arg) 1)
	((integerp arg) arg)
	((consp arg) (if (car arg) (car arg) 1))))

(defun vi-reverse-last-find-char (count &optional find-arg)
  "Reverse last f F t T operation COUNT times.  If the optional FIND-ARG
is given, it is used instead of the saved one."
  (interactive "p")
  (if (null find-arg) (setq find-arg vi-last-find-char))
  (if (null find-arg)
885
      (progn (ding) (message "No last find char to repeat."))
Richard M. Stallman's avatar
Richard M. Stallman committed
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912
    (vi-find-char (cons (* (car find-arg) -1) (cdr find-arg)) count))) ;6/13/86

(defun vi-find-char (arg count)
  "Find in DIRECTION (1/-1) for CHAR of COUNT'th times on current line.
If UPTO-FLAG is T, stop before the char. ARG = (DIRECTION.CHAR.UPTO-FLAG."
  (let* ((direction (car arg)) (char (car (cdr arg)))
	 (upto-flag (cdr (cdr arg))) (pos (+ (point) direction)))
    (if (catch 'exit-find-char
	  (while t
	    (cond ((null (char-after pos)) (throw 'exit-find-char nil))
		  ((char-equal (char-after pos) ?\n) (throw 'exit-find-char nil))
		  ((char-equal char (char-after pos)) (setq count (1- count))
		   (if (= count 0)
		       (throw 'exit-find-char
			      (if upto-flag
				  (setq pos (- pos direction))
				pos)))))
	    (setq pos (+ pos direction))))
      (goto-char pos)
    (ding))))

(defun vi-repeat-last-find-char (count &optional find-arg)
  "Repeat last f F t T operation COUNT times.  If optional FIND-ARG is given,
it is used instead of the saved one."
  (interactive "p")
  (if (null find-arg) (setq find-arg vi-last-find-char))
  (if (null find-arg)
913
      (progn (ding) (message "No last find char to repeat."))
Richard M. Stallman's avatar
Richard M. Stallman committed
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
    (vi-find-char find-arg count)))

(defun vi-backward-find-char (count char)
  "Find the COUNT'th CHAR backward on current line."
  (interactive "p\nc")
  (setq vi-last-find-char (cons -1 (cons char nil)))
  (vi-repeat-last-find-char count))

(defun vi-forward-find-char (count char)
  "Find the COUNT'th CHAR forward on current line."
  (interactive "p\nc")
  (setq vi-last-find-char (cons 1 (cons char nil)))
  (vi-repeat-last-find-char count))

(defun vi-backward-upto-char (count char)
  "Find upto the COUNT'th CHAR backward on current line."
  (interactive "p\nc")
  (setq vi-last-find-char (cons -1 (cons char t)))
  (vi-repeat-last-find-char count))

(defun vi-forward-upto-char (count char)
  "Find upto the COUNT'th CHAR forward on current line."
  (interactive "p\nc")
  (setq vi-last-find-char (cons 1 (cons char t)))
  (vi-repeat-last-find-char count))

(defun vi-end-of-word (count)
  "Move forward until encountering the end of a word.
With argument, do this that many times."
  (interactive "p")
  (if (not (eobp)) (forward-char))
  (if (re-search-forward "\\W*\\w+\\>" nil t count)
      (backward-char)))

(defun vi-replace-1-char (count char)
  "Replace char after point by CHAR.  Repeat COUNT times."
  (interactive "p\nc")
  (delete-char count nil)       ; don't save in kill ring
  (setq last-command-char char)
  (self-insert-command count)
  (vi-set-last-change-command 'vi-replace-1-char count char))

(defun vi-replace-chars (arg)
  "Replace chars over old ones."
  (interactive "*p")
  (overwrite-mode 1)
  (vi-goto-insert-state arg))

(defun vi-substitute-chars (count)
  "Substitute COUNT chars by the input chars, enter insert state."
  (interactive "*p")
  (vi-goto-insert-state 1 (list (function (lambda (c) ; this is a bit tricky
					    (delete-region (point)
							   (+ (point) c))))
				count) t))

(defun vi-substitute-lines (count)
  "Substitute COUNT lines by the input chars. (=cc in vi)"
  (interactive "*p")
  (vi-goto-insert-state 1 (list 'vi-delete-op 'next-line (1- count)) t))

(defun vi-prefix-char-value (arg)
  "Get the char part of the current prefix argument."
  (cond ((null arg) nil)
	((integerp arg) nil)
	((consp arg) (cdr arg))
	(t nil)))

(defun vi-operator (arg)
  "Handling vi operators (d/c/</>/!/=/y).  Current implementation requires
the key bindings of the operators being fixed."
  (interactive "P")
  (catch 'vi-exit-op
    (let ((this-op-char last-command-char))
      (setq last-command-char (read-char))
      (setq this-command (lookup-key vi-com-map (char-to-string last-command-char)))
      (if (not (eq this-command 'vi-digit-argument))
	  (setq prefix-arg arg)
	(vi-digit-argument arg)
	(setq last-command-char (read-char))
	(setq this-command (lookup-key vi-com-map (char-to-string last-command-char))))
      (cond ((char-equal this-op-char last-command-char) ; line op
	     (vi-execute-op this-op-char 'next-line
			    (cons (1- (vi-prefix-numeric-value prefix-arg))
				  (vi-prefix-char-value prefix-arg))))
	    ;; We assume any command that has no property 'point-moving-unit'
	    ;; as having that property with the value 'CHAR'.  3/12/86
	    (t   ;;  (get this-command 'point-moving-unit)
	     (vi-execute-op this-op-char this-command prefix-arg))))))
	    ;;   (t (throw 'vi-exit-op (ding)))))))

(defun vi-execute-op (op-char motion-command arg)
  "Execute vi edit operator as specified by OP-CHAR, the operand is the region
determined by the MOTION-COMMAND with ARG."
  (cond ((= op-char ?d)
	 (if (vi-delete-op motion-command arg)
	     (vi-set-last-change-command 'vi-delete-op (vi-repeat-command-of motion-command) arg)))
	 ((= op-char ?c)
	  (if (vi-delete-op motion-command arg)
	      (vi-goto-insert-state 1 (list 'vi-delete-op
			      (vi-repeat-command-of motion-command) arg) nil)))
	 ((= op-char ?y)
	  (if (vi-yank-op motion-command arg)
	      (vi-set-last-change-command 'vi-yank-op (vi-repeat-command-of motion-command) arg)))
	 ((= op-char ?!)
	  (if (vi-shell-op motion-command arg)
	      (vi-set-last-change-command 'vi-shell-op (vi-repeat-command-of motion-command) arg vi-last-shell-command)))
	 ((= op-char ?<)
	  (if (vi-shift-op motion-command arg (- vi-shift-width))
	      (vi-set-last-change-command 'vi-shift-op (vi-repeat-command-of motion-command) arg (- vi-shift-width))))
	 ((= op-char ?>)
	  (if (vi-shift-op motion-command arg vi-shift-width)
	      (vi-set-last-change-command 'vi-shift-op (vi-repeat-command-of motion-command) arg vi-shift-width)))
	 ((= op-char ?=)
	  (if (vi-indent-op motion-command arg)
	      (vi-set-last-change-command 'vi-indent-op (vi-repeat-command-of motion-command) arg)))
	 ((= op-char ?\\)
	  (vi-narrow-op motion-command arg))))

(defun vi-repeat-command-of (command)
  "Return the command for redo the given command."
  (let ((cmd-type (get command 'point-moving-unit)))
    (cond ((eq cmd-type 'search) 'vi-repeat-last-search)
	  ((eq cmd-type 'find) 'vi-repeat-last-find-char)
	  (t command))))

(defun vi-effective-range (motion-command arg)
  "Return (begin . end) of the range spanned by executing the given
MOTION-COMMAND with ARG.
   MOTION-COMMAND in ready-to-eval list form is not yet supported."
  (save-excursion
    (let ((begin (point)) end opoint
	  (moving-unit (get motion-command 'point-moving-unit)))
      (setq prefix-arg arg)
      (setq opoint (point))
      (command-execute motion-command nil)
;; Check if there is any effective motion. Note that for single line operation
;; the motion-command causes no effective point movement (since it moves up or
;; down zero lines), but it should be counted as effectively moved.
      (if (and (= (point) opoint) (not (eq moving-unit 'line)))
	  (cons opoint opoint)		; no effective motion
	(if (eq moving-unit 'region)
	    (setq begin (or (mark) (point))))
	(if (<= begin (point))
	    (setq end (point))
	  (setq end begin)
	  (setq begin (point)))
	(cond ((or (eq moving-unit 'match) (eq moving-unit 'find))
	       (setq end (1+ end)))
	      ((eq moving-unit 'line)
	       (goto-char begin) (beginning-of-line) (setq begin (point))
1065
	       (goto-char end) (forward-line 1) (beginning-of-line) (setq end (point))))
1066
	(if (> end (point-max)) (setq end (point-max))) ; force in buffer region
Richard M. Stallman's avatar
Richard M. Stallman committed
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
	(cons begin end)))))

(defun vi-delete-op (motion-command arg)
  "Delete range specified by MOTION-COMMAND with ARG."
  (let* ((range (vi-effective-range motion-command arg))
	 (begin (car range)) (end (cdr range)) reg)
    (if (= begin end)
	nil				; point not moved, abort op
      (setq reg (vi-prefix-char-value arg))
      (if (null reg)
	  (kill-region begin end)	; kill ring as unnamed registers
	(if (and (>= reg ?A) (<= reg ?Z))
	    (append-to-register (downcase reg) begin end t)
	  (copy-to-register reg begin end t)))
      t)))

(defun vi-yank-op (motion-command arg)
  "Yank (in vi sense) range specified by MOTION-COMMAND with ARG."
  (let* ((range (vi-effective-range motion-command arg))
	 (begin (car range)) (end (cdr range)) reg)
    (if (= begin end)
	nil				; point not moved, abort op
      (setq reg (vi-prefix-char-value arg))
      (if (null reg)
	  (copy-region-as-kill begin end); kill ring as unnamed registers
	(if (and (>= reg ?A) (<= reg ?Z))
	    (append-to-register (downcase reg) begin end nil)
	  (copy-to-register reg begin end nil)))
      t)))

(defun vi-yank-line (arg)
  "Yank (in vi sense) lines (= `yy' command)."
  (interactive "*P")
  (setq arg (cons (1- (vi-prefix-numeric-value arg)) (vi-prefix-char-value arg)))
  (if (vi-yank-op 'next-line arg)
      (vi-set-last-change-command 'vi-yank-op 'next-line arg)))

(defun vi-string-end-with-nl-p (string)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1105 1106
  "See if STRING ends with a newline char.
Used in checking whether the yanked text should be put back as lines or not."
Richard M. Stallman's avatar
Richard M. Stallman committed
1107 1108 1109
  (= (aref string (1- (length string))) ?\n))

(defun vi-put-before (arg &optional after-p)
1110
  "Put yanked (in vi sense) text back before/above cursor.
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1111 1112
If a numeric prefix value (currently it should be >1) is given, put back
text as lines.  If the optional after-p is given, put after/below the cursor."
Richard M. Stallman's avatar
Richard M. Stallman committed
1113 1114 1115 1116 1117 1118
  (interactive "P")
  (let ((reg (vi-prefix-char-value arg)) put-text)
    (if (and reg (or (< reg ?1) (> reg ?9)) (null (get-register reg)))
	(error "Nothing in register %c" reg)
      (if (null reg) (setq reg ?1))	; the default is the last text killed
      (setq put-text
Jim Blandy's avatar
Jim Blandy committed
1119 1120 1121 1122 1123 1124
	    (cond
	     ((and (>= reg ?1) (<= reg ?9))
	      (setq this-command 'yank) ; So we may yank-pop !!
	      (current-kill (- reg ?0 1) 'do-not-rotate))
	     ((stringp (get-register reg)) (get-register reg))
	     (t (error "Register %c is not containing text string" reg))))
Richard M. Stallman's avatar
Richard M. Stallman committed
1125 1126
      (if (vi-string-end-with-nl-p put-text) ; put back text as lines
	  (if after-p
1127
	      (progn (forward-line 1) (beginning-of-line))
Richard M. Stallman's avatar
Richard M. Stallman committed
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
	    (beginning-of-line))
	(if after-p (forward-char 1)))
      (push-mark (point))
      (insert put-text)
      (exchange-point-and-mark)
;;    (back-to-indentation)      ; this is not allowed if we allow yank-pop
      (vi-set-last-change-command 'vi-put-before arg after-p))))

(defun vi-put-after (arg)
  "Put yanked (in vi sense) text back after/below cursor."
  (interactive "P")
  (vi-put-before arg t))

(defun vi-shell-op (motion-command arg &optional shell-command)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1142 1143
  "Perform shell command (as filter).
Performs command on range specified by MOTION-COMMAND
Richard M. Stallman's avatar
Richard M. Stallman committed
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192
with ARG. If SHELL-COMMAND is not given, ask for one from minibuffer.
If char argument is given, it directs the output to a *temp* buffer."
  (let* ((range (vi-effective-range motion-command arg))
	 (begin (car range)) (end (cdr range)))
    (if (= begin end)
	nil				; point not moved, abort op
      (cond ((null shell-command)
	     (setq shell-command (read-string "!" nil))
	     (setq vi-last-shell-command shell-command)))
      (shell-command-on-region begin end shell-command (not (vi-prefix-char-value arg)))
      t)))

(defun vi-shift-op (motion-command arg amount)
  "Perform shift command on range specified by MOTION-COMMAND with ARG for
AMOUNT on each line.  Negative amount means shift left.
SPECIAL FEATURE: char argument can be used to specify shift amount(1-9)."
  (let* ((range (vi-effective-range motion-command arg))
	 (begin (car range)) (end (cdr range)))
    (if (= begin end)
	nil				; point not moved, abort op
      (if (vi-prefix-char-value arg)
	  (setq amount (if (> amount 0)
			   (- (vi-prefix-char-value arg) ?0)
			 (- ?0 (vi-prefix-char-value arg)))))
      (indent-rigidly begin end amount)
      t)))

(defun vi-indent-op (motion-command arg)
  "Perform indent command on range specified by MOTION-COMMAND with ARG."
  (let* ((range (vi-effective-range motion-command arg))
	 (begin (car range)) (end (cdr range)))
    (if (= begin end)
	nil				; point not moved, abort op
      (indent-region begin end nil)	; insert TAB as indent command
      t)))

(defun vi-narrow-op (motion-command arg)
  "Narrow to region specified by MOTION-COMMAND with ARG."
  (let* ((range (vi-effective-range motion-command arg))
	 (begin (car range)) (end (cdr range)) reg)
    (if (= begin end)
	nil				; point not moved, abort op
      (narrow-to-region begin end))))

(defun vi-get-mark (char)
  "Return contents of vi mark register named CHAR, or nil if undefined."
  (cdr (assq char vi-mark-alist)))

(defun vi-set-mark (char)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1193 1194
  "Set contents of vi mark register named CHAR to current point.
'@' is the special anonymous mark register."
Richard M. Stallman's avatar
Richard M. Stallman committed
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254
  (interactive "c")
  (if (char-equal char ?@)
      (set-mark-command nil)
    (let ((aelt (assq char vi-mark-alist)))
      (if aelt
	  (move-marker (cdr aelt) (point)) ; fixed 6/12/86
	(setq aelt (cons char (copy-marker (point))))
	(setq vi-mark-alist (cons aelt vi-mark-alist))))))

(defun vi-find-matching-paren ()
  "Locate the matching paren.  It's a hack right now."
  (interactive)
  (cond ((looking-at "[[({]") (forward-sexp 1) (backward-char 1))
	((looking-at "[])}]") (forward-char 1) (backward-sexp 1))
        (t (ding))))

(defun vi-backward-blank-delimited-word (count)
  "Backward COUNT blank-delimited words."
  (interactive "p")
  (if (re-search-backward "[ \t\n\`][^ \t\n\`]+" nil t count)
      (if (not (bobp)) (forward-char 1))))

(defun vi-forward-blank-delimited-word (count)
  "Forward COUNT blank-delimited words."
  (interactive "p")
  (if (re-search-forward "[^ \t\n]*[ \t\n]+[^ \t\n]" nil t count)
      (if (not (eobp)) (backward-char 1))))

(defun vi-end-of-blank-delimited-word (count)
  "Forward to the end of the COUNT'th blank-delimited word."
  (interactive "p")
  (if (re-search-forward "[^ \t\n\']+[ \t\n\']" nil t count)
      (if (not (eobp)) (backward-char 2))))

(defun vi-home-window-line (arg)
  "To window home or arg'th line from the top of the window."
  (interactive "p")
  (move-to-window-line (1- arg))
  (back-to-indentation))

(defun vi-last-window-line (arg)
  "To window last line or arg'th line from the bottom of the window."
  (interactive "p")
  (move-to-window-line (- arg))
  (back-to-indentation))

(defun vi-middle-window-line ()
  "To the middle line of the window."
  (interactive)
  (move-to-window-line nil)
  (back-to-indentation))

(defun vi-forward-word (count)
  "Stop at the beginning of the COUNT'th words from point."
  (interactive "p")
  (if (re-search-forward "\\w*\\W+\\<" nil t count)
      t
    (vi-ding)))

(defun vi-set-last-change-command (fun &rest args)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1255
  "Set (FUN . ARGS) as the `last-change-command'."
Richard M. Stallman's avatar
Richard M. Stallman committed
1256 1257 1258 1259
  (setq vi-last-change-command (cons fun args)))

(defun vi-redo-last-change-command (count &optional command)
  "Redo last change command COUNT times.  If the optional COMMAND is given,
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1260
it is used instead of the current `last-change-command'."
Richard M. Stallman's avatar
Richard M. Stallman committed
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
  (interactive "p")
  (if (null command)
      (setq command vi-last-change-command))
  (if (null command)
      (message "No last change command available.")
    (while (> count 0)
      (apply (car command) (cdr command))
      (setq count (1- count)))))

(defun vi-kill-char (count)
  "Kill COUNT chars from current point."
  (interactive "*p")
  (delete-char count t)			; save in kill ring
  (vi-set-last-change-command 'delete-char count t))

(defun vi-transpose-objects (arg unit)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1277 1278
  "Transpose objects.
The following char specifies unit of objects to be
Richard M. Stallman's avatar
Richard M. Stallman committed
1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
transposed -- \"c\" for chars, \"l\" for lines, \"w\" for words, \"s\" for
 sexp, \"p\" for paragraph.
For the use of the prefix-arg, refer to individual functions called."
  (interactive "*P\nc")
  (if (char-equal unit ??)
      (progn
	(message "Transpose: c(har), l(ine), p(aragraph), s(-exp), w(ord),")
	(setq unit (read-char))))
  (vi-set-last-change-command 'vi-transpose-objects arg unit)
  (cond ((char-equal unit ?c) (transpose-chars arg))
	((char-equal unit ?l) (transpose-lines (vi-prefix-numeric-value arg)))
	((char-equal unit ?p) (transpose-paragraphs (vi-prefix-numeric-value arg)))
	((char-equal unit ?s) (transpose-sexps (vi-prefix-numeric-value arg)))
	((char-equal unit ?w) (transpose-words (vi-prefix-numeric-value arg)))
	(t (vi-transpose-objects arg ??))))

(defun vi-query-replace (arg)
  "Query replace, use regexp version if ARG is non-nil."
  (interactive "*P")
  (let ((rcmd (if arg 'query-replace-regexp 'query-replace)))
    (call-interactively rcmd nil)))

(defun vi-replace (arg)
  "Replace strings, use regexp version if ARG is non-nil."
  (interactive "*P")
  (let ((rcmd (if arg 'replace-regexp 'replace-string)))
    (call-interactively rcmd nil)))
1306

Richard M. Stallman's avatar
Richard M. Stallman committed
1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
(defun vi-adjust-window (arg position)
  "Move current line to the top/center/bottom of the window."
  (interactive "p\nc")
  (cond ((char-equal position ?\r) (recenter 0))
	((char-equal position ?-) (recenter -1))
	((char-equal position ?.) (recenter (/ (window-height) 2)))
	(t (message "Move current line to: \\r(top) -(bottom) .(middle)")
	   (setq position (read-char))
	   (vi-adjust-window arg position))))

(defun vi-goto-column (col)
  "Go to given column of the current line."
  (interactive "p")
  (let ((opoint (point)))
    (beginning-of-line)
    (while (> col 1)
      (if (eolp)
	  (setq col 0)
	(forward-char 1)
	(setq col (1- col))))
    (if (= col 1)
	t
      (goto-char opoint)
      (ding))))

(defun vi-name-last-change-or-macro (arg char)
1333
  "Give name to the last change command or just defined kbd macro.
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1334 1335
If prefix ARG is given, name last macro, otherwise name last change command.
The following CHAR will be the name for the command or macro."
Richard M. Stallman's avatar
Richard M. Stallman committed
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346
  (interactive "P\nc")
  (if arg
      (name-last-kbd-macro (intern (char-to-string char)))
    (if (eq (car vi-last-change-command) 'vi-first-redo-insertion)
	(let* ((args (cdr vi-last-change-command)) ; save the insertion text
	       (str (buffer-substring (nth 0 args) (nth 1 args)))
	       (overwrite-p (nth 2 args))
	       (prefix-code (nth 3 args)))
	  (vi-set-last-change-command 'vi-more-redo-insertion str
				   overwrite-p prefix-code)))
    (fset (intern (char-to-string char)) vi-last-change-command)))
1347

Richard M. Stallman's avatar
Richard M. Stallman committed
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
(defun vi-call-named-change-or-macro (count char)
  "Execute COUNT times the keyboard macro definition named by the following CHAR."
  (interactive "p\nc")
  (if (stringp (symbol-function (intern (char-to-string char))))
      (execute-kbd-macro (intern (char-to-string char)) count)
    (vi-redo-last-change-command count (symbol-function (intern (char-to-string char))))))

(defun vi-change-case (arg)		; could be made as an operator ?
  "Change the case of the char after point."
  (interactive "*p")
  (catch 'exit
    (if (looking-at "[a-z]")
	(upcase-region (point) (+ (point) arg))
      (if (looking-at "[A-Z]")
	  (downcase-region (point) (+ (point) arg))
	(ding)
	(throw 'exit nil)))
    (vi-set-last-change-command 'vi-change-case arg) ;should avoid redundant save
    (forward-char arg)))

(defun vi-ask-for-info (char)
  "Inquire status info. The next CHAR will specify the particular info requested."
  (interactive "c")
  (cond ((char-equal char ?l) (what-line))
	((char-equal char ?c) (what-cursor-position))
	((char-equal char ?p) (what-page))
	(t (message "Ask for: l(ine number), c(ursor position), p(age number)")
	   (setq char (read-char))
	   (vi-ask-for-info char))))

(defun vi-mark-region (arg region)
1379
  "Mark region appropriately.  The next char REGION is d(efun),s(-exp),b(uffer),
Richard M. Stallman's avatar
Richard M. Stallman committed
1380 1381 1382
p(aragraph), P(age), f(unction in C/Pascal etc.), w(ord), e(nd of sentence),
l(ines)."
  (interactive "p\nc")
1383
  (cond ((char-equal region ?d) (mark-defun))
Richard M. Stallman's avatar
Richard M. Stallman committed
1384 1385
	((char-equal region ?s) (mark-sexp arg))
	((char-equal region ?b) (mark-whole-buffer))
1386
	((char-equal region ?p) (mark-paragraph))
Richard M. Stallman's avatar
Richard M. Stallman committed
1387
	((char-equal region ?P) (mark-page arg))
1388
	((char-equal region ?f) (c-mark-function))
Richard M. Stallman's avatar
Richard M. Stallman committed
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433
	((char-equal region ?w) (mark-word arg))
	((char-equal region ?e) (mark-end-of-sentence arg))
	((char-equal region ?l) (vi-mark-lines arg))
	(t (message "Mark: d(efun),s(-exp),b(uf),p(arag),P(age),f(unct),w(ord),e(os),l(ines)")
	   (setq region (read-char))
	   (vi-mark-region arg region))))

(defun vi-mark-lines (num)
  "Mark NUM of lines from current line as current region."
  (beginning-of-line 1)
  (push-mark)
  (end-of-line num))

(defun vi-verify-spelling (arg unit)
  "Verify spelling for the objects specified by char UNIT : [b(uffer),
r(egion), s(tring), w(ord) ]."
  (interactive "P\nc")
  (setq prefix-arg arg)			; seems not needed
  (cond ((char-equal unit ?b) (call-interactively 'spell-buffer))
	((char-equal unit ?r) (call-interactively 'spell-region))
	((char-equal unit ?s) (call-interactively 'spell-string))
	((char-equal unit ?w) (call-interactively 'spell-word))
	(t (message "Spell check: b(uffer), r(egion), s(tring), w(ord)")
	   (setq unit (read-char))
	   (vi-verify-spelling arg unit))))

(defun vi-do-old-mode-C-c-command (arg)
  "This is a hack for accessing mode specific C-c commands in vi-mode."
  (interactive "P")
  (let ((cmd (lookup-key vi-mode-old-local-map
			 (concat "\C-c" (char-to-string (read-char))))))
    (if (catch 'exit-vi-mode	; kludge hack due to dynamic binding
				; of case-fold-search
	  (if (null cmd)
	      (progn (ding) nil)
	    (let ((case-fold-search vi-mode-old-case-fold)) ; a hack
	      (setq prefix-arg arg)
	      (command-execute cmd nil)
	      nil)))
	(progn
	  (vi-back-to-old-mode)
	  (setq prefix-arg arg)
	  (command-execute cmd nil)))))

(defun vi-quote-words (arg char)
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1434 1435
  "Quote ARG words from the word point is on with pattern specified by CHAR.
Currently, CHAR could be [,{,(,\",',`,<,*, etc."
Richard M. Stallman's avatar
Richard M. Stallman committed
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
  (interactive "*p\nc")
  (while (not (string-match "[[({<\"'`*]" (char-to-string char)))
    (message "Enter any of [,{,(,<,\",',`,* as quoting character.")
    (setq char (read-char)))
  (vi-set-last-change-command 'vi-quote-words arg char)
  (if (not (looking-at "\\<")) (forward-word -1))
  (insert char)
  (cond ((char-equal char ?[) (setq char ?]))
	((char-equal char ?{) (setq char ?}))
	((char-equal char ?<) (setq char ?>))
	((char-equal char ?() (setq char ?)))
	((char-equal char ?`) (setq char ?')))
  (vi-end-of-word arg)
  (forward-char 1)
  (insert char))

(defun vi-locate-def ()
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1453 1454
  "Locate definition in current file for the name before the point.
It assumes a `(def..' always starts at the beginning of a line."
Richard M. Stallman's avatar
Richard M. Stallman committed
1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
  (interactive)
  (let (name)
    (save-excursion
      (setq name (buffer-substring (progn (vi-backward-blank-delimited-word 1)
					  (skip-chars-forward "^a-zA-Z")
					  (point))
				   (progn (vi-end-of-blank-delimited-word 1)
					  (forward-char)
					  (skip-chars-backward "^a-zA-Z")
					  (point)))))
    (set-mark-command nil)
    (goto-char (point-min))
    (if (re-search-forward (concat "^(def[unvarconst ]*" name) nil t)
	nil
1469 1470
      (ding)
      (message "No definition for \"%s\" in current file." name)
Richard M. Stallman's avatar
Richard M. Stallman committed
1471 1472 1473 1474
      (set-mark-command t))))

(defun vi-split-open-line (arg)
  "Insert a newline and leave point before it.
Christopher Zaborsky's avatar
Christopher Zaborsky committed
1475
With ARG, inserts that many newlines."
Richard M. Stallman's avatar
Richard M. Stallman committed
1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487
  (interactive "*p")
  (vi-goto-insert-state 1
    (list (function (lambda (arg)
		      (let ((flag (and (bolp) (not (bobp)))))
			(if flag (forward-char -1))
			(while (> arg 0)
			  (save-excursion
			    (insert ?\n)
			    (if fill-prefix (insert fill-prefix)))
			  (setq arg (1- arg)))
			(if flag (forward-char 1))))) arg)
    t))
Eric S. Raymond's avatar
Eric S. Raymond committed
1488

Richard M. Stallman's avatar
Richard M. Stallman committed
1489 1490
(provide 'vi)

1491
;; arch-tag: ac9bdac3-8acb-4ddd-bdae-c6dd873153b3
Eric S. Raymond's avatar
Eric S. Raymond committed
1492
;;; vi.el ends here