message format spec fixes (commit # 7)
[bpt/emacs.git] / lisp / emulation / viper-ex.el
CommitLineData
52fa07ba 1;;; viper-ex.el --- functions implementing the Ex commands for Viper
be010748 2
5fd6d89f
TTN
3;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003,
4;; 2004, 2005 Free Software Foundation, Inc.
6c2e12f4 5
50a07e18 6;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
02f34c70 7
6c2e12f4
KH
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
52fa07ba 21;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
22;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23;; Boston, MA 02110-1301, USA.
6c2e12f4 24
60370d40
PJ
25;;; Commentary:
26
27;;; Code:
03fc1246 28
9b70a748 29(provide 'viper-ex)
6c2e12f4 30
03fc1246 31;; Compiler pacifier
52fa07ba 32(defvar read-file-name-map)
8626cfa2
MK
33(defvar viper-use-register)
34(defvar viper-s-string)
35(defvar viper-shift-width)
36(defvar viper-ex-history)
37(defvar viper-related-files-and-buffers-ring)
38(defvar viper-local-search-start-marker)
1e70790f 39(defvar viper-expert-level)
8626cfa2
MK
40(defvar viper-custom-file-name)
41(defvar viper-case-fold-search)
e36a387d 42(defvar explicit-shell-file-name)
50a07e18 43(defvar compile-command)
9b70a748 44
726e270f
MK
45;; loading happens only in non-interactive compilation
46;; in order to spare non-viperized emacs from being viperized
47(if noninteractive
48 (eval-when-compile
49 (let ((load-path (cons (expand-file-name ".") load-path)))
50 (or (featurep 'viper-util)
51 (load "viper-util.el" nil nil 'nosuffix))
52 (or (featurep 'viper-keym)
53 (load "viper-keym.el" nil nil 'nosuffix))
54 (or (featurep 'viper-cmd)
55 (load "viper-cmd.el" nil nil 'nosuffix))
56 )))
9b70a748
MK
57;; end pacifier
58
59(require 'viper-util)
60
1e70790f 61(defgroup viper-ex nil
87c81c89 62 "Viper support for Ex commands."
1e70790f
MK
63 :prefix "ex-"
64 :group 'viper)
65
66
03fc1246 67
6c2e12f4 68;;; Variables
bbe6126c 69
8626cfa2 70(defconst viper-ex-work-buf-name " *ex-working-space*")
e07ff078 71(defvar viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 72(defconst viper-ex-tmp-buf-name " *ex-tmp*")
241d963d 73(defconst viper-ex-print-buf-name " *ex-print*")
e07ff078 74(defvar viper-ex-print-buf (get-buffer-create viper-ex-print-buf-name))
52fa07ba
MK
75
76
4986c2c6
MK
77;;; ex-commands...
78
79(defun ex-cmd-obsolete (name)
80 (error "`%s': Obsolete command, not supported by Viper" name))
81
82(defun ex-cmd-not-yet (name)
83 (error "`%s': Command not implemented in Viper" name))
84
85;; alist entries: name (in any order), command, cont(??)
86;; If command is a string, then that is an alias to the real command
87;; to execute (for instance, ":m" -> ":move").
88;; command attributes:
89;; is-mashed: the command's args may be jammed right up against the command
90;; one-letter: this is a one-letter token. Any text appearing after
91;; the name gets appended as an argument for the command
92;; i.e. ":kabc" gets turned into (ex-mark "abc")
93(defconst ex-token-alist '(
94 ("!" (ex-command))
95 ("&" (ex-substitute t))
96 ("=" (ex-line-no))
97 (">" (ex-line "right"))
98 ("<" (ex-line "left"))
99 ("Buffer" (if ex-cycle-other-window
100 (viper-switch-to-buffer)
101 (viper-switch-to-buffer-other-window)))
102 ("Next" (ex-next (not ex-cycle-other-window)))
103 ("PreviousRelatedFile" (ex-next-related-buffer -1))
104 ("RelatedFile" (ex-next-related-buffer 1))
105 ("W" "Write")
106 ("WWrite" (save-some-buffers t))
107 ("Write" (save-some-buffers))
108 ("a" "append")
109 ("args" (ex-args))
110 ("buffer" (if ex-cycle-other-window
111 (viper-switch-to-buffer-other-window)
112 (viper-switch-to-buffer)))
113 ("c" "change")
114 ;; ch should be "change" but maintain old viper compatibility
115 ("ch" "chdir")
116 ("cd" (ex-cd))
117 ("chdir" (ex-cd))
118 ("copy" (ex-copy nil))
119 ("customize" (customize-group "viper"))
120 ("delete" (ex-delete))
121 ("edit" (ex-edit))
b9fe4732 122 ("file" (ex-set-visited-file-name))
4986c2c6
MK
123 ("g" "global")
124 ("global" (ex-global nil) is-mashed)
125 ("goto" (ex-goto))
126 ("help" (ex-help))
127 ("join" (ex-line "join"))
128 ("k" (ex-mark) one-letter)
129 ("kmark" (ex-mark))
130 ("m" "move")
4960e757 131 ("make" (ex-compile))
4986c2c6
MK
132 ; old viper doesn't specify a default for "ma" so leave it undefined
133 ("map" (ex-map))
134 ("mark" (ex-mark))
135 ("move" (ex-copy t))
136 ("next" (ex-next ex-cycle-other-window))
137 ("p" "print")
138 ("preserve" (ex-preserve))
241d963d 139 ("print" (ex-print))
4986c2c6
MK
140 ("put" (ex-put))
141 ("pwd" (ex-pwd))
142 ("quit" (ex-quit))
143 ("r" "read")
144 ("re" "read")
145 ("read" (ex-read))
146 ("recover" (ex-recover))
147 ("rewind" (ex-rewind))
148 ("s" "substitute")
149 ("su" "substitute")
150 ("sub" "substitute")
151 ("set" (ex-set))
152 ("shell" (ex-shell))
153 ("source" (ex-source))
a1506d29 154 ("stop" (suspend-emacs))
4986c2c6
MK
155 ("sr" (ex-substitute t t))
156 ("submitReport" (viper-submit-report))
157 ("substitute" (ex-substitute) is-mashed)
158 ("suspend" (suspend-emacs))
159 ("t" "transfer")
160 ("tag" (ex-tag))
161 ("transfer" (ex-copy nil))
162 ("u" "undo")
163 ("un" "undo")
164 ("undo" (viper-undo))
165 ("unmap" (ex-unmap))
166 ("v" "vglobal")
167 ("version" (viper-version))
168 ("vglobal" (ex-global t) is-mashed)
a1506d29 169 ("visual" (ex-edit))
4986c2c6
MK
170 ("w" "write")
171 ("wq" (ex-write t))
172 ("write" (ex-write nil))
173 ("xit" (ex-write t))
174 ("yank" (ex-yank))
175 ("~" (ex-substitute t t))
176
177 ("append" (ex-cmd-obsolete "append"))
178 ("change" (ex-cmd-obsolete "change"))
179 ("insert" (ex-cmd-obsolete "insert"))
180 ("open" (ex-cmd-obsolete "open"))
181
182 ("list" (ex-cmd-not-yet "list"))
4986c2c6
MK
183 ("z" (ex-cmd-not-yet "z"))
184 ("#" (ex-cmd-not-yet "#"))
185
186 ("abbreviate" (error "`%s': Vi abbreviations are obsolete. Use the more powerful Emacs abbrevs" ex-token))
187 ("unabbreviate" (error "`%s': Vi abbreviations are obsolete. Use the more powerful Emacs abbrevs" ex-token))
188 ))
189
190;; No code should touch anything in the alist entry! (other than the name,
191;; "car entry", of course) This way, changing this data structure
192;; requires changing only the following ex-cmd functions...
193
194;; Returns cmd if the command may be jammed right up against its
195;; arguments, nil if there must be a space.
196;; examples of mashable commands: g// g!// v// s// sno// sm//
197(defun ex-cmd-is-mashed-with-args (cmd)
198 (if (eq 'is-mashed (car (nthcdr 2 cmd))) cmd))
199
200;; Returns true if this is a one-letter command that may be followed
201;; by anything, no whitespace needed. This is a special-case for ":k".
202(defun ex-cmd-is-one-letter (cmd)
203 (if (eq 'one-letter (car (nthcdr 2 cmd))) cmd))
204
205;; Executes the function associated with the command
206(defun ex-cmd-execute (cmd)
207 (eval (cadr cmd)))
208
209;; If this is a one-letter magic command, splice in args.
210(defun ex-splice-args-in-1-letr-cmd (key list)
211 (let ((onelet (ex-cmd-is-one-letter (assoc (substring key 0 1) list))))
212 (if onelet
213 (list key
214 (append (cadr onelet)
215 (if (< 1 (length key)) (list (substring key 1))))
216 (caddr onelet)))
217 ))
218
219
220;; Returns the alist entry for the appropriate key.
221;; Tries to complete the key before using it in the alist.
222;; If there is no appropriate key (no match or duplicate matches) return nil
223(defun ex-cmd-assoc (key list)
224 (let ((entry (try-completion key list))
4960e757 225 result)
4986c2c6
MK
226 (setq result (cond
227 ((eq entry t) (assoc key list))
228 ((stringp entry) (or (ex-splice-args-in-1-letr-cmd key list)
229 (assoc entry list)))
230 ((eq entry nil) (ex-splice-args-in-1-letr-cmd key list))
231 (t nil)
232 ))
233 ;; If we end up with an alias, look up the alias...
234 (if (stringp (cadr result))
235 (setq result (ex-cmd-assoc (cadr result) list)))
236 ;; and return the corresponding alist entry
237 result
238 ))
239
52fa07ba
MK
240
241;; A-list of Ex variables that can be set using the :set command.
a1506d29 242(defconst ex-variable-alist
52fa07ba 243 '(("wrapscan") ("ws") ("wrapmargin") ("wm")
e36a387d 244 ("tabstop-global") ("ts-g") ("tabstop") ("ts")
52fa07ba 245 ("showmatch") ("sm") ("shiftwidth") ("sw") ("shell") ("sh")
a1506d29 246 ("readonly") ("ro")
52fa07ba
MK
247 ("nowrapscan") ("nows") ("noshowmatch") ("nosm")
248 ("noreadonly") ("noro") ("nomagic") ("noma")
249 ("noignorecase") ("noic")
e36a387d 250 ("noautoindent-global") ("noai-g") ("noautoindent") ("noai")
52fa07ba 251 ("magic") ("ma") ("ignorecase") ("ic")
a1506d29
JB
252 ("autoindent-global") ("ai-g") ("autoindent") ("ai")
253 ("all")
52fa07ba 254 ))
bbe6126c 255
a1506d29 256
bbe6126c 257
52fa07ba
MK
258;; Token recognized during parsing of Ex commands (e.g., "read", "comma")
259(defvar ex-token nil)
260
a1506d29 261;; Type of token.
52fa07ba
MK
262;; If non-nil, gives type of address; if nil, it is a command.
263(defvar ex-token-type nil)
264
265;; List of addresses passed to Ex command
266(defvar ex-addresses nil)
267
8e41a31c 268;; This flag is supposed to be set only by `#', `print', and `list',
3af0304a
MK
269;; none of which is implemented. So, it and the pices of the code it
270;; controls are dead weight. We keep it just in case this might be
8e41a31c 271;; needed in the future.
52fa07ba
MK
272(defvar ex-flag nil)
273
274;; "buffer" where Ex commands keep deleted data.
275;; In Emacs terms, this is a register.
276(defvar ex-buffer nil)
277
278;; Value of ex count.
279(defvar ex-count nil)
280
1e70790f 281;; Flag indicating that :global Ex command is being executed.
52fa07ba 282(defvar ex-g-flag nil)
1e70790f 283;; Flag indicating that :vglobal Ex command is being executed.
52fa07ba 284(defvar ex-g-variant nil)
241d963d
MK
285;; Marks to operate on during a :global Ex command.
286(defvar ex-g-marks nil)
52fa07ba
MK
287
288;; Save reg-exp used in substitute.
289(defvar ex-reg-exp nil)
290
291
292;; Replace pattern for substitute.
293(defvar ex-repl nil)
294
295;; Pattern for global command.
296(defvar ex-g-pat nil)
297
1e70790f 298(defcustom ex-unix-type-shell
52fa07ba
MK
299 (let ((case-fold-search t))
300 (and (stringp shell-file-name)
301 (string-match
302 (concat
303 "\\("
304 "csh$\\|csh.exe$"
305 "\\|"
306 "ksh$\\|ksh.exe$"
307 "\\|"
308 "^sh$\\|sh.exe$"
309 "\\|"
310 "[^a-z]sh$\\|[^a-z]sh.exe$"
311 "\\|"
312 "bash$\\|bash.exe$"
313 "\\)")
314 shell-file-name)))
1e70790f 315 "Is the user using a unix-type shell under a non-OS?"
5ee3742d 316 :type 'boolean
1e70790f 317 :group 'viper-ex)
52fa07ba 318
1e70790f 319(defcustom ex-unix-type-shell-options
52fa07ba
MK
320 (let ((case-fold-search t))
321 (if ex-unix-type-shell
322 (cond ((string-match "\\(csh$\\|csh.exe$\\)" shell-file-name)
323 "-f") ; csh: do it fast
324 ((string-match "\\(bash$\\|bash.exe$\\)" shell-file-name)
325 "-noprofile") ; bash: ignore .profile
326 )))
a1506d29 327 "Options to pass to the Unix-style shell.
1e70790f 328Don't put `-c' here, as it is added automatically."
ee6a3436 329 :type '(choice (const nil) string)
1e70790f 330 :group 'viper-ex)
52fa07ba 331
4960e757 332(defcustom ex-compile-command "make"
c760d040 333 "The command to run when the user types :make."
4960e757
MK
334 :type 'string
335 :group 'viper-ex)
336
3af0304a
MK
337(defcustom viper-glob-function
338 (cond (ex-unix-type-shell 'viper-glob-unix-files)
339 ((eq system-type 'emx) 'viper-glob-mswindows-files) ; OS/2
340 (viper-ms-style-os-p 'viper-glob-mswindows-files) ; Microsoft OS
341 (viper-vms-os-p 'viper-glob-unix-files) ; VMS
342 (t 'viper-glob-unix-files) ; presumably UNIX
343 )
344 "Expand the file spec containing wildcard symbols.
345The default tries to set this variable to work with Unix, Windows,
346OS/2, and VMS.
347
a1506d29 348However, if it doesn't work right for some types of Unix shells or some OS,
3af0304a
MK
349the user should supply the appropriate function and set this variable to the
350corresponding function symbol."
351 :type 'symbol
352 :group 'viper-ex)
353
bbe6126c 354
52fa07ba
MK
355;; Remembers the previous Ex tag.
356(defvar ex-tag nil)
bbe6126c 357
52fa07ba
MK
358;; file used by Ex commands like :r, :w, :n
359(defvar ex-file nil)
bbe6126c 360
52fa07ba
MK
361;; If t, tells Ex that this is a variant-command, i.e., w>>, r!, etc.
362(defvar ex-variant nil)
bbe6126c 363
52fa07ba
MK
364;; Specified the offset of an Ex command, such as :read.
365(defvar ex-offset nil)
bbe6126c 366
52fa07ba
MK
367;; Tells Ex that this is a w>> command.
368(defvar ex-append nil)
bbe6126c 369
52fa07ba
MK
370;; File containing the shell command to be executed at Ex prompt,
371;; e.g., :r !date
372(defvar ex-cmdfile nil)
328b4b70 373(defvar ex-cmdfile-args "")
a1506d29 374
8626cfa2 375;; flag used in viper-ex-read-file-name to indicate that we may be reading
3af0304a 376;; multiple file names. Used for :edit and :next
8626cfa2 377(defvar viper-keep-reading-filename nil)
bbe6126c 378
1e70790f 379(defcustom ex-cycle-other-window t
52fa07ba 380 "*If t, :n and :b cycles through files and buffers in other window.
3af0304a 381Then :N and :B cycles in the current window. If nil, this behavior is
1e70790f
MK
382reversed."
383 :type 'boolean
384 :group 'viper-ex)
bbe6126c 385
1e70790f
MK
386(defcustom ex-cycle-through-non-files nil
387 "*Cycle through *scratch* and other buffers that don't visit any file."
388 :type 'boolean
389 :group 'viper-ex)
bbe6126c 390
52fa07ba 391;; Last shell command executed with :! command.
8626cfa2 392(defvar viper-ex-last-shell-com nil)
a1506d29 393
52fa07ba 394;; Indicates if Minibuffer was exited temporarily in Ex-command.
8626cfa2 395(defvar viper-incomplete-ex-cmd nil)
a1506d29 396
52fa07ba 397;; Remembers the last ex-command prompt.
8626cfa2 398(defvar viper-last-ex-prompt "")
bbe6126c 399
bbe6126c 400
52fa07ba 401;; Get a complete ex command
8626cfa2 402(defun viper-get-ex-com-subr ()
4986c2c6 403 (let (cmd case-fold-search)
52fa07ba
MK
404 (set-mark (point))
405 (re-search-forward "[a-zA-Z][a-zA-Z]*")
406 (setq ex-token-type 'command)
407 (setq ex-token (buffer-substring (point) (mark t)))
4986c2c6
MK
408 (setq cmd (ex-cmd-assoc ex-token ex-token-alist))
409 (if cmd
410 (setq ex-token (car cmd))
411 (setq ex-token-type 'non-command))
52fa07ba 412 ))
bbe6126c 413
52fa07ba
MK
414;; Get an ex-token which is either an address or a command.
415;; A token has a type, \(command, address, end-mark\), and a value
8626cfa2 416(defun viper-get-ex-token ()
52fa07ba 417 (save-window-excursion
a1506d29 418 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 419 (set-buffer viper-ex-work-buf)
52fa07ba 420 (skip-chars-forward " \t|")
96dffd25
MK
421 (let ((case-fold-search t))
422 (cond ((looking-at "#")
423 (setq ex-token-type 'command)
424 (setq ex-token (char-to-string (following-char)))
425 (forward-char 1))
426 ((looking-at "[a-z]") (viper-get-ex-com-subr))
427 ((looking-at "\\.")
428 (forward-char 1)
429 (setq ex-token-type 'dot))
430 ((looking-at "[0-9]")
431 (set-mark (point))
432 (re-search-forward "[0-9]*")
433 (setq ex-token-type
434 (cond ((eq ex-token-type 'plus) 'add-number)
435 ((eq ex-token-type 'minus) 'sub-number)
436 (t 'abs-number)))
437 (setq ex-token
027a4b6b 438 (string-to-number (buffer-substring (point) (mark t)))))
96dffd25
MK
439 ((looking-at "\\$")
440 (forward-char 1)
441 (setq ex-token-type 'end))
442 ((looking-at "%")
443 (forward-char 1)
444 (setq ex-token-type 'whole))
445 ((looking-at "+")
446 (cond ((or (looking-at "+[-+]") (looking-at "+[\n|]"))
447 (forward-char 1)
448 (insert "1")
449 (backward-char 1)
52fa07ba 450 (setq ex-token-type 'plus))
96dffd25
MK
451 ((looking-at "+[0-9]")
452 (forward-char 1)
453 (setq ex-token-type 'plus))
454 (t
455 (error viper-BadAddress))))
456 ((looking-at "-")
457 (cond ((or (looking-at "-[-+]") (looking-at "-[\n|]"))
458 (forward-char 1)
459 (insert "1")
460 (backward-char 1)
461 (setq ex-token-type 'minus))
462 ((looking-at "-[0-9]")
463 (forward-char 1)
464 (setq ex-token-type 'minus))
465 (t
466 (error viper-BadAddress))))
467 ((looking-at "/")
468 (forward-char 1)
469 (set-mark (point))
470 (let ((cont t))
471 (while (and (not (eolp)) cont)
472 ;;(re-search-forward "[^/]*/")
473 (re-search-forward "[^/]*\\(/\\|\n\\)")
474 (if (not (viper-looking-back "[^\\\\]\\(\\\\\\\\\\)*\\\\/"))
475 (setq cont nil))))
476 (backward-char 1)
477 (setq ex-token (buffer-substring (point) (mark t)))
478 (if (looking-at "/") (forward-char 1))
479 (setq ex-token-type 'search-forward))
480 ((looking-at "\\?")
481 (forward-char 1)
482 (set-mark (point))
483 (let ((cont t))
484 (while (and (not (eolp)) cont)
485 ;;(re-search-forward "[^\\?]*\\?")
486 (re-search-forward "[^\\?]*\\(\\?\\|\n\\)")
487 (if (not (viper-looking-back "[^\\\\]\\(\\\\\\\\\\)*\\\\\\?"))
488 (setq cont nil))
489 (backward-char 1)
490 (if (not (looking-at "\n")) (forward-char 1))))
491 (setq ex-token-type 'search-backward)
492 (setq ex-token (buffer-substring (1- (point)) (mark t))))
493 ((looking-at ",")
494 (forward-char 1)
495 (setq ex-token-type 'comma))
496 ((looking-at ";")
497 (forward-char 1)
498 (setq ex-token-type 'semi-colon))
499 ((looking-at "[!=><&~]")
500 (setq ex-token-type 'command)
501 (setq ex-token (char-to-string (following-char)))
502 (forward-char 1))
503 ((looking-at "'")
504 (setq ex-token-type 'goto-mark)
505 (forward-char 1)
506 (cond ((looking-at "'") (setq ex-token nil))
507 ((looking-at "[a-z]") (setq ex-token (following-char)))
508 (t (error "Marks are ' and a-z")))
509 (forward-char 1))
510 ((looking-at "\n")
511 (setq ex-token-type 'end-mark)
512 (setq ex-token "goto"))
513 (t
514 (error viper-BadExCommand))))))
bbe6126c 515
3af0304a
MK
516;; Reads Ex command. Tries to determine if it has to exit because command
517;; is complete or invalid. If not, keeps reading command.
52fa07ba
MK
518(defun ex-cmd-read-exit ()
519 (interactive)
8626cfa2 520 (setq viper-incomplete-ex-cmd t)
52fa07ba
MK
521 (let ((quit-regex1 (concat
522 "\\(" "set[ \t]*"
523 "\\|" "edit[ \t]*"
524 "\\|" "[nN]ext[ \t]*"
525 "\\|" "unm[ \t]*"
526 "\\|" "^[ \t]*rep"
527 "\\)"))
528 (quit-regex2 (concat
529 "[a-zA-Z][ \t]*"
530 "\\(" "!" "\\|" ">>"
531 "\\|" "\\+[0-9]+"
532 "\\)"
533 "*[ \t]*$"))
534 (stay-regex (concat
535 "\\(" "^[ \t]*$"
1e70790f 536 "\\|" "[?/].*"
52fa07ba
MK
537 "\\|" "[ktgjmsz][ \t]*$"
538 "\\|" "^[ \t]*ab.*"
539 "\\|" "tr[ansfer \t]*"
540 "\\|" "sr[ \t]*"
541 "\\|" "mo.*"
542 "\\|" "^[ \t]*k?ma[^p]*"
543 "\\|" "^[ \t]*fi.*"
544 "\\|" "v?gl.*"
545 "\\|" "[vg][ \t]*$"
546 "\\|" "jo.*"
547 "\\|" "^[ \t]*ta.*"
548 "\\|" "^[ \t]*una.*"
2eb4bdca
MK
549 ;; don't jump up in :s command
550 "\\|" "^[ \t]*\\([`'][a-z]\\|[.,%]\\)*[ \t]*su.*"
551 "\\|" "^[ \t]*\\([`'][a-z]\\|[.,%]\\)*[ \t]*s[^a-z].*"
328b4b70
MK
552 "\\|" "['`][a-z][ \t]*"
553 ;; r! assumes that the next one is a shell command
554 "\\|" "\\(r\\|re\\|rea\\|read\\)[ \t]*!"
555 ;; w ! assumes that the next one is a shell command
556 "\\|" "\\(w\\|wr\\|wri\\|writ.?\\)[ \t]+!"
52fa07ba
MK
557 "\\|" "![ \t]*[a-zA-Z].*"
558 "\\)"
559 "!*")))
a1506d29 560
52fa07ba 561 (save-window-excursion ;; put cursor at the end of the Ex working buffer
a1506d29 562 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 563 (set-buffer viper-ex-work-buf)
52fa07ba 564 (goto-char (point-max)))
8626cfa2
MK
565 (cond ((viper-looking-back quit-regex1) (exit-minibuffer))
566 ((viper-looking-back stay-regex) (insert " "))
567 ((viper-looking-back quit-regex2) (exit-minibuffer))
52fa07ba 568 (t (insert " ")))))
a1506d29 569
52fa07ba
MK
570;; complete Ex command
571(defun ex-cmd-complete ()
572 (interactive)
573 (let (save-pos dist compl-list string-to-complete completion-result)
a1506d29 574
52fa07ba
MK
575 (save-excursion
576 (setq dist (skip-chars-backward "[a-zA-Z!=>&~]")
577 save-pos (point)))
a1506d29 578
52fa07ba 579 (if (or (= dist 0)
8626cfa2
MK
580 (viper-looking-back "\\([ \t]*['`][ \t]*[a-z]*\\)")
581 (viper-looking-back
3af0304a 582 "^[ \t]*[a-zA-Z!=>&~][ \t]*[/?]*[ \t]+[a-zA-Z!=>&~]+"))
52fa07ba
MK
583 ;; Preceding characters are not the ones allowed in an Ex command
584 ;; or we have typed past command name.
3af0304a 585 ;; Note: we didn't do parsing, so there can be surprises.
8626cfa2
MK
586 (if (or (viper-looking-back "[a-zA-Z!=>&~][ \t]*[/?]*[ \t]*")
587 (viper-looking-back "\\([ \t]*['`][ \t]*[a-z]*\\)")
52fa07ba
MK
588 (looking-at "[^ \t\n\C-m]"))
589 nil
a1506d29 590 (with-output-to-temp-buffer "*Completions*"
52fa07ba 591 (display-completion-list
8626cfa2 592 (viper-alist-to-list ex-token-alist))))
52fa07ba
MK
593 ;; Preceding chars may be part of a command name
594 (setq string-to-complete (buffer-substring save-pos (point)))
595 (setq completion-result
596 (try-completion string-to-complete ex-token-alist))
a1506d29 597
52fa07ba 598 (cond ((eq completion-result t) ; exact match--do nothing
8626cfa2 599 (viper-tmp-insert-at-eob " (Sole completion)"))
52fa07ba 600 ((eq completion-result nil)
8626cfa2 601 (viper-tmp-insert-at-eob " (No match)"))
52fa07ba
MK
602 (t ;; partial completion
603 (goto-char save-pos)
604 (delete-region (point) (point-max))
605 (insert completion-result)
606 (let (case-fold-search)
607 (setq compl-list
8626cfa2 608 (viper-filter-alist (concat "^" completion-result)
52fa07ba
MK
609 ex-token-alist)))
610 (if (> (length compl-list) 1)
a1506d29 611 (with-output-to-temp-buffer "*Completions*"
52fa07ba 612 (display-completion-list
8626cfa2 613 (viper-alist-to-list (reverse compl-list)))))))
52fa07ba 614 )))
bbe6126c 615
a1506d29
JB
616
617;; Read Ex commands
3af0304a
MK
618;; ARG is a prefix argument. If given, the ex command runs on the region
619;;(without the user having to specify the address :a,b
620;; STRING is the command to execute. If nil, then Viper asks you to enter the
a1506d29 621;; command.
c004db97
MK
622(defun viper-ex (arg &optional string)
623 (interactive "P")
52fa07ba
MK
624 (or string
625 (setq ex-g-flag nil
626 ex-g-variant nil))
627 (let* ((map (copy-keymap minibuffer-local-map))
628 (address nil)
629 (cont t)
630 (dot (point))
c004db97
MK
631 reg-beg-line reg-end-line
632 reg-beg reg-end
633 initial-str
52fa07ba 634 prev-token-type com-str)
8626cfa2 635 (viper-add-keymap viper-ex-cmd-map map)
c004db97
MK
636
637 (if arg
638 (progn
639 (viper-enlarge-region (mark t) (point))
640 (if (> (point) (mark t))
641 (setq reg-beg (mark t)
642 reg-end (point))
643 (setq reg-end (mark t)
644 reg-beg (point)))
645 (save-excursion
646 (goto-char reg-beg)
647 (setq reg-beg-line (1+ (count-lines (point-min) (point)))
648 reg-end-line
649 (+ reg-beg-line (count-lines reg-beg reg-end) -1)))))
650 (if reg-beg-line
651 (setq initial-str (format "%d,%d" reg-beg-line reg-end-line)))
a1506d29
JB
652
653 (setq com-str
c004db97 654 (or string (viper-read-string-with-history
a1506d29 655 ":"
c004db97
MK
656 initial-str
657 'viper-ex-history
658 ;; no default when working on region
659 (if initial-str
4960e757 660 nil
c004db97
MK
661 (car viper-ex-history))
662 map
663 (if initial-str
664 " [Type command to execute on current region]"))))
52fa07ba
MK
665 (save-window-excursion
666 ;; just a precaution
a1506d29 667 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 668 (set-buffer viper-ex-work-buf)
52fa07ba
MK
669 (delete-region (point-min) (point-max))
670 (insert com-str "\n")
671 (goto-char (point-min)))
672 (setq ex-token-type nil
673 ex-addresses nil)
674 (while cont
8626cfa2 675 (viper-get-ex-token)
52fa07ba
MK
676 (cond ((memq ex-token-type '(command end-mark))
677 (if address (setq ex-addresses (cons address ex-addresses)))
4986c2c6
MK
678 (viper-deactivate-mark)
679 (let ((cmd (ex-cmd-assoc ex-token ex-token-alist)))
680 (if (null cmd)
681 (error "`%s': %s" ex-token viper-BadExCommand))
682 (ex-cmd-execute cmd)
683 (if (or (ex-cmd-is-mashed-with-args cmd)
684 (ex-cmd-is-one-letter cmd))
685 (setq cont nil)
686 (save-excursion
687 (save-window-excursion
688 (setq viper-ex-work-buf
689 (get-buffer-create viper-ex-work-buf-name))
690 (set-buffer viper-ex-work-buf)
691 (skip-chars-forward " \t")
692 (cond ((looking-at "|")
693 (forward-char 1))
694 ((looking-at "\n")
695 (setq cont nil))
696 (t (error
697 "`%s': %s" ex-token viper-SpuriousText)))
698 )))
699 ))
52fa07ba 700 ((eq ex-token-type 'non-command)
8626cfa2 701 (error "`%s': %s" ex-token viper-BadExCommand))
52fa07ba
MK
702 ((eq ex-token-type 'whole)
703 (setq address nil)
704 (setq ex-addresses
705 (if ex-addresses
706 (cons (point-max) ex-addresses)
707 (cons (point-max) (cons (point-min) ex-addresses)))))
708 ((eq ex-token-type 'comma)
709 (if (eq prev-token-type 'whole)
710 (setq address (point-min)))
711 (setq ex-addresses
712 (cons (if (null address) (point) address) ex-addresses)))
713 ((eq ex-token-type 'semi-colon)
714 (if (eq prev-token-type 'whole)
715 (setq address (point-min)))
716 (if address (setq dot address))
717 (setq ex-addresses
718 (cons (if (null address) (point) address) ex-addresses)))
8626cfa2 719 (t (let ((ans (viper-get-ex-address-subr address dot)))
52fa07ba
MK
720 (if ans (setq address ans)))))
721 (setq prev-token-type ex-token-type))))
a1506d29 722
bbe6126c 723
52fa07ba 724;; Get a regular expression and set `ex-variant', if found
2d341681
MK
725;; Viper doesn't parse the substitution or search patterns.
726;; In particular, it doesn't expand ~ into the last substitution.
8626cfa2 727(defun viper-get-ex-pat ()
52fa07ba 728 (save-window-excursion
8626cfa2
MK
729 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
730 (set-buffer viper-ex-work-buf)
52fa07ba
MK
731 (skip-chars-forward " \t")
732 (if (looking-at "!")
2eb4bdca 733 ;; this is probably a variant command r!
52fa07ba
MK
734 (progn
735 (setq ex-g-variant (not ex-g-variant)
736 ex-g-flag (not ex-g-flag))
737 (forward-char 1)
738 (skip-chars-forward " \t")))
739 (let ((c (following-char)))
2eb4bdca
MK
740 (cond ((string-match "[0-9A-Za-z]" (format "%c" c))
741 (error
742 "Global regexp must be inside matching non-alphanumeric chars"))
743 ((= c ??) (error "`?' is not an allowed pattern delimiter here")))
52fa07ba
MK
744 (if (looking-at "[^\\\\\n]")
745 (progn
746 (forward-char 1)
747 (set-mark (point))
748 (let ((cont t))
2eb4bdca
MK
749 ;; the use of eobp instead of eolp permits the use of newlines in
750 ;; pat2 in s/pat1/pat2/
751 (while (and (not (eobp)) cont)
52fa07ba
MK
752 (if (not (re-search-forward (format "[^%c]*%c" c c) nil t))
753 (if (member ex-token '("global" "vglobal"))
2eb4bdca 754 (error "Missing closing delimiter for global regexp")
52fa07ba 755 (goto-char (point-max))))
8626cfa2 756 (if (not (viper-looking-back
52fa07ba 757 (format "[^\\\\]\\(\\\\\\\\\\)*\\\\%c" c)))
2eb4bdca
MK
758 (setq cont nil)
759 ;; we are at an escaped delimiter: unescape it and continue
760 (delete-backward-char 2)
761 (insert c)
762 (if (eolp)
763 ;; if at eol, exit loop and go to next line
764 ;; later, delim will be inserted at the end
765 (progn
766 (setq cont nil)
767 (forward-char))))
768 ))
52fa07ba
MK
769 (setq ex-token
770 (if (= (mark t) (point)) ""
771 (buffer-substring (1- (point)) (mark t))))
1e70790f 772 (backward-char 1)
2eb4bdca 773 ;; if the user didn't insert the final pattern delimiter, we're
3af0304a 774 ;; at newline now. In this case, insert the initial delimiter
1e70790f 775 ;; specified in variable c
2eb4bdca 776 (if (eolp)
1e70790f 777 (progn
2eb4bdca
MK
778 (insert c)
779 (backward-char 1)))
1e70790f 780 )
52fa07ba
MK
781 (setq ex-token nil))
782 c)))
bbe6126c 783
52fa07ba 784;; Get an Ex option g or c
8626cfa2 785(defun viper-get-ex-opt-gc (c)
52fa07ba 786 (save-window-excursion
a1506d29 787 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 788 (set-buffer viper-ex-work-buf)
52fa07ba
MK
789 (if (looking-at (format "%c" c)) (forward-char 1))
790 (skip-chars-forward " \t")
791 (cond ((looking-at "g")
792 (setq ex-token "g")
793 (forward-char 1)
794 t)
795 ((looking-at "c")
796 (setq ex-token "c")
797 (forward-char 1)
798 t)
799 (t nil))))
800
801;; Compute default addresses. WHOLE-FLAG means use the whole buffer
8626cfa2 802(defun viper-default-ex-addresses (&optional whole-flag)
52fa07ba
MK
803 (cond ((null ex-addresses)
804 (setq ex-addresses
805 (if whole-flag
2eb4bdca
MK
806 (list (point-max) (point-min))
807 (list (point) (point)))))
52fa07ba
MK
808 ((null (cdr ex-addresses))
809 (setq ex-addresses
810 (cons (car ex-addresses) ex-addresses)))))
811
812;; Get an ex-address as a marker and set ex-flag if a flag is found
8626cfa2 813(defun viper-get-ex-address ()
9b70a748
MK
814 (let ((address (point-marker))
815 (cont t))
52fa07ba
MK
816 (setq ex-token "")
817 (setq ex-flag nil)
818 (while cont
8626cfa2 819 (viper-get-ex-token)
52fa07ba
MK
820 (cond ((eq ex-token-type 'command)
821 (if (member ex-token '("print" "list" "#"))
822 (progn
823 (setq ex-flag t
824 cont nil))
825 (error "Address expected in this Ex command")))
826 ((eq ex-token-type 'end-mark)
827 (setq cont nil))
828 ((eq ex-token-type 'whole)
829 (error "Trailing address expected"))
830 ((eq ex-token-type 'comma)
8626cfa2
MK
831 (error "`%s': %s" ex-token viper-SpuriousText))
832 (t (let ((ans (viper-get-ex-address-subr address (point-marker))))
52fa07ba
MK
833 (if ans (setq address ans))))))
834 address))
835
836;; Returns an address as a point
8626cfa2 837(defun viper-get-ex-address-subr (old-address dot)
52fa07ba
MK
838 (let ((address nil))
839 (if (null old-address) (setq old-address dot))
840 (cond ((eq ex-token-type 'dot)
841 (setq address dot))
842 ((eq ex-token-type 'add-number)
843 (save-excursion
844 (goto-char old-address)
845 (forward-line (if (= old-address 0) (1- ex-token) ex-token))
846 (setq address (point-marker))))
847 ((eq ex-token-type 'sub-number)
848 (save-excursion
849 (goto-char old-address)
850 (forward-line (- ex-token))
851 (setq address (point-marker))))
852 ((eq ex-token-type 'abs-number)
853 (save-excursion
854 (goto-char (point-min))
855 (if (= ex-token 0) (setq address 0)
856 (forward-line (1- ex-token))
857 (setq address (point-marker)))))
858 ((eq ex-token-type 'end)
2d341681
MK
859 (save-excursion
860 (goto-char (1- (point-max)))
861 (setq address (point-marker))))
52fa07ba
MK
862 ((eq ex-token-type 'plus) t) ; do nothing
863 ((eq ex-token-type 'minus) t) ; do nothing
864 ((eq ex-token-type 'search-forward)
865 (save-excursion
866 (ex-search-address t)
867 (setq address (point-marker))))
868 ((eq ex-token-type 'search-backward)
869 (save-excursion
870 (ex-search-address nil)
871 (setq address (point-marker))))
872 ((eq ex-token-type 'goto-mark)
873 (save-excursion
874 (if (null ex-token)
875 (exchange-point-and-mark)
a1506d29 876 (goto-char
4960e757
MK
877 (viper-register-to-point
878 (viper-int-to-char (1+ (- ex-token ?a))) 'enforce-buffer)))
52fa07ba
MK
879 (setq address (point-marker)))))
880 address))
881
882
883;; Search pattern and set address
2d341681 884;; Doesn't wrap around. Should it?
52fa07ba
MK
885(defun ex-search-address (forward)
886 (if (string= ex-token "")
8626cfa2
MK
887 (if (null viper-s-string)
888 (error viper-NoPrevSearch)
889 (setq ex-token viper-s-string))
890 (setq viper-s-string ex-token))
52fa07ba
MK
891 (if forward
892 (progn
893 (forward-line 1)
894 (re-search-forward ex-token))
895 (forward-line -1)
896 (re-search-backward ex-token)))
897
898;; Get a buffer name and set `ex-count' and `ex-flag' if found
8626cfa2 899(defun viper-get-ex-buffer ()
52fa07ba
MK
900 (setq ex-buffer nil)
901 (setq ex-count nil)
902 (setq ex-flag nil)
903 (save-window-excursion
a1506d29 904 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 905 (set-buffer viper-ex-work-buf)
52fa07ba
MK
906 (skip-chars-forward " \t")
907 (if (looking-at "[a-zA-Z]")
908 (progn
909 (setq ex-buffer (following-char))
910 (forward-char 1)
911 (skip-chars-forward " \t")))
912 (if (looking-at "[0-9]")
913 (progn
914 (set-mark (point))
915 (re-search-forward "[0-9][0-9]*")
027a4b6b 916 (setq ex-count (string-to-number (buffer-substring (point) (mark t))))
52fa07ba
MK
917 (skip-chars-forward " \t")))
918 (if (looking-at "[pl#]")
919 (progn
920 (setq ex-flag t)
921 (forward-char 1)))
922 (if (not (looking-at "[\n|]"))
8626cfa2 923 (error "`%s': %s" ex-token viper-SpuriousText))))
52fa07ba 924
8626cfa2 925(defun viper-get-ex-count ()
52fa07ba
MK
926 (setq ex-variant nil
927 ex-count nil
928 ex-flag nil)
929 (save-window-excursion
a1506d29 930 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 931 (set-buffer viper-ex-work-buf)
52fa07ba
MK
932 (skip-chars-forward " \t")
933 (if (looking-at "!")
934 (progn
935 (setq ex-variant t)
936 (forward-char 1)))
937 (skip-chars-forward " \t")
938 (if (looking-at "[0-9]")
939 (progn
940 (set-mark (point))
941 (re-search-forward "[0-9][0-9]*")
027a4b6b 942 (setq ex-count (string-to-number (buffer-substring (point) (mark t))))
52fa07ba
MK
943 (skip-chars-forward " \t")))
944 (if (looking-at "[pl#]")
945 (progn
946 (setq ex-flag t)
947 (forward-char 1)))
948 (if (not (looking-at "[\n|]"))
949 (error "`%s': %s"
8626cfa2
MK
950 (buffer-substring
951 (point-min) (1- (point-max))) viper-BadExCommand))))
52fa07ba
MK
952
953;; Expand \% and \# in ex command
954(defun ex-expand-filsyms (cmd buf)
955 (let (cf pf ret)
a1506d29 956 (save-excursion
52fa07ba
MK
957 (set-buffer buf)
958 (setq cf buffer-file-name)
959 (setq pf (ex-next nil t))) ; this finds alternative file name
960 (if (and (null cf) (string-match "[^\\]%\\|\\`%" cmd))
961 (error "No current file to substitute for `%%'"))
962 (if (and (null pf) (string-match "[^\\]#\\|\\`#" cmd))
963 (error "No alternate file to substitute for `#'"))
964 (save-excursion
8626cfa2 965 (set-buffer (get-buffer-create viper-ex-tmp-buf-name))
52fa07ba
MK
966 (erase-buffer)
967 (insert cmd)
968 (goto-char (point-min))
969 (while (re-search-forward "%\\|#" nil t)
a1506d29 970 (let ((data (match-data))
52fa07ba 971 (char (buffer-substring (match-beginning 0) (match-end 0))))
8626cfa2 972 (if (viper-looking-back (concat "\\\\" char))
52fa07ba 973 (replace-match char)
2eb4bdca 974 (store-match-data data)
52fa07ba
MK
975 (if (string= char "%")
976 (replace-match cf)
977 (replace-match pf)))))
978 (end-of-line)
979 (setq ret (buffer-substring (point-min) (point)))
980 (message "%s" ret))
981 ret))
982
328b4b70
MK
983;; Get a file name and set `ex-variant', `ex-append' and `ex-offset' if found
984;; If it is r!, then get the command name and whatever args
8626cfa2 985(defun viper-get-ex-file ()
52fa07ba
MK
986 (let (prompt)
987 (setq ex-file nil
988 ex-variant nil
989 ex-append nil
990 ex-offset nil
328b4b70
MK
991 ex-cmdfile nil
992 ex-cmdfile-args "")
52fa07ba
MK
993 (save-excursion
994 (save-window-excursion
a1506d29 995 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 996 (set-buffer viper-ex-work-buf)
52fa07ba
MK
997 (skip-chars-forward " \t")
998 (if (looking-at "!")
8626cfa2 999 (if (and (not (viper-looking-back "[ \t]"))
52fa07ba
MK
1000 ;; read doesn't have a corresponding :r! form, so ! is
1001 ;; immediately interpreted as a shell command.
1002 (not (string= ex-token "read")))
1003 (progn
1004 (setq ex-variant t)
1005 (forward-char 1)
1006 (skip-chars-forward " \t"))
1007 (setq ex-cmdfile t)
1008 (forward-char 1)
1009 (skip-chars-forward " \t")))
1010 (if (looking-at ">>")
1011 (progn
1012 (setq ex-append t
1013 ex-variant t)
1014 (forward-char 2)
1015 (skip-chars-forward " \t")))
1016 (if (looking-at "+")
1017 (progn
1018 (forward-char 1)
1019 (set-mark (point))
1020 (re-search-forward "[ \t\n]")
1021 (backward-char 1)
1022 (setq ex-offset (buffer-substring (point) (mark t)))
1023 (forward-char 1)
1024 (skip-chars-forward " \t")))
1025 ;; this takes care of :r, :w, etc., when they get file names
1026 ;; from the history list
1027 (if (member ex-token '("read" "write" "edit" "visual" "next"))
1028 (progn
1029 (setq ex-file (buffer-substring (point) (1- (point-max))))
1030 (setq ex-file
1031 ;; For :e, match multiple non-white strings separated
3af0304a 1032 ;; by white. For others, find the first non-white string
52fa07ba
MK
1033 (if (string-match
1034 (if (string= ex-token "edit")
1035 "[^ \t\n]+\\([ \t]+[^ \t\n]+\\)*"
1036 "[^ \t\n]+")
1037 ex-file)
1038 (progn
1039 ;; if file name comes from history, don't leave
1040 ;; minibuffer when the user types space
8626cfa2 1041 (setq viper-incomplete-ex-cmd nil)
328b4b70
MK
1042 (setq ex-cmdfile-args
1043 (substring ex-file (match-end 0) nil))
52fa07ba
MK
1044 ;; this must be the last clause in this progn
1045 (substring ex-file (match-beginning 0) (match-end 0))
1046 )
1047 ""))
1048 ;; this leaves only the command name in the work area
1049 ;; file names are gone
1050 (delete-region (point) (1- (point-max)))
1051 ))
1052 (goto-char (point-max))
1053 (skip-chars-backward " \t\n")
1054 (setq prompt (buffer-substring (point-min) (point)))
1055 ))
a1506d29 1056
8626cfa2 1057 (setq viper-last-ex-prompt prompt)
a1506d29 1058
52fa07ba 1059 ;; If we just finished reading command, redisplay prompt
8626cfa2
MK
1060 (if viper-incomplete-ex-cmd
1061 (setq ex-file (viper-ex-read-file-name (format ":%s " prompt)))
52fa07ba
MK
1062 ;; file was typed in-line
1063 (setq ex-file (or ex-file "")))
1064 ))
bbe6126c 1065
bbe6126c 1066
3af0304a 1067;; Completes file name or exits minibuffer. If Ex command accepts multiple
52fa07ba 1068;; file names, arranges to re-enter the minibuffer.
8626cfa2 1069(defun viper-complete-filename-or-exit ()
52fa07ba 1070 (interactive)
a1506d29
JB
1071 (setq viper-keep-reading-filename t)
1072 ;; don't exit if directory---ex-commands don't
52fa07ba
MK
1073 (cond ((ex-cmd-accepts-multiple-files-p ex-token) (exit-minibuffer))
1074 ;; apparently the argument to an Ex command is
1075 ;; supposed to be a shell command
8626cfa2 1076 ((viper-looking-back "^[ \t]*!.*")
52fa07ba
MK
1077 (setq ex-cmdfile t)
1078 (insert " "))
1079 (t
1080 (setq ex-cmdfile nil)
1081 (minibuffer-complete-word))))
bbe6126c 1082
8626cfa2 1083(defun viper-handle-! ()
52fa07ba
MK
1084 (interactive)
1085 (if (and (string=
8626cfa2 1086 (buffer-string) (viper-abbreviate-file-name default-directory))
52fa07ba
MK
1087 (member ex-token '("read" "write")))
1088 (erase-buffer))
1089 (insert "!"))
1090
1091(defun ex-cmd-accepts-multiple-files-p (token)
1092 (member token '("edit" "next" "Next")))
1093
328b4b70 1094;; Read file name from the minibuffer in an ex command.
52fa07ba
MK
1095;; If user doesn't enter anything, then "" is returned, i.e., the
1096;; prompt-directory is not returned.
8626cfa2 1097(defun viper-ex-read-file-name (prompt)
52fa07ba
MK
1098 (let* ((str "")
1099 (minibuffer-local-completion-map
1100 (copy-keymap minibuffer-local-completion-map))
1101 beg end cont val)
a1506d29 1102
8626cfa2 1103 (viper-add-keymap ex-read-filename-map
a1506d29 1104 (if viper-emacs-p
52fa07ba 1105 minibuffer-local-completion-map
a1506d29
JB
1106 read-file-name-map))
1107
8626cfa2 1108 (setq cont (setq viper-keep-reading-filename t))
52fa07ba 1109 (while cont
8626cfa2 1110 (setq viper-keep-reading-filename nil
52fa07ba 1111 val (read-file-name (concat prompt str) nil default-directory))
2eb4bdca
MK
1112 (setq val (expand-file-name val))
1113 (if (and (string-match " " val)
1114 (ex-cmd-accepts-multiple-files-p ex-token))
1115 (setq val (concat "\"" val "\"")))
52fa07ba
MK
1116 (setq str (concat str (if (equal val "") "" " ")
1117 val (if (equal val "") "" " ")))
a1506d29 1118
52fa07ba 1119 ;; Only edit, next, and Next commands accept multiple files.
8626cfa2 1120 ;; viper-keep-reading-filename is set in the anonymous function that is
52fa07ba 1121 ;; bound to " " in ex-read-filename-map.
8626cfa2 1122 (setq cont (and viper-keep-reading-filename
52fa07ba
MK
1123 (ex-cmd-accepts-multiple-files-p ex-token)))
1124 )
a1506d29 1125
52fa07ba
MK
1126 (setq beg (string-match "[^ \t]" str) ; delete leading blanks
1127 end (string-match "[ \t]*$" str)) ; delete trailing blanks
1128 (if (member ex-token '("read" "write"))
1129 (if (string-match "[\t ]*!" str)
1130 ;; this is actually a shell command
1131 (progn
1132 (setq ex-cmdfile t)
1133 (setq beg (1+ beg))
8626cfa2
MK
1134 (setq viper-last-ex-prompt
1135 (concat viper-last-ex-prompt " !")))))
52fa07ba 1136 (substring str (or beg 0) end)))
bbe6126c 1137
52fa07ba 1138
8626cfa2 1139(defun viper-undisplayed-files ()
52fa07ba 1140 (mapcar
a1506d29 1141 (lambda (b)
3af0304a
MK
1142 (if (null (get-buffer-window b))
1143 (let ((f (buffer-file-name b)))
1144 (if f f
a1506d29 1145 (if ex-cycle-through-non-files
3af0304a
MK
1146 (let ((s (buffer-name b)))
1147 (if (string= " " (substring s 0 1))
1148 nil
1149 s))
1150 nil)))
1151 nil))
52fa07ba
MK
1152 (buffer-list)))
1153
1154
1155(defun ex-args ()
8626cfa2 1156 (let ((l (viper-undisplayed-files))
52fa07ba
MK
1157 (args "")
1158 (file-count 1))
1159 (while (not (null l))
a1506d29 1160 (if (car l)
52fa07ba
MK
1161 (setq args (format "%s %d) %s\n" args file-count (car l))
1162 file-count (1+ file-count)))
1163 (setq l (cdr l)))
1164 (if (string= args "")
1165 (message "All files are already displayed")
1166 (save-excursion
1167 (save-window-excursion
8626cfa2 1168 (with-output-to-temp-buffer " *viper-info*"
52fa07ba
MK
1169 (princ "\n\nThese files are not displayed in any window.\n")
1170 (princ "\n=============\n")
1171 (princ args)
1172 (princ "\n=============\n")
1173 (princ "\nThe numbers can be given as counts to :next. ")
1174 (princ "\n\nPress any key to continue...\n\n"))
8626cfa2 1175 (viper-read-event))))))
52fa07ba 1176
3af0304a 1177;; Ex cd command. Default directory of this buffer changes
52fa07ba 1178(defun ex-cd ()
8626cfa2 1179 (viper-get-ex-file)
52fa07ba
MK
1180 (if (string= ex-file "")
1181 (setq ex-file "~"))
1182 (setq default-directory (file-name-as-directory (expand-file-name ex-file))))
1183
1184;; Ex copy and move command. DEL-FLAG means delete
1185(defun ex-copy (del-flag)
8626cfa2
MK
1186 (viper-default-ex-addresses)
1187 (let ((address (viper-get-ex-address))
52fa07ba
MK
1188 (end (car ex-addresses)) (beg (car (cdr ex-addresses))))
1189 (goto-char end)
1190 (save-excursion
1191 (push-mark beg t)
8626cfa2 1192 (viper-enlarge-region (mark t) (point))
52fa07ba
MK
1193 (if del-flag
1194 (kill-region (point) (mark t))
1195 (copy-region-as-kill (point) (mark t)))
1196 (if ex-flag
1197 (progn
8e41a31c 1198 (with-output-to-temp-buffer " *copy text*"
52fa07ba
MK
1199 (princ
1200 (if (or del-flag ex-g-flag ex-g-variant)
1201 (current-kill 0)
1202 (buffer-substring (point) (mark t)))))
1203 (condition-case nil
1204 (progn
8e41a31c
MK
1205 (read-string "[Hit return to confirm] ")
1206 (save-excursion (kill-buffer " *copy text*")))
1207 (quit (save-excursion (kill-buffer " *copy text*"))
52fa07ba
MK
1208 (signal 'quit nil))))))
1209 (if (= address 0)
1210 (goto-char (point-min))
1211 (goto-char address)
1212 (forward-line 1))
1213 (insert (current-kill 0))))
1214
1215;; Ex delete command
1216(defun ex-delete ()
8626cfa2
MK
1217 (viper-default-ex-addresses)
1218 (viper-get-ex-buffer)
52fa07ba 1219 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses))))
8626cfa2 1220 (if (> beg end) (error viper-FirstAddrExceedsSecond))
52fa07ba 1221 (save-excursion
8626cfa2 1222 (viper-enlarge-region beg end)
52fa07ba
MK
1223 (exchange-point-and-mark)
1224 (if ex-count
1225 (progn
1226 (set-mark (point))
1227 (forward-line (1- ex-count)))
1228 (set-mark end))
8626cfa2 1229 (viper-enlarge-region (point) (mark t))
52fa07ba
MK
1230 (if ex-flag
1231 ;; show text to be deleted and ask for confirmation
1232 (progn
1233 (with-output-to-temp-buffer " *delete text*"
1234 (princ (buffer-substring (point) (mark t))))
1235 (condition-case nil
8e41a31c 1236 (read-string "[Hit return to confirm] ")
52fa07ba
MK
1237 (quit
1238 (save-excursion (kill-buffer " *delete text*"))
1239 (error "")))
1240 (save-excursion (kill-buffer " *delete text*")))
1241 (if ex-buffer
8626cfa2
MK
1242 (cond ((viper-valid-register ex-buffer '(Letter))
1243 (viper-append-to-register
52fa07ba 1244 (downcase ex-buffer) (point) (mark t)))
8626cfa2 1245 ((viper-valid-register ex-buffer)
52fa07ba 1246 (copy-to-register ex-buffer (point) (mark t) nil))
8626cfa2 1247 (t (error viper-InvalidRegister ex-buffer))))
52fa07ba
MK
1248 (kill-region (point) (mark t))))))
1249
1250
1251
1252;; Ex edit command
3af0304a 1253;; In Viper, `e' and `e!' behave identically. In both cases, the user is
52fa07ba 1254;; asked if current buffer should really be discarded.
3af0304a 1255;; This command can take multiple file names. It replaces the current buffer
52fa07ba
MK
1256;; with the first file in its argument list
1257(defun ex-edit (&optional file)
1258 (if (not file)
8626cfa2 1259 (viper-get-ex-file))
52fa07ba 1260 (cond ((and (string= ex-file "") buffer-file-name)
8626cfa2 1261 (setq ex-file (viper-abbreviate-file-name (buffer-file-name))))
52fa07ba 1262 ((string= ex-file "")
8626cfa2 1263 (error viper-NoFileSpecified)))
a1506d29 1264
2d341681
MK
1265 (let (msg do-edit)
1266 (if buffer-file-name
1267 (cond ((buffer-modified-p)
1268 (setq msg
1269 (format "Buffer %s is modified. Discard changes? "
1270 (buffer-name))
1271 do-edit t))
1272 ((not (verify-visited-file-modtime (current-buffer)))
1273 (setq msg
1274 (format "File %s changed on disk. Reread from disk? "
1275 buffer-file-name)
1276 do-edit t))
1277 (t (setq do-edit nil))))
a1506d29 1278
2d341681
MK
1279 (if do-edit
1280 (if (yes-or-no-p msg)
1281 (progn
1282 (set-buffer-modified-p nil)
1283 (kill-buffer (current-buffer)))
1284 (message "Buffer %s was left intact" (buffer-name))))
1285 ) ; let
a1506d29 1286
52fa07ba 1287 (if (null (setq file (get-file-buffer ex-file)))
a1506d29 1288 (progn
3af0304a
MK
1289 ;; this also does shell-style globbing
1290 (ex-find-file
1291 ;; replace # and % with the previous/current file
1292 (ex-expand-filsyms ex-file (current-buffer)))
ab124470 1293 (or (eq major-mode 'dired-mode)
8626cfa2 1294 (viper-change-state-to-vi))
52fa07ba
MK
1295 (goto-char (point-min)))
1296 (switch-to-buffer file))
1297 (if ex-offset
1298 (progn
1299 (save-window-excursion
a1506d29 1300 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 1301 (set-buffer viper-ex-work-buf)
52fa07ba
MK
1302 (delete-region (point-min) (point-max))
1303 (insert ex-offset "\n")
1304 (goto-char (point-min)))
8626cfa2 1305 (goto-char (viper-get-ex-address))
52fa07ba 1306 (beginning-of-line)))
8626cfa2 1307 (ex-fixup-history viper-last-ex-prompt ex-file))
52fa07ba 1308
ab124470 1309;; Find-file FILESPEC if it appears to specify a single file.
2eb4bdca 1310;; Otherwise, assume that FILESPEC is a wildcard.
ab124470 1311;; In this case, split it into substrings separated by newlines.
3af0304a 1312;; Each line is assumed to be a file name.
52fa07ba 1313(defun ex-find-file (filespec)
ab124470
MK
1314 (let ((nonstandard-filename-chars "[^-a-zA-Z0-9_./,~$\\]"))
1315 (cond ((file-exists-p filespec) (find-file filespec))
1316 ((string-match nonstandard-filename-chars filespec)
3af0304a 1317 (mapcar 'find-file (funcall viper-glob-function filespec)))
ab124470 1318 (t (find-file filespec)))
52fa07ba 1319 ))
bbe6126c 1320
6c2e12f4 1321
52fa07ba 1322;; Ex global command
1e70790f
MK
1323;; This is executed in response to:
1324;; :global "pattern" ex-command
1325;; :vglobal "pattern" ex-command
1326;; :global executes ex-command on all lines matching <pattern>
1327;; :vglobal executes ex-command on all lines that don't match <pattern>
1328;;
1329;; With VARIANT nil, this functions executes :global
1330;; With VARIANT t, executes :vglobal
52fa07ba
MK
1331(defun ex-global (variant)
1332 (let ((gcommand ex-token))
1333 (if (or ex-g-flag ex-g-variant)
1334 (error "`%s' within `global' is not allowed" gcommand)
1335 (if variant
1336 (setq ex-g-flag nil
1337 ex-g-variant t)
1338 (setq ex-g-flag t
1339 ex-g-variant nil)))
8626cfa2 1340 (viper-get-ex-pat)
52fa07ba
MK
1341 (if (null ex-token)
1342 (error "`%s': Missing regular expression" gcommand)))
a1506d29 1343
52fa07ba 1344 (if (string= ex-token "")
8626cfa2
MK
1345 (if (null viper-s-string)
1346 (error viper-NoPrevSearch)
1347 (setq ex-g-pat viper-s-string))
52fa07ba 1348 (setq ex-g-pat ex-token
8626cfa2 1349 viper-s-string ex-token))
52fa07ba
MK
1350 (if (null ex-addresses)
1351 (setq ex-addresses (list (point-max) (point-min)))
8626cfa2 1352 (viper-default-ex-addresses))
241d963d
MK
1353 (setq ex-g-marks nil)
1354 (let ((mark-count 0)
1e70790f
MK
1355 (end (car ex-addresses))
1356 (beg (car (cdr ex-addresses)))
1357 com-str)
8626cfa2 1358 (if (> beg end) (error viper-FirstAddrExceedsSecond))
52fa07ba 1359 (save-excursion
8626cfa2 1360 (viper-enlarge-region beg end)
52fa07ba
MK
1361 (exchange-point-and-mark)
1362 (let ((cont t) (limit (point-marker)))
1363 (exchange-point-and-mark)
1364 ;; skip the last line if empty
1365 (beginning-of-line)
8626cfa2 1366 (if (eobp) (viper-backward-char-carefully))
52fa07ba
MK
1367 (while (and cont (not (bobp)) (>= (point) limit))
1368 (beginning-of-line)
1369 (set-mark (point))
1370 (end-of-line)
1371 (let ((found (re-search-backward ex-g-pat (mark t) t)))
1372 (if (or (and ex-g-flag found)
1373 (and ex-g-variant (not found)))
1374 (progn
1375 (end-of-line)
1376 (setq mark-count (1+ mark-count))
241d963d 1377 (setq ex-g-marks (cons (point-marker) ex-g-marks)))))
52fa07ba
MK
1378 (beginning-of-line)
1379 (if (bobp) (setq cont nil)
1380 (forward-line -1)
1381 (end-of-line)))))
1382 (save-window-excursion
a1506d29 1383 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 1384 (set-buffer viper-ex-work-buf)
3af0304a 1385 ;; com-str is the command string, i.e., g/pattern/ or v/pattern'
52fa07ba 1386 (setq com-str (buffer-substring (1+ (point)) (1- (point-max)))))
241d963d
MK
1387 (while ex-g-marks
1388 (goto-char (car ex-g-marks))
3af0304a 1389 (viper-ex nil com-str)
52fa07ba 1390 (setq mark-count (1- mark-count))
241d963d 1391 (setq ex-g-marks (cdr ex-g-marks)))))
52fa07ba
MK
1392
1393;; Ex goto command
1394(defun ex-goto ()
1395 (if (null ex-addresses)
1396 (setq ex-addresses (cons (point) nil)))
1397 (push-mark (point) t)
1398 (goto-char (car ex-addresses))
3af0304a
MK
1399 (beginning-of-line)
1400 )
52fa07ba
MK
1401
1402;; Ex line commands. COM is join, shift-right or shift-left
1403(defun ex-line (com)
8626cfa2
MK
1404 (viper-default-ex-addresses)
1405 (viper-get-ex-count)
52fa07ba 1406 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses))) point)
8626cfa2 1407 (if (> beg end) (error viper-FirstAddrExceedsSecond))
52fa07ba 1408 (save-excursion
8626cfa2 1409 (viper-enlarge-region beg end)
52fa07ba
MK
1410 (exchange-point-and-mark)
1411 (if ex-count
1412 (progn
1413 (set-mark (point))
1414 (forward-line ex-count)))
1415 (if ex-flag
1416 ;; show text to be joined and ask for confirmation
1417 (progn
8e41a31c 1418 (with-output-to-temp-buffer " *join text*"
52fa07ba
MK
1419 (princ (buffer-substring (point) (mark t))))
1420 (condition-case nil
1421 (progn
8e41a31c 1422 (read-string "[Hit return to confirm] ")
52fa07ba
MK
1423 (ex-line-subr com (point) (mark t)))
1424 (quit (ding)))
8e41a31c 1425 (save-excursion (kill-buffer " *join text*")))
52fa07ba
MK
1426 (ex-line-subr com (point) (mark t)))
1427 (setq point (point)))
1428 (goto-char (1- point))
1429 (beginning-of-line)))
1430
1431(defun ex-line-subr (com beg end)
1432 (cond ((string= com "join")
1433 (goto-char (min beg end))
1434 (while (and (not (eobp)) (< (point) (max beg end)))
1435 (end-of-line)
1436 (if (and (<= (point) (max beg end)) (not (eobp)))
1437 (progn
1438 (forward-line 1)
1439 (delete-region (point) (1- (point)))
1440 (if (not ex-variant) (fixup-whitespace))))))
1441 ((or (string= com "right") (string= com "left"))
1442 (indent-rigidly
1443 (min beg end) (max beg end)
8626cfa2 1444 (if (string= com "right") viper-shift-width (- viper-shift-width)))
52fa07ba
MK
1445 (goto-char (max beg end))
1446 (end-of-line)
8626cfa2 1447 (viper-forward-char-carefully))))
52fa07ba
MK
1448
1449
1450;; Ex mark command
4986c2c6
MK
1451;; Sets the mark to the current point.
1452;; If name is omitted, get the name straight from the work buffer."
1453(defun ex-mark (&optional name)
52fa07ba
MK
1454 (let (char)
1455 (if (null ex-addresses)
1456 (setq ex-addresses
1457 (cons (point) nil)))
4986c2c6
MK
1458 (if name
1459 (if (eq 1 (length name))
1460 (setq char (string-to-char name))
60370d40 1461 (error "`%s': Spurious text \"%s\" after mark name"
0b762d9a 1462 name (substring name 1)))
52fa07ba 1463 (save-window-excursion
a1506d29 1464 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 1465 (set-buffer viper-ex-work-buf)
52fa07ba
MK
1466 (skip-chars-forward " \t")
1467 (if (looking-at "[a-z]")
1468 (progn
1469 (setq char (following-char))
1470 (forward-char 1)
1471 (skip-chars-forward " \t")
1472 (if (not (looking-at "[\n|]"))
8626cfa2 1473 (error "`%s': %s" ex-token viper-SpuriousText)))
4986c2c6 1474 (error "`%s' requires a following letter" ex-token))))
52fa07ba
MK
1475 (save-excursion
1476 (goto-char (car ex-addresses))
4960e757 1477 (point-to-register (viper-int-to-char (1+ (- char ?a)))))))
6c2e12f4 1478
a1506d29
JB
1479
1480
52fa07ba
MK
1481;; Alternate file is the file next to the first one in the buffer ring
1482(defun ex-next (cycle-other-window &optional find-alt-file)
1483 (catch 'ex-edit
1484 (let (count l)
a1506d29 1485 (if (not find-alt-file)
bbe6126c 1486 (progn
8626cfa2 1487 (viper-get-ex-file)
52fa07ba 1488 (if (or (char-or-string-p ex-offset)
a1506d29 1489 (and (not (string= "" ex-file))
52fa07ba 1490 (not (string-match "^[0-9]+$" ex-file))))
bbe6126c 1491 (progn
52fa07ba
MK
1492 (ex-edit t)
1493 (throw 'ex-edit nil))
027a4b6b 1494 (setq count (string-to-number ex-file))
52fa07ba
MK
1495 (if (= count 0) (setq count 1))
1496 (if (< count 0) (error "Usage: `next <count>' (count >= 0)"))))
1497 (setq count 1))
8626cfa2 1498 (setq l (viper-undisplayed-files))
52fa07ba
MK
1499 (while (> count 0)
1500 (while (and (not (null l)) (null (car l)))
1501 (setq l (cdr l)))
1502 (setq count (1- count))
1503 (if (> count 0)
1504 (setq l (cdr l))))
1505 (if find-alt-file (car l)
1506 (progn
1507 (if (and (car l) (get-file-buffer (car l)))
1508 (let* ((w (if cycle-other-window
1509 (get-lru-window) (selected-window)))
1510 (b (window-buffer w)))
1511 (set-window-buffer w (get-file-buffer (car l)))
1512 (bury-buffer b)
1513 ;; this puts "next <count>" in the ex-command history
8626cfa2 1514 (ex-fixup-history viper-last-ex-prompt ex-file))
52fa07ba
MK
1515 (error "Not that many undisplayed files")))))))
1516
1517
1518(defun ex-next-related-buffer (direction &optional no-recursion)
a1506d29 1519
8626cfa2 1520 (viper-ring-rotate1 viper-related-files-and-buffers-ring direction)
a1506d29
JB
1521
1522 (let ((file-or-buffer-name
8626cfa2
MK
1523 (viper-current-ring-item viper-related-files-and-buffers-ring))
1524 (old-ring viper-related-files-and-buffers-ring)
52fa07ba
MK
1525 (old-win (selected-window))
1526 skip-rest buf wind)
a1506d29 1527
8626cfa2
MK
1528 (or (and (ring-p viper-related-files-and-buffers-ring)
1529 (> (ring-length viper-related-files-and-buffers-ring) 0))
52fa07ba 1530 (error "This buffer has no related files or buffers"))
a1506d29 1531
52fa07ba
MK
1532 (or (stringp file-or-buffer-name)
1533 (error
1534 "File and buffer names must be strings, %S" file-or-buffer-name))
a1506d29 1535
52fa07ba
MK
1536 (setq buf (cond ((get-buffer file-or-buffer-name))
1537 ((file-exists-p file-or-buffer-name)
1538 (find-file-noselect file-or-buffer-name))
1539 ))
a1506d29 1540
8626cfa2 1541 (if (not (viper-buffer-live-p buf))
52fa07ba
MK
1542 (error "Didn't find buffer %S or file %S"
1543 file-or-buffer-name
8626cfa2 1544 (viper-abbreviate-file-name
52fa07ba 1545 (expand-file-name file-or-buffer-name))))
a1506d29 1546
52fa07ba
MK
1547 (if (equal buf (current-buffer))
1548 (or no-recursion
1549 ;; try again
1550 (progn
1551 (setq skip-rest t)
1552 (ex-next-related-buffer direction 'norecursion))))
a1506d29 1553
52fa07ba
MK
1554 (if skip-rest
1555 ()
1556 ;; setup buffer
8626cfa2 1557 (if (setq wind (viper-get-visible-buffer-window buf))
52fa07ba 1558 ()
8626cfa2 1559 (setq wind (get-lru-window (if viper-xemacs-p nil 'visible)))
52fa07ba 1560 (set-window-buffer wind buf))
a1506d29 1561
8626cfa2 1562 (if (viper-window-display-p)
52fa07ba
MK
1563 (progn
1564 (raise-frame (window-frame wind))
1565 (if (equal (window-frame wind) (window-frame old-win))
1566 (save-window-excursion (select-window wind) (sit-for 1))
1567 (select-window wind)))
1568 (save-window-excursion (select-window wind) (sit-for 1)))
a1506d29 1569
52fa07ba
MK
1570 (save-excursion
1571 (set-buffer buf)
8626cfa2 1572 (setq viper-related-files-and-buffers-ring old-ring))
a1506d29 1573
8626cfa2 1574 (setq viper-local-search-start-marker (point-marker))
52fa07ba 1575 )))
a1506d29
JB
1576
1577
52fa07ba
MK
1578;; Force auto save
1579(defun ex-preserve ()
1580 (message "Autosaving all buffers that need to be saved...")
1581 (do-auto-save t))
1582
1583;; Ex put
1584(defun ex-put ()
1585 (let ((point (if (null ex-addresses) (point) (car ex-addresses))))
8626cfa2
MK
1586 (viper-get-ex-buffer)
1587 (setq viper-use-register ex-buffer)
52fa07ba 1588 (goto-char point)
8626cfa2 1589 (if (bobp) (viper-Put-back 1) (viper-put-back 1))))
52fa07ba
MK
1590
1591;; Ex print working directory
1592(defun ex-pwd ()
1593 (message default-directory))
1594
1595;; Ex quit command
1596(defun ex-quit ()
3af0304a 1597 ;; skip "!", if it is q!. In Viper q!, w!, etc., behave as q, w, etc.
52fa07ba 1598 (save-excursion
a1506d29 1599 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 1600 (set-buffer viper-ex-work-buf)
52fa07ba 1601 (if (looking-at "!") (forward-char 1)))
1e70790f 1602 (if (< viper-expert-level 3)
52fa07ba
MK
1603 (save-buffers-kill-emacs)
1604 (kill-buffer (current-buffer))))
6c2e12f4 1605
6c2e12f4 1606
52fa07ba 1607;; Ex read command
3af0304a
MK
1608;; ex-read doesn't support wildcards, because file completion is a better
1609;; mechanism. We also don't support # and % (except in :r <shell-command>
1610;; because file history is a better mechanism.
52fa07ba 1611(defun ex-read ()
8626cfa2 1612 (viper-get-ex-file)
52fa07ba
MK
1613 (let ((point (if (null ex-addresses) (point) (car ex-addresses)))
1614 command)
1615 (goto-char point)
8626cfa2 1616 (viper-add-newline-at-eob-if-necessary)
52fa07ba
MK
1617 (if (not (or (bobp) (eobp))) (forward-line 1))
1618 (if (and (not ex-variant) (string= ex-file ""))
1619 (progn
1620 (if (null buffer-file-name)
8626cfa2 1621 (error viper-NoFileSpecified))
52fa07ba
MK
1622 (setq ex-file buffer-file-name)))
1623 (if ex-cmdfile
1624 (progn
a1506d29 1625 (setq command
3af0304a
MK
1626 ;; replace # and % with the previous/current file
1627 (ex-expand-filsyms (concat ex-file ex-cmdfile-args)
1628 (current-buffer)))
52fa07ba
MK
1629 (shell-command command t))
1630 (insert-file-contents ex-file)))
328b4b70 1631 (ex-fixup-history viper-last-ex-prompt ex-file ex-cmdfile-args))
a1506d29 1632
52fa07ba 1633;; this function fixes ex-history for some commands like ex-read, ex-edit
a1506d29 1634(defun ex-fixup-history (&rest args)
8626cfa2
MK
1635 (setq viper-ex-history
1636 (cons (mapconcat 'identity args " ") (cdr viper-ex-history))))
a1506d29 1637
52fa07ba
MK
1638
1639;; Ex recover from emacs \#file\#
1640(defun ex-recover ()
8626cfa2 1641 (viper-get-ex-file)
52fa07ba 1642 (if (or ex-append ex-offset)
8626cfa2 1643 (error "`recover': %s" viper-SpuriousText))
52fa07ba
MK
1644 (if (string= ex-file "")
1645 (progn
1646 (if (null buffer-file-name)
1647 (error "This buffer isn't visiting any file"))
1648 (setq ex-file buffer-file-name))
1649 (setq ex-file (expand-file-name ex-file)))
1650 (if (and (not (string= ex-file (buffer-file-name)))
1651 (buffer-modified-p)
1652 (not ex-variant))
1653 (error "No write since last change \(:rec! overrides\)"))
1654 (recover-file ex-file))
1655
1656;; Tell that `rewind' is obsolete and to use `:next count' instead
1657(defun ex-rewind ()
1658 (message
3af0304a 1659 "Use `:n <count>' instead. Counts are obtained from the `:args' command"))
52fa07ba
MK
1660
1661
1662;; read variable name for ex-set
1663(defun ex-set-read-variable ()
1664 (let ((minibuffer-local-completion-map
1665 (copy-keymap minibuffer-local-completion-map))
1666 (cursor-in-echo-area t)
1667 str batch)
1668 (define-key
1669 minibuffer-local-completion-map " " 'minibuffer-complete-and-exit)
1670 (define-key minibuffer-local-completion-map "=" 'exit-minibuffer)
8626cfa2 1671 (if (viper-set-unread-command-events
52fa07ba
MK
1672 (ex-get-inline-cmd-args "[ \t]*[a-zA-Z]*[ \t]*" nil "\C-m"))
1673 (progn
1674 (setq batch t)
8626cfa2 1675 (viper-set-unread-command-events ?\C-m)))
52fa07ba
MK
1676 (message ":set <Variable> [= <Value>]")
1677 (or batch (sit-for 2))
a1506d29 1678
52fa07ba
MK
1679 (while (string-match "^[ \\t\\n]*$"
1680 (setq str
1681 (completing-read ":set " ex-variable-alist)))
e36a387d 1682 (message ":set <Variable> [= <Value>]")
52fa07ba 1683 ;; if there are unread events, don't wait
8626cfa2 1684 (or (viper-set-unread-command-events "") (sit-for 2))
52fa07ba
MK
1685 ) ; while
1686 str))
1687
1688
1689(defun ex-set ()
1690 (let ((var (ex-set-read-variable))
1691 (val 0)
1692 (set-cmd "setq")
1693 (ask-if-save t)
1694 (auto-cmd-label "; don't touch or else...")
1695 (delete-turn-on-auto-fill-pattern
2eb4bdca 1696 "([ \t]*add-hook[ \t]+'viper-insert-state-hook[ \t]+'turn-on-auto-fill.*)")
52fa07ba
MK
1697 actual-lisp-cmd lisp-cmd-del-pattern
1698 val2 orig-var)
1699 (setq orig-var var)
e36a387d
MK
1700 (cond ((string= var "all")
1701 (setq ask-if-save nil
1702 set-cmd nil))
1703 ((member var '("ai" "autoindent"))
8626cfa2 1704 (setq var "viper-auto-indent"
52fa07ba
MK
1705 set-cmd "setq"
1706 ask-if-save nil
1707 val "t"))
e36a387d 1708 ((member var '("ai-g" "autoindent-global"))
8626cfa2
MK
1709 (kill-local-variable 'viper-auto-indent)
1710 (setq var "viper-auto-indent"
52fa07ba
MK
1711 set-cmd "setq-default"
1712 val "t"))
1713 ((member var '("noai" "noautoindent"))
8626cfa2 1714 (setq var "viper-auto-indent"
52fa07ba
MK
1715 ask-if-save nil
1716 val "nil"))
e36a387d 1717 ((member var '("noai-g" "noautoindent-global"))
8626cfa2
MK
1718 (kill-local-variable 'viper-auto-indent)
1719 (setq var "viper-auto-indent"
52fa07ba
MK
1720 set-cmd "setq-default"
1721 val "nil"))
1722 ((member var '("ic" "ignorecase"))
8626cfa2 1723 (setq var "viper-case-fold-search"
52fa07ba
MK
1724 val "t"))
1725 ((member var '("noic" "noignorecase"))
8626cfa2 1726 (setq var "viper-case-fold-search"
52fa07ba
MK
1727 val "nil"))
1728 ((member var '("ma" "magic"))
8626cfa2 1729 (setq var "viper-re-search"
52fa07ba 1730 val "t"))
e36a387d 1731 ((member var '("noma" "nomagic"))
8626cfa2 1732 (setq var "viper-re-search"
52fa07ba
MK
1733 val "nil"))
1734 ((member var '("ro" "readonly"))
1735 (setq var "buffer-read-only"
1736 val "t"))
1737 ((member var '("noro" "noreadonly"))
1738 (setq var "buffer-read-only"
1739 val "nil"))
1740 ((member var '("sm" "showmatch"))
1741 (setq var "blink-matching-paren"
1742 val "t"))
1743 ((member var '("nosm" "noshowmatch"))
1744 (setq var "blink-matching-paren"
1745 val "nil"))
1746 ((member var '("ws" "wrapscan"))
8626cfa2 1747 (setq var "viper-search-wrap-around-t"
52fa07ba
MK
1748 val "t"))
1749 ((member var '("nows" "nowrapscan"))
8626cfa2 1750 (setq var "viper-search-wrap-around-t"
52fa07ba 1751 val "nil")))
e36a387d 1752 (if (and set-cmd (eq val 0)) ; value must be set by the user
52fa07ba
MK
1753 (let ((cursor-in-echo-area t))
1754 (message ":set %s = <Value>" var)
1755 ;; if there are unread events, don't wait
8626cfa2 1756 (or (viper-set-unread-command-events "") (sit-for 2))
52fa07ba
MK
1757 (setq val (read-string (format ":set %s = " var)))
1758 (ex-fixup-history "set" orig-var val)
a1506d29 1759
52fa07ba
MK
1760 ;; check numerical values
1761 (if (member var
1762 '("sw" "shiftwidth"
1763 "ts" "tabstop"
e36a387d 1764 "ts-g" "tabstop-global"
a1506d29 1765 "wm" "wrapmargin"))
52fa07ba
MK
1766 (condition-case nil
1767 (or (numberp (setq val2 (car (read-from-string val))))
1768 (error "%s: Invalid value, numberp, %S" var val))
1769 (error
1770 (error "%s: Invalid value, numberp, %S" var val))))
a1506d29 1771
52fa07ba
MK
1772 (cond
1773 ((member var '("sw" "shiftwidth"))
8626cfa2 1774 (setq var "viper-shift-width"))
52fa07ba
MK
1775 ((member var '("ts" "tabstop"))
1776 ;; make it take effect in curr buff and new bufs
1777 (setq var "tab-width"
1778 set-cmd "setq"
1779 ask-if-save nil))
e36a387d 1780 ((member var '("ts-g" "tabstop-global"))
52fa07ba
MK
1781 (kill-local-variable 'tab-width)
1782 (setq var "tab-width"
1783 set-cmd "setq-default"))
1784 ((member var '("wm" "wrapmargin"))
1785 ;; make it take effect in curr buff and new bufs
a1506d29
JB
1786 (kill-local-variable 'fill-column)
1787 (setq var "fill-column"
52fa07ba
MK
1788 val (format "(- (window-width) %s)" val)
1789 set-cmd "setq-default"))
1790 ((member var '("sh" "shell"))
1791 (setq var "explicit-shell-file-name"
1792 val (format "\"%s\"" val)))))
1793 (ex-fixup-history "set" orig-var))
a1506d29 1794
e36a387d
MK
1795 (if set-cmd
1796 (setq actual-lisp-cmd
1797 (format "\n(%s %s %s) %s" set-cmd var val auto-cmd-label)
1798 lisp-cmd-del-pattern
1799 (format "^\n?[ \t]*([ \t]*%s[ \t]+%s[ \t].*)[ \t]*%s"
1800 set-cmd var auto-cmd-label)))
a1506d29 1801
52fa07ba
MK
1802 (if (and ask-if-save
1803 (y-or-n-p (format "Do you want to save this setting in %s "
8626cfa2 1804 viper-custom-file-name)))
52fa07ba 1805 (progn
a1506d29 1806 (viper-save-string-in-file
8626cfa2 1807 actual-lisp-cmd viper-custom-file-name
52fa07ba
MK
1808 ;; del pattern
1809 lisp-cmd-del-pattern)
1810 (if (string= var "fill-column")
1811 (if (> val2 0)
8626cfa2 1812 (viper-save-string-in-file
52fa07ba 1813 (concat
2eb4bdca 1814 "(add-hook 'viper-insert-state-hook 'turn-on-auto-fill) "
52fa07ba 1815 auto-cmd-label)
8626cfa2 1816 viper-custom-file-name
52fa07ba 1817 delete-turn-on-auto-fill-pattern)
8626cfa2
MK
1818 (viper-save-string-in-file
1819 nil viper-custom-file-name delete-turn-on-auto-fill-pattern)
1820 (viper-save-string-in-file
1821 nil viper-custom-file-name
52fa07ba
MK
1822 ;; del pattern
1823 lisp-cmd-del-pattern)
1824 ))
1825 ))
a1506d29 1826
e36a387d
MK
1827 (if set-cmd
1828 (message "%s %s %s"
1829 set-cmd var
1830 (if (string-match "^[ \t]*$" val)
1831 (format "%S" val)
1832 val)))
1833 (if actual-lisp-cmd
1834 (eval (car (read-from-string actual-lisp-cmd))))
1835 (if (string= var "fill-column")
1836 (if (> val2 0)
1837 (auto-fill-mode 1)
1838 (auto-fill-mode -1)))
1839 (if (string= var "all") (ex-show-vars))
52fa07ba 1840 ))
6c2e12f4 1841
52fa07ba
MK
1842;; In inline args, skip regex-forw and (optionally) chars-back.
1843;; Optional 3d arg is a string that should replace ' ' to prevent its
1844;; special meaning
1845(defun ex-get-inline-cmd-args (regex-forw &optional chars-back replace-str)
1846 (save-excursion
a1506d29 1847 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 1848 (set-buffer viper-ex-work-buf)
52fa07ba
MK
1849 (goto-char (point-min))
1850 (re-search-forward regex-forw nil t)
1851 (let ((beg (point))
1852 end)
1853 (goto-char (point-max))
1854 (if chars-back
1855 (skip-chars-backward chars-back)
1856 (skip-chars-backward " \t\n\C-m"))
1857 (setq end (point))
1858 ;; replace SPC with `=' to suppress the special meaning SPC has
1859 ;; in Ex commands
1860 (goto-char beg)
1861 (if replace-str
1862 (while (re-search-forward " +" nil t)
1863 (replace-match replace-str nil t)
8626cfa2 1864 (viper-forward-char-carefully)))
52fa07ba
MK
1865 (goto-char end)
1866 (buffer-substring beg end))))
1867
1868
1869;; Ex shell command
1870(defun ex-shell ()
1871 (shell))
a1506d29 1872
3af0304a 1873;; Viper help. Invokes Info
52fa07ba
MK
1874(defun ex-help ()
1875 (condition-case nil
1876 (progn
1877 (pop-to-buffer (get-buffer-create "*info*"))
8626cfa2 1878 (info (if viper-xemacs-p "viper.info" "viper"))
52fa07ba
MK
1879 (message "Type `i' to search for a specific topic"))
1880 (error (beep 1)
8626cfa2 1881 (with-output-to-temp-buffer " *viper-info*"
52fa07ba
MK
1882 (princ (format "
1883The Info file for Viper does not seem to be installed.
1884
1885This file is part of the standard distribution of %sEmacs.
1886Please contact your system administrator. "
8626cfa2 1887 (if viper-xemacs-p "X" "")
52fa07ba
MK
1888 ))))))
1889
3af0304a 1890;; Ex source command. Loads the file specified as argument or `~/.viper'
52fa07ba 1891(defun ex-source ()
8626cfa2 1892 (viper-get-ex-file)
52fa07ba 1893 (if (string= ex-file "")
8626cfa2 1894 (load viper-custom-file-name)
52fa07ba
MK
1895 (load ex-file)))
1896
1897;; Ex substitute command
8626cfa2 1898;; If REPEAT use previous regexp which is ex-reg-exp or viper-s-string
a1506d29 1899(defun ex-substitute (&optional repeat r-flag)
52fa07ba
MK
1900 (let ((opt-g nil)
1901 (opt-c nil)
1902 (matched-pos nil)
8626cfa2 1903 (case-fold-search viper-case-fold-search)
52fa07ba 1904 delim pat repl)
8626cfa2 1905 (if repeat (setq ex-token nil) (setq delim (viper-get-ex-pat)))
52fa07ba
MK
1906 (if (null ex-token)
1907 (progn
8626cfa2 1908 (setq pat (if r-flag viper-s-string ex-reg-exp))
52fa07ba
MK
1909 (or (stringp pat)
1910 (error "No previous pattern to use in substitution"))
1911 (setq repl ex-repl
1912 delim (string-to-char pat)))
8626cfa2
MK
1913 (setq pat (if (string= ex-token "") viper-s-string ex-token))
1914 (setq viper-s-string pat
52fa07ba 1915 ex-reg-exp pat)
8626cfa2 1916 (setq delim (viper-get-ex-pat))
52fa07ba
MK
1917 (if (null ex-token)
1918 (setq ex-token ""
1919 ex-repl "")
1920 (setq repl ex-token
1921 ex-repl ex-token)))
8626cfa2 1922 (while (viper-get-ex-opt-gc delim)
52fa07ba 1923 (if (string= ex-token "g") (setq opt-g t) (setq opt-c t)))
8626cfa2 1924 (viper-get-ex-count)
52fa07ba
MK
1925 (if ex-count
1926 (save-excursion
1927 (if ex-addresses (goto-char (car ex-addresses)))
1928 (set-mark (point))
1929 (forward-line (1- ex-count))
1930 (setq ex-addresses (cons (point) (cons (mark t) nil))))
1931 (if (null ex-addresses)
1932 (setq ex-addresses (cons (point) (cons (point) nil)))
1933 (if (null (cdr ex-addresses))
1934 (setq ex-addresses (cons (car ex-addresses) ex-addresses)))))
1935 ;(setq G opt-g)
1936 (let ((beg (car ex-addresses))
1937 (end (car (cdr ex-addresses)))
1938 eol-mark)
1939 (save-excursion
8626cfa2 1940 (viper-enlarge-region beg end)
52fa07ba
MK
1941 (let ((limit (save-excursion
1942 (goto-char (max (point) (mark t)))
1943 (point-marker))))
1944 (goto-char (min (point) (mark t)))
1945 (while (< (point) limit)
2eb4bdca
MK
1946 (save-excursion
1947 (end-of-line)
1948 ;; This move allows the use of newline as the last character in
1949 ;; the substitution pattern
1950 (viper-forward-char-carefully)
1951 (setq eol-mark (point-marker)))
52fa07ba
MK
1952 (beginning-of-line)
1953 (if opt-g
1954 (progn
1955 (while (and (not (eolp))
1956 (re-search-forward pat eol-mark t))
d35bee0e
MK
1957 (if (or (not opt-c)
1958 (progn
1959 (viper-put-on-search-overlay (match-beginning 0)
1960 (match-end 0))
1961 (y-or-n-p "Replace? ")))
52fa07ba 1962 (progn
d35bee0e 1963 (viper-hide-search-overlay)
52fa07ba
MK
1964 (setq matched-pos (point))
1965 (if (not (stringp repl))
1966 (error "Can't perform Ex substitution: No previous replacement pattern"))
1967 (replace-match repl t))))
1968 (end-of-line)
8626cfa2 1969 (viper-forward-char-carefully))
52fa07ba
MK
1970 (if (null pat)
1971 (error
1972 "Can't repeat Ex substitution: No previous regular expression"))
1973 (if (and (re-search-forward pat eol-mark t)
d35bee0e
MK
1974 (or (not opt-c)
1975 (progn
1976 (viper-put-on-search-overlay (match-beginning 0)
1977 (match-end 0))
1978 (y-or-n-p "Replace? "))))
52fa07ba 1979 (progn
d35bee0e 1980 (viper-hide-search-overlay)
52fa07ba
MK
1981 (setq matched-pos (point))
1982 (if (not (stringp repl))
1983 (error "Can't perform Ex substitution: No previous replacement pattern"))
1984 (replace-match repl t)))
2eb4bdca
MK
1985 ;;(end-of-line)
1986 ;;(viper-forward-char-carefully)
1987 (goto-char eol-mark)
1988 )))))
52fa07ba
MK
1989 (if matched-pos (goto-char matched-pos))
1990 (beginning-of-line)
1991 (if opt-c (message "done"))))
8f2685cb 1992
52fa07ba
MK
1993;; Ex tag command
1994(defun ex-tag ()
1995 (let (tag)
1996 (save-window-excursion
a1506d29 1997 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 1998 (set-buffer viper-ex-work-buf)
52fa07ba
MK
1999 (skip-chars-forward " \t")
2000 (set-mark (point))
2001 (skip-chars-forward "^ |\t\n")
2002 (setq tag (buffer-substring (mark t) (point))))
2003 (if (not (string= tag "")) (setq ex-tag tag))
8626cfa2 2004 (viper-change-state-to-emacs)
52fa07ba
MK
2005 (condition-case conds
2006 (progn
2007 (if (string= tag "")
2008 (find-tag ex-tag t)
2009 (find-tag-other-window ex-tag))
8626cfa2 2010 (viper-change-state-to-vi))
52fa07ba 2011 (error
8626cfa2
MK
2012 (viper-change-state-to-vi)
2013 (viper-message-conditions conds)))))
bbe6126c 2014
52fa07ba 2015;; Ex write command
3af0304a 2016;; ex-write doesn't support wildcards, because file completion is a better
a1506d29 2017;; mechanism. We also don't support # and %
3af0304a 2018;; because file history is a better mechanism.
52fa07ba 2019(defun ex-write (q-flag)
8626cfa2
MK
2020 (viper-default-ex-addresses t)
2021 (viper-get-ex-file)
9b70a748 2022 (let ((end (car ex-addresses))
a1506d29 2023 (beg (car (cdr ex-addresses)))
9b70a748 2024 (orig-buf (current-buffer))
50a07e18
MK
2025 ;;(orig-buf-file-name (buffer-file-name))
2026 ;;(orig-buf-name (buffer-name))
2027 ;;(buff-changed-p (buffer-modified-p))
52fa07ba
MK
2028 temp-buf writing-same-file region
2029 file-exists writing-whole-file)
8626cfa2 2030 (if (> beg end) (error viper-FirstAddrExceedsSecond))
52fa07ba
MK
2031 (if ex-cmdfile
2032 (progn
8626cfa2 2033 (viper-enlarge-region beg end)
a1506d29 2034 (shell-command-on-region (point) (mark t)
328b4b70 2035 (concat ex-file ex-cmdfile-args)))
52fa07ba
MK
2036 (if (and (string= ex-file "") (not (buffer-file-name)))
2037 (setq ex-file
2038 (read-file-name
3af0304a 2039 (format "Buffer %s isn't visiting any file. File to save in: "
52fa07ba 2040 (buffer-name)))))
a1506d29 2041
52fa07ba
MK
2042 (setq writing-whole-file (and (= (point-min) beg) (= (point-max) end))
2043 ex-file (if (string= ex-file "")
2044 (buffer-file-name)
2045 (expand-file-name ex-file)))
2046 ;; if ex-file is a directory use the file portion of the buffer file name
2047 (if (and (file-directory-p ex-file)
2048 buffer-file-name
2049 (not (file-directory-p buffer-file-name)))
2050 (setq ex-file
9b70a748
MK
2051 (concat (file-name-as-directory ex-file)
2052 (file-name-nondirectory buffer-file-name))))
a1506d29 2053
52fa07ba
MK
2054 (setq file-exists (file-exists-p ex-file)
2055 writing-same-file (string= ex-file (buffer-file-name)))
2056
2eb4bdca 2057 ;; do actual writing
52fa07ba 2058 (if (and writing-whole-file writing-same-file)
2eb4bdca 2059 ;; saving whole buffer in visited file
52fa07ba
MK
2060 (if (not (buffer-modified-p))
2061 (message "(No changes need to be saved)")
2eb4bdca 2062 (viper-maybe-checkout (current-buffer))
52fa07ba 2063 (save-buffer)
9b70a748
MK
2064 (save-restriction
2065 (widen)
2066 (ex-write-info file-exists ex-file (point-min) (point-max))
2067 ))
2eb4bdca
MK
2068 ;; writing to non-visited file and it already exists
2069 (if (and file-exists (not writing-same-file)
2070 (not (yes-or-no-p
3af0304a 2071 (format "File %s exists. Overwrite? " ex-file))))
2eb4bdca
MK
2072 (error "Quit"))
2073 ;; writing a region or whole buffer to non-visited file
a1506d29 2074 (unwind-protect
2eb4bdca
MK
2075 (save-excursion
2076 (viper-enlarge-region beg end)
2077 (setq region (buffer-substring (point) (mark t)))
2078 ;; create temp buffer for the region
2079 (setq temp-buf (get-buffer-create " *ex-write*"))
2080 (set-buffer temp-buf)
50a07e18
MK
2081 (viper-cond-compile-for-xemacs-or-emacs
2082 (set-visited-file-name ex-file) ; xemacs
2083 (set-visited-file-name ex-file 'noquerry) ; emacs
2084 )
2eb4bdca
MK
2085 (erase-buffer)
2086 (if (and file-exists ex-append)
2087 (insert-file-contents ex-file))
2088 (goto-char (point-max))
2089 (insert region)
2090 ;; ask user
2091 (viper-maybe-checkout (current-buffer))
fc290d1d 2092 (setq selective-display nil)
2eb4bdca
MK
2093 (save-buffer)
2094 (ex-write-info
2095 file-exists ex-file (point-min) (point-max))
2096 )
2097 ;; this must be under unwind-protect so that
2098 ;; temp-buf will be deleted in case of an error
2099 (set-buffer temp-buf)
2100 (set-buffer-modified-p nil)
2101 (kill-buffer temp-buf)
2102 ;; buffer/region has been written, now take care of details
2103 (set-buffer orig-buf)))
2104 ;; set the right file modification time
52fa07ba
MK
2105 (if (and (buffer-file-name) writing-same-file)
2106 (set-visited-file-modtime))
2eb4bdca 2107 ;; prevent loss of data if saving part of the buffer in visited file
a1506d29 2108 (or writing-whole-file
52fa07ba 2109 (not writing-same-file)
2eb4bdca
MK
2110 (progn
2111 (sit-for 2)
2112 (message "Warning: you have saved only part of the buffer!")
2113 (set-buffer-modified-p t)))
52fa07ba 2114 (if q-flag
1e70790f 2115 (if (< viper-expert-level 2)
52fa07ba
MK
2116 (save-buffers-kill-emacs)
2117 (kill-buffer (current-buffer))))
2118 )))
a1506d29 2119
bbe6126c 2120
52fa07ba
MK
2121(defun ex-write-info (exists file-name beg end)
2122 (message "`%s'%s %d lines, %d characters"
8626cfa2 2123 (viper-abbreviate-file-name file-name)
52fa07ba
MK
2124 (if exists "" " [New file]")
2125 (count-lines beg (min (1+ end) (point-max)))
2126 (- end beg)))
2127
2128;; Ex yank command
2129(defun ex-yank ()
8626cfa2
MK
2130 (viper-default-ex-addresses)
2131 (viper-get-ex-buffer)
52fa07ba 2132 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses))))
8626cfa2 2133 (if (> beg end) (error viper-FirstAddrExceedsSecond))
52fa07ba 2134 (save-excursion
8626cfa2 2135 (viper-enlarge-region beg end)
52fa07ba
MK
2136 (exchange-point-and-mark)
2137 (if (or ex-g-flag ex-g-variant)
2138 (error "Can't execute `yank' within `global'"))
2139 (if ex-count
bbe6126c 2140 (progn
52fa07ba
MK
2141 (set-mark (point))
2142 (forward-line (1- ex-count)))
2143 (set-mark end))
8626cfa2
MK
2144 (viper-enlarge-region (point) (mark t))
2145 (if ex-flag (error "`yank': %s" viper-SpuriousText))
52fa07ba 2146 (if ex-buffer
8626cfa2
MK
2147 (cond ((viper-valid-register ex-buffer '(Letter))
2148 (viper-append-to-register
52fa07ba 2149 (downcase ex-buffer) (point) (mark t)))
8626cfa2 2150 ((viper-valid-register ex-buffer)
52fa07ba 2151 (copy-to-register ex-buffer (point) (mark t) nil))
8626cfa2 2152 (t (error viper-InvalidRegister ex-buffer))))
52fa07ba
MK
2153 (copy-region-as-kill (point) (mark t)))))
2154
2155;; Execute shell command
2156(defun ex-command ()
2157 (let (command)
2158 (save-window-excursion
a1506d29 2159 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
8626cfa2 2160 (set-buffer viper-ex-work-buf)
52fa07ba
MK
2161 (skip-chars-forward " \t")
2162 (setq command (buffer-substring (point) (point-max)))
2163 (end-of-line))
3af0304a 2164 ;; replace # and % with the previous/current file
52fa07ba
MK
2165 (setq command (ex-expand-filsyms command (current-buffer)))
2166 (if (and (> (length command) 0) (string= "!" (substring command 0 1)))
8626cfa2
MK
2167 (if viper-ex-last-shell-com
2168 (setq command
2169 (concat viper-ex-last-shell-com (substring command 1)))
52fa07ba 2170 (error "No previous shell command")))
8626cfa2 2171 (setq viper-ex-last-shell-com command)
52fa07ba
MK
2172 (if (null ex-addresses)
2173 (shell-command command)
2174 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses))))
2175 (if (null beg) (setq beg end))
6c2e12f4 2176 (save-excursion
52fa07ba
MK
2177 (goto-char beg)
2178 (set-mark end)
8626cfa2 2179 (viper-enlarge-region (point) (mark t))
52fa07ba
MK
2180 (shell-command-on-region (point) (mark t) command t))
2181 (goto-char beg)))))
2182
4960e757
MK
2183(defun ex-compile ()
2184 "Reads args from the command line, then runs make with the args.
2185If no args are given, then it runs the last compile command.
2186Type 'mak ' (including the space) to run make with no args."
2187 (let (args)
2188 (save-window-excursion
2189 (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
2190 (set-buffer viper-ex-work-buf)
2191 (setq args (buffer-substring (point) (point-max)))
2192 (end-of-line))
2193 ;; Remove the newline that may (will?) be at the end of the args
2194 (if (string= "\n" (substring args (1- (length args))))
2195 (setq args (substring args 0 (1- (length args)))))
2196 ;; Run last command if no args given, else construct a new command.
2197 (setq args
2198 (if (string= "" args)
2199 (if (boundp 'compile-command)
2200 compile-command
2201 ex-compile-command)
2202 (concat ex-compile-command " " args)))
2203 (compile args)
2204 ))
2205
52fa07ba
MK
2206;; Print line number
2207(defun ex-line-no ()
2208 (message "%d"
2209 (1+ (count-lines
2210 (point-min)
2211 (if (null ex-addresses) (point-max) (car ex-addresses))))))
2212
2213;; Give information on the file visited by the current buffer
8626cfa2 2214(defun viper-info-on-file ()
6c2e12f4 2215 (interactive)
8626cfa2
MK
2216 (let ((pos1 (viper-line-pos 'start))
2217 (pos2 (viper-line-pos 'end))
52fa07ba 2218 lines file info)
8626cfa2 2219 (setq lines (count-lines (point-min) (viper-line-pos 'end))
52fa07ba 2220 file (if (buffer-file-name)
8626cfa2 2221 (concat (viper-abbreviate-file-name (buffer-file-name)) ":")
52fa07ba
MK
2222 (concat (buffer-name) " [Not visiting any file]:"))
2223 info (format "line=%d/%d pos=%d/%d col=%d %s"
2224 (if (= pos1 pos2)
2225 (1+ lines)
2226 lines)
2227 (count-lines (point-min) (point-max))
2228 (point) (1- (point-max))
2229 (1+ (current-column))
2230 (if (buffer-modified-p) "[Modified]" "[Unchanged]")))
2231 (if (< (+ 1 (length info) (length file))
2232 (window-width (minibuffer-window)))
2233 (message (concat file " " info))
2234 (save-window-excursion
8626cfa2 2235 (with-output-to-temp-buffer " *viper-info*"
8e41a31c
MK
2236 (princ (concat "\n" file "\n\n\t" info "\n\n")))
2237 (let ((inhibit-quit t))
2238 (viper-set-unread-command-events (viper-read-event)))
8626cfa2 2239 (kill-buffer " *viper-info*")))
fad2477b 2240 ))
6c2e12f4 2241
b9fe4732
MK
2242
2243;; Without arguments displays info on file. With an arg, sets the visited file
2244;; name to that arg
2245(defun ex-set-visited-file-name ()
2246 (viper-get-ex-file)
2247 (if (string= ex-file "")
2248 (viper-info-on-file)
2249 ;; If ex-file is a directory, use the file portion of the buffer
2250 ;; file name (like ex-write). Do this even if ex-file is a
2251 ;; non-existent directory, since set-visited-file-name signals an
2252 ;; error on this condition, too.
2253 (if (and (string= (file-name-nondirectory ex-file) "")
2254 buffer-file-name
2255 (not (file-directory-p buffer-file-name)))
2256 (setq ex-file (concat (file-name-as-directory ex-file)
2257 (file-name-nondirectory buffer-file-name))))
2258 (set-visited-file-name ex-file)))
2259
2260
e36a387d
MK
2261;; display all variables set through :set
2262(defun ex-show-vars ()
8626cfa2
MK
2263 (with-output-to-temp-buffer " *viper-info*"
2264 (princ (if viper-auto-indent
e36a387d 2265 "autoindent (local)\n" "noautoindent (local)\n"))
a1506d29 2266 (princ (if (default-value 'viper-auto-indent)
e36a387d 2267 "autoindent (global) \n" "noautoindent (global) \n"))
8626cfa2
MK
2268 (princ (if viper-case-fold-search "ignorecase\n" "noignorecase\n"))
2269 (princ (if viper-re-search "magic\n" "nomagic\n"))
e36a387d
MK
2270 (princ (if buffer-read-only "readonly\n" "noreadonly\n"))
2271 (princ (if blink-matching-paren "showmatch\n" "noshowmatch\n"))
8626cfa2
MK
2272 (princ (if viper-search-wrap-around-t "wrapscan\n" "nowrapscan\n"))
2273 (princ (format "shiftwidth \t\t= %S\n" viper-shift-width))
e36a387d
MK
2274 (princ (format "tabstop (local) \t= %S\n" tab-width))
2275 (princ (format "tabstop (global) \t= %S\n" (default-value 'tab-width)))
2276 (princ (format "wrapmargin (local) \t= %S\n"
2277 (- (window-width) fill-column)))
2278 (princ (format "wrapmargin (global) \t= %S\n"
2279 (- (window-width) (default-value 'fill-column))))
2280 (princ (format "shell \t\t\t= %S\n" (if (boundp 'explicit-shell-file-name)
2281 explicit-shell-file-name
2282 'none)))
2283 ))
2284
241d963d
MK
2285(defun ex-print ()
2286 (viper-default-ex-addresses)
2287 (let ((end (car ex-addresses))
2288 (beg (car (cdr ex-addresses))))
2289 (if (> beg end) (error viper-FirstAddrExceedsSecond))
2290 (save-excursion
2291 (viper-enlarge-region beg end)
2292 (if (or ex-g-flag ex-g-variant)
2293 ;; When executing a global command, collect output of each
2294 ;; print in viper-ex-print-buf.
2295 (progn
2296 (append-to-buffer viper-ex-print-buf (point) (mark t))
2297 ;; Is this the last mark for the global command?
2298 (unless (cdr ex-g-marks)
2299 (with-current-buffer viper-ex-print-buf
2300 (ex-print-display-lines (buffer-string))
2301 (erase-buffer))))
2302 (ex-print-display-lines (buffer-substring (point) (mark t)))))))
2303
2304(defun ex-print-display-lines (lines)
2305 (cond
2306 ;; String doesn't contain a newline.
2307 ((not (string-match "\n" lines))
2308 (message "%s" lines))
2309 ;; String contains only one newline at the end. Strip it off.
2310 ((= (string-match "\n" lines) (1- (length lines)))
2311 (message "%s" (substring lines 0 -1)))
2312 ;; String spans more than one line. Use a temporary buffer.
2313 (t
2314 (save-current-buffer
2315 (with-output-to-temp-buffer " *viper-info*"
2316 (princ lines))))))
2317
e36a387d
MK
2318
2319
2320
6c2e12f4 2321
ab5796a9 2322;;; arch-tag: 56b80d36-f880-4d10-bd66-85ad91a295db
60370d40 2323;;; viper-ex.el ends here