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