Initial revision
[bpt/emacs.git] / lisp / emulation / viper-ex.el
CommitLineData
6c2e12f4
KH
1;;; viper-ex.el -- functions implementing the Ex commands for Viper
2
3;; This file is part of GNU Emacs.
4
5;; GNU Emacs is free software; you can redistribute it and/or modify
6;; it under the terms of the GNU General Public License as published by
7;; the Free Software Foundation; either version 2, or (at your option)
8;; any later version.
9
10;; GNU Emacs is distributed in the hope that it will be useful,
11;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13;; GNU General Public License for more details.
14
15;; You should have received a copy of the GNU General Public License
16;; along with GNU Emacs; see the file COPYING. If not, write to
17;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18
19
20(require 'viper-util)
21
22;;; Variables
23
24(defconst vip-ex-work-buf-name " *ex-working-space*")
25(defconst vip-ex-work-buf (get-buffer-create vip-ex-work-buf-name))
26
27
28;;; Completion in :set command
29
30;; The list of Ex commands. Used for completing command names.
31(defconst ex-token-alist
32 '(("!") ("=") (">") ("&") ("~")
33 ("yank") ("xit") ("WWrite") ("Write") ("write") ("wq") ("visual")
34 ("version") ("vglobal") ("unmap") ("undo") ("tag") ("transfer") ("suspend")
35 ("substitute") ("submitReport") ("stop") ("sr") ("source") ("shell")
36 ("set") ("rewind") ("recover") ("read") ("quit") ("pwd")
37 ("put") ("preserve") ("PreviousRelatedFile") ("RelatedFile")
38 ("next") ("Next") ("move") ("mark") ("map") ("kmark") ("join")
39 ("help") ("goto") ("global") ("file") ("edit") ("delete") ("copy")
40 ("chdir") ("cd") ("Buffer") ("buffer") ("args")) )
41
42;; A-list of Ex variables that can be set using the :set command.
43(defconst ex-variable-alist
44 '(("wrapscan") ("ws") ("wrapmargin") ("wm")
45 ("tab-stop-local") ("tsl") ("tabstop") ("ts")
46 ("showmatch") ("sm") ("shiftwidth") ("sw") ("shell") ("sh")
47 ("readonly") ("ro")
48 ("nowrapscan") ("nows") ("noshowmatch") ("nosm")
49 ("noreadonly") ("noro") ("nomagic") ("noma")
50 ("noignorecase") ("noic") ("noautoindent") ("noai")
51 ("magic") ("ma") ("ignorecase") ("ic") ("autoindent") ("ai")
52 ))
53
54
55
56;; Token recognized during parsing of Ex commands (e.g., "read", "comma")
57(defvar ex-token nil)
58
59;; Type of token.
60;; If non-nil, gives type of address; if nil, it is a command.
61(defvar ex-token-type nil)
62
63;; List of addresses passed to Ex command
64(defvar ex-addresses nil)
65
66;; It seems that this flag is used only for `#', `print', and `list', which
67;; aren't implemented. Check later.
68(defvar ex-flag nil)
69
70;; "buffer" where Ex commands keep deleted data.
71;; In Emacs terms, this is a register.
72(defvar ex-buffer nil)
73
74;; Value of ex count.
75(defvar ex-count nil)
76
77;; Flag for global command.
78(defvar ex-g-flag nil)
79
80;; If t, global command is executed on lines not matching ex-g-pat.
81(defvar ex-g-variant nil)
82
83;; Save reg-exp used in substitute.
84(defvar ex-reg-exp nil)
85
86
87;; Replace pattern for substitute.
88(defvar ex-repl nil)
89
90;; Pattern for global command.
91(defvar ex-g-pat nil)
92
93;; `sh' doesn't seem to expand wildcards, like `*'
94(defconst ex-find-file-shell "csh"
95 "Shell in which to interpret wildcards.")
96(defvar ex-find-file-shell-options "-f"
97 "*Options to pass to `ex-find-file-shell'.")
98
99;; Remembers the previous Ex tag.
100(defvar ex-tag nil)
101
102;; file used by Ex commands like :r, :w, :n
103(defvar ex-file nil)
104
105;; If t, tells Ex that this is a variant-command, i.e., w>>, r!, etc.
106(defvar ex-variant nil)
107
108;; Specified the offset of an Ex command, such as :read.
109(defvar ex-offset nil)
110
111;; Tells Ex that this is a w>> command.
112(defvar ex-append nil)
113
114;; File containing the shell command to be executed at Ex prompt,
115;; e.g., :r !date
116(defvar ex-cmdfile nil)
117
118;; flag used in vip-ex-read-file-name to indicate that we may be reading
119;; multiple file names. Used for :edit and :next
120(defvar vip-keep-reading-filename nil)
121
122(defconst ex-cycle-other-window t
123 "*If t, :n and :b cycles through files and buffers in other window.
124Then :N and :B cycles in the current window. If nil, this behavior is
125reversed.")
126
127(defconst ex-cycle-through-non-files nil
128 "*Cycle through *scratch* and other buffers that don't visit any file.")
129
130;; Last shell command executed with :! command.
131(defvar vip-ex-last-shell-com nil)
132
133;; Indicates if Minibuffer was exited temporarily in Ex-command.
134(defvar vip-incomplete-ex-cmd nil)
135
136;; Remembers the last ex-command prompt.
137(defvar vip-last-ex-prompt "")
138
139
140;;; Code
141
142(defun vip-check-sub (str)
143 "Check if ex-token is an initial segment of STR."
144 (let ((length (length ex-token)))
145 (if (and (<= length (length str))
146 (string= ex-token (substring str 0 length)))
147 (setq ex-token str)
148 (setq ex-token-type 'non-command))))
149
150(defun vip-get-ex-com-subr ()
151 "Get a complete ex command."
152 (let (case-fold-search)
153 (set-mark (point))
154 (re-search-forward "[a-zA-Z][a-zA-Z]*")
155 (setq ex-token-type 'command)
156 (setq ex-token (buffer-substring (point) (mark t)))
157 (exchange-point-and-mark)
158 (cond ((looking-at "a")
159 (cond ((looking-at "ab") (vip-check-sub "abbreviate"))
160 ((looking-at "ar") (vip-check-sub "args"))
161 (t (vip-check-sub "append"))))
162 ((looking-at "h") (vip-check-sub "help"))
163 ((looking-at "c")
164 (cond ((looking-at "cd") (vip-check-sub "cd"))
165 ((looking-at "ch") (vip-check-sub "chdir"))
166 ((looking-at "co") (vip-check-sub "copy"))
167 (t (vip-check-sub "change"))))
168 ((looking-at "d") (vip-check-sub "delete"))
169 ((looking-at "b") (vip-check-sub "buffer"))
170 ((looking-at "B") (vip-check-sub "Buffer"))
171 ((looking-at "e")
172 (if (looking-at "ex") (vip-check-sub "ex")
173 (vip-check-sub "edit")))
174 ((looking-at "f") (vip-check-sub "file"))
175 ((looking-at "g") (vip-check-sub "global"))
176 ((looking-at "i") (vip-check-sub "insert"))
177 ((looking-at "j") (vip-check-sub "join"))
178 ((looking-at "l") (vip-check-sub "list"))
179 ((looking-at "m")
180 (cond ((looking-at "map") (vip-check-sub "map"))
181 ((looking-at "mar") (vip-check-sub "mark"))
182 (t (vip-check-sub "move"))))
183 ((looking-at "k[a-z][^a-z]")
184 (setq ex-token "kmark")
185 (forward-char 1)
186 (exchange-point-and-mark)) ;; this is canceled out by another
187 ;; exchange-point-and-mark at the end
188 ((looking-at "k") (vip-check-sub "kmark"))
189 ((looking-at "n") (if (looking-at "nu")
190 (vip-check-sub "number")
191 (vip-check-sub "next")))
192 ((looking-at "N") (vip-check-sub "Next"))
193 ((looking-at "o") (vip-check-sub "open"))
194 ((looking-at "p")
195 (cond ((looking-at "pre") (vip-check-sub "preserve"))
196 ((looking-at "pu") (vip-check-sub "put"))
197 ((looking-at "pw") (vip-check-sub "pwd"))
198 (t (vip-check-sub "print"))))
199 ((looking-at "P") (vip-check-sub "PreviousRelatedFile"))
200 ((looking-at "R") (vip-check-sub "RelatedFile"))
201 ((looking-at "q") (vip-check-sub "quit"))
202 ((looking-at "r")
203 (cond ((looking-at "rec") (vip-check-sub "recover"))
204 ((looking-at "rew") (vip-check-sub "rewind"))
205 (t (vip-check-sub "read"))))
206 ((looking-at "s")
207 (cond ((looking-at "se") (vip-check-sub "set"))
208 ((looking-at "sh") (vip-check-sub "shell"))
209 ((looking-at "so") (vip-check-sub "source"))
210 ((looking-at "sr") (vip-check-sub "sr"))
211 ((looking-at "st") (vip-check-sub "stop"))
212 ((looking-at "sus") (vip-check-sub "suspend"))
213 ((looking-at "subm") (vip-check-sub "submitReport"))
214 (t (vip-check-sub "substitute"))))
215 ((looking-at "t")
216 (if (looking-at "ta") (vip-check-sub "tag")
217 (vip-check-sub "transfer")))
218 ((looking-at "u")
219 (cond ((looking-at "una") (vip-check-sub "unabbreviate"))
220 ((looking-at "unm") (vip-check-sub "unmap"))
221 (t (vip-check-sub "undo"))))
222 ((looking-at "v")
223 (cond ((looking-at "ve") (vip-check-sub "version"))
224 ((looking-at "vi") (vip-check-sub "visual"))
225 (t (vip-check-sub "vglobal"))))
226 ((looking-at "w")
227 (if (looking-at "wq") (vip-check-sub "wq")
228 (vip-check-sub "write")))
229 ((looking-at "W")
230 (if (looking-at "WW")
231 (vip-check-sub "WWrite")
232 (vip-check-sub "Write")))
233 ((looking-at "x") (vip-check-sub "xit"))
234 ((looking-at "y") (vip-check-sub "yank"))
235 ((looking-at "z") (vip-check-sub "z")))
236 (exchange-point-and-mark)
237 ))
238
239(defun vip-get-ex-token ()
240 "Get an ex-token which is either an address or a command.
241A token has a type, \(command, address, end-mark\), and a value."
242 (save-window-excursion
243 (set-buffer vip-ex-work-buf)
244 (skip-chars-forward " \t|")
245 (cond ((looking-at "#")
246 (setq ex-token-type 'command)
247 (setq ex-token (char-to-string (following-char)))
248 (forward-char 1))
249 ((looking-at "[a-z]") (vip-get-ex-com-subr))
250 ((looking-at "\\.")
251 (forward-char 1)
252 (setq ex-token-type 'dot))
253 ((looking-at "[0-9]")
254 (set-mark (point))
255 (re-search-forward "[0-9]*")
256 (setq ex-token-type
257 (cond ((eq ex-token-type 'plus) 'add-number)
258 ((eq ex-token-type 'minus) 'sub-number)
259 (t 'abs-number)))
260 (setq ex-token (string-to-int (buffer-substring (point) (mark t)))))
261 ((looking-at "\\$")
262 (forward-char 1)
263 (setq ex-token-type 'end))
264 ((looking-at "%")
265 (forward-char 1)
266 (setq ex-token-type 'whole))
267 ((looking-at "+")
268 (cond ((or (looking-at "+[-+]") (looking-at "+[\n|]"))
269 (forward-char 1)
270 (insert "1")
271 (backward-char 1)
272 (setq ex-token-type 'plus))
273 ((looking-at "+[0-9]")
274 (forward-char 1)
275 (setq ex-token-type 'plus))
276 (t
277 (error vip-BadAddress))))
278 ((looking-at "-")
279 (cond ((or (looking-at "-[-+]") (looking-at "-[\n|]"))
280 (forward-char 1)
281 (insert "1")
282 (backward-char 1)
283 (setq ex-token-type 'minus))
284 ((looking-at "-[0-9]")
285 (forward-char 1)
286 (setq ex-token-type 'minus))
287 (t
288 (error vip-BadAddress))))
289 ((looking-at "/")
290 (forward-char 1)
291 (set-mark (point))
292 (let ((cont t))
293 (while (and (not (eolp)) cont)
294 ;;(re-search-forward "[^/]*/")
295 (re-search-forward "[^/]*\\(/\\|\n\\)")
296 (if (not (vip-looking-back "[^\\\\]\\(\\\\\\\\\\)*\\\\/"))
297 (setq cont nil))))
298 (backward-char 1)
299 (setq ex-token (buffer-substring (point) (mark t)))
300 (if (looking-at "/") (forward-char 1))
301 (setq ex-token-type 'search-forward))
302 ((looking-at "\\?")
303 (forward-char 1)
304 (set-mark (point))
305 (let ((cont t))
306 (while (and (not (eolp)) cont)
307 ;;(re-search-forward "[^\\?]*\\?")
308 (re-search-forward "[^\\?]*\\(\\?\\|\n\\)")
309 (if (not (vip-looking-back "[^\\\\]\\(\\\\\\\\\\)*\\\\\\?"))
310 (setq cont nil))
311 (backward-char 1)
312 (if (not (looking-at "\n")) (forward-char 1))))
313 (setq ex-token-type 'search-backward)
314 (setq ex-token (buffer-substring (1- (point)) (mark t))))
315 ((looking-at ",")
316 (forward-char 1)
317 (setq ex-token-type 'comma))
318 ((looking-at ";")
319 (forward-char 1)
320 (setq ex-token-type 'semi-colon))
321 ((looking-at "[!=><&~]")
322 (setq ex-token-type 'command)
323 (setq ex-token (char-to-string (following-char)))
324 (forward-char 1))
325 ((looking-at "'")
326 (setq ex-token-type 'goto-mark)
327 (forward-char 1)
328 (cond ((looking-at "'") (setq ex-token nil))
329 ((looking-at "[a-z]") (setq ex-token (following-char)))
330 (t (error "Marks are ' and a-z")))
331 (forward-char 1))
332 ((looking-at "\n")
333 (setq ex-token-type 'end-mark)
334 (setq ex-token "goto"))
335 (t
336 (error vip-BadExCommand)))))
337
338;; Reads Ex command. Tries to determine if it has to exit because command
339;; is complete or invalid. If not, keeps reading command.
340(defun ex-cmd-read-exit ()
341 (interactive)
342 (setq vip-incomplete-ex-cmd t)
343 (let ((quit-regex1 (concat
344 "\\("
345 "set[ \t]*" "\\|" "edit[ \t]*" "\\|" "[nN]ext[ \t]*"
346 "\\|" "unm[ \t]*" "\\|" "^[ \t]*rep"
347 "\\)"))
348 (quit-regex2 (concat
349 "[a-zA-Z][ \t]*"
350 "\\(" "!" "\\|" ">>"
351 "\\|" "\\+[0-9]+"
352 "\\)"
353 "*[ \t]*$"))
354 (stay-regex (concat
355 "\\("
356 "^[ \t]*$" "\\|" "[ktgjmsz][ \t]*$" "\\|" "^[ \t]*ab.*"
357 "\\|" "tr[ansfer \t]*" "\\|" "sr[ \t]*"
358 "\\|" "mo.*" "\\|" "^[ \t]*k?ma[^p]*"
359 "\\|" "^[ \t]*fi.*" "\\|" "v?gl.*" "\\|" "[vg][ \t]*$"
360 "\\|" "jo.*" "\\|" "^[ \t]*ta.*" "\\|" "^[ \t]*una.*"
361 "\\|" "^[ \t]*su.*" "\\|['`][a-z][ \t]*"
362 "\\|" "![ \t]*[a-zA-Z].*"
363 "\\)"
364 "!*")))
365
366 (save-window-excursion ;; put cursor at the end of the Ex working buffer
367 (set-buffer vip-ex-work-buf)
368 (goto-char (point-max)))
369 (cond ((vip-looking-back quit-regex1) (exit-minibuffer))
370 ((vip-looking-back stay-regex) (insert " "))
371 ((vip-looking-back quit-regex2) (exit-minibuffer))
372 (t (insert " ")))))
373
374;; complete Ex command
375(defun ex-cmd-complete ()
376 (interactive)
377 (let (save-pos dist compl-list string-to-complete completion-result)
378
379 (save-excursion
380 (setq dist (skip-chars-backward "[a-zA-Z!=>&~]")
381 save-pos (point)))
382
383 (if (or (= dist 0)
384 (vip-looking-back "\\([ \t]*['`][ \t]*[a-z]*\\)")
385 (vip-looking-back
386 "^[ \t]*[a-zA-Z!=>&~][ \t]*[/?]*+[ \t]+[a-zA-Z!=>&~]+"))
387 ;; Preceding characters are not the ones allowed in an Ex command
388 ;; or we have typed past command name.
389 ;; Note: we didn't do parsing, so there may be surprises.
390 (if (or (vip-looking-back "[a-zA-Z!=>&~][ \t]*[/?]*[ \t]*")
391 (vip-looking-back "\\([ \t]*['`][ \t]*[a-z]*\\)")
392 (looking-at "[^ \t\n\C-m]"))
393 nil
394 (with-output-to-temp-buffer "*Completions*"
395 (display-completion-list
396 (vip-alist-to-list ex-token-alist))))
397 ;; Preceding chars may be part of a command name
398 (setq string-to-complete (buffer-substring save-pos (point)))
399 (setq completion-result
400 (try-completion string-to-complete ex-token-alist))
401
402 (cond ((eq completion-result t) ;; exact match--do nothing
403 (vip-tmp-insert-at-eob " (Sole completion)"))
404 ((eq completion-result nil)
405 (vip-tmp-insert-at-eob " (No match)"))
406 (t ;; partial completion
407 (goto-char save-pos)
408 (delete-region (point) (point-max))
409 (insert completion-result)
410 (let (case-fold-search)
411 (setq compl-list
412 (vip-filter-alist (concat "^" completion-result)
413 ex-token-alist)))
414 (if (> (length compl-list) 1)
415 (with-output-to-temp-buffer "*Completions*"
416 (display-completion-list
417 (vip-alist-to-list (reverse compl-list)))))))
418 )))
419
420(defun vip-ex (&optional string)
421 "Ex commands within Viper."
422 (interactive)
423 (or string
424 (setq ex-g-flag nil
425 ex-g-variant nil))
426 (let* ((map (copy-keymap minibuffer-local-map))
427 (address nil)
428 (cont t)
429 (dot (point))
430 com-str)
431
432 (vip-add-keymap vip-ex-cmd-map map)
433
434 (setq com-str (or string (vip-read-string-with-history
435 ":"
436 nil
437 'vip-ex-history
438 (car vip-ex-history)
439 map)))
440 (save-window-excursion
441 ;; just a precaution
442 (or (vip-buffer-live-p vip-ex-work-buf)
443 (setq vip-ex-work-buf (get-buffer-create vip-ex-work-buf-name)))
444 (set-buffer vip-ex-work-buf)
445 (delete-region (point-min) (point-max))
446 (insert com-str "\n")
447 (goto-char (point-min)))
448 (setq ex-token-type nil
449 ex-addresses nil)
450 (while cont
451 (vip-get-ex-token)
452 (cond ((memq ex-token-type '(command end-mark))
453 (if address (setq ex-addresses (cons address ex-addresses)))
454 (cond ((string= ex-token "global")
455 (ex-global nil)
456 (setq cont nil))
457 ((string= ex-token "vglobal")
458 (ex-global t)
459 (setq cont nil))
460 (t
461 (vip-execute-ex-command)
462 (save-window-excursion
463 (set-buffer vip-ex-work-buf)
464 (skip-chars-forward " \t")
465 (cond ((looking-at "|")
466 (forward-char 1))
467 ((looking-at "\n")
468 (setq cont nil))
469 (t (error "`%s': %s" ex-token vip-SpuriousText)))
470 ))
471 ))
472 ((eq ex-token-type 'non-command)
473 (error (format "`%s': %s" ex-token vip-BadExCommand)))
474 ((eq ex-token-type 'whole)
475 (setq ex-addresses
476 (cons (point-max) (cons (point-min) ex-addresses))))
477 ((eq ex-token-type 'comma)
478 (setq ex-addresses
479 (cons (if (null address) (point) address) ex-addresses)))
480 ((eq ex-token-type 'semi-colon)
481 (if address (setq dot address))
482 (setq ex-addresses
483 (cons (if (null address) (point) address) ex-addresses)))
484 (t (let ((ans (vip-get-ex-address-subr address dot)))
485 (if ans (setq address ans))))))))
486
487(defun vip-get-ex-pat ()
488 "Get a regular expression and set `ex-variant', if found."
489 (save-window-excursion
490 (set-buffer vip-ex-work-buf)
491 (skip-chars-forward " \t")
492 (if (looking-at "!")
493 (progn
494 (setq ex-g-variant (not ex-g-variant)
495 ex-g-flag (not ex-g-flag))
496 (forward-char 1)
497 (skip-chars-forward " \t")))
498 (let ((c (following-char)))
499 (if (string-match "[0-9A-Za-z]" (format "%c" c))
500 (error
501 "Global regexp must be inside matching non-alphanumeric chars"))
502 (if (looking-at "[^\\\\\n]")
503 (progn
504 (forward-char 1)
505 (set-mark (point))
506 (let ((cont t))
507 (while (and (not (eolp)) cont)
508 (if (not (re-search-forward (format "[^%c]*%c" c c) nil t))
509 (if (member ex-token '("global" "vglobal"))
510 (error
511 "Missing closing delimiter for global regexp")
512 (goto-char (point-max))))
513 (if (not (vip-looking-back
514 (format "[^\\\\]\\(\\\\\\\\\\)*\\\\%c" c)))
515 (setq cont nil))))
516 (setq ex-token
517 (if (= (mark t) (point)) ""
518 (buffer-substring (1- (point)) (mark t))))
519 (backward-char 1))
520 (setq ex-token nil))
521 c)))
522
523(defun vip-get-ex-command ()
524 "get an ex command"
525 (save-window-excursion
526 (set-buffer vip-ex-work-buf)
527 (if (looking-at "/") (forward-char 1))
528 (skip-chars-forward " \t")
529 (cond ((looking-at "[a-z]")
530 (vip-get-ex-com-subr)
531 (if (eq ex-token-type 'non-command)
532 (error "`%s': %s" ex-token vip-BadExCommand)))
533 ((looking-at "[!=><&~]")
534 (setq ex-token (char-to-string (following-char)))
535 (forward-char 1))
536 (t (error vip-BadExCommand)))))
537
538(defun vip-get-ex-opt-gc (c)
539 "Get an Ex option g or c."
540 (save-window-excursion
541 (set-buffer vip-ex-work-buf)
542 (if (looking-at (format "%c" c)) (forward-char 1))
543 (skip-chars-forward " \t")
544 (cond ((looking-at "g")
545 (setq ex-token "g")
546 (forward-char 1)
547 t)
548 ((looking-at "c")
549 (setq ex-token "c")
550 (forward-char 1)
551 t)
552 (t nil))))
553
554(defun vip-default-ex-addresses (&optional whole-flag)
555 "Compute default addresses. WHOLE-FLAG means use the whole buffer."
556 (cond ((null ex-addresses)
557 (setq ex-addresses
558 (if whole-flag
559 (cons (point-max) (cons (point-min) nil))
560 (cons (point) (cons (point) nil)))))
561 ((null (cdr ex-addresses))
562 (setq ex-addresses
563 (cons (car ex-addresses) ex-addresses)))))
564
565(defun vip-get-ex-address ()
566 "Get an ex-address as a marker and set ex-flag if a flag is found."
567 (let ((address (point-marker)) (cont t))
568 (setq ex-token "")
569 (setq ex-flag nil)
570 (while cont
571 (vip-get-ex-token)
572 (cond ((eq ex-token-type 'command)
573 (if (member ex-token '("print" "list" "#"))
574 (progn
575 (setq ex-flag t
576 cont nil))
577 (error "Address expected in this Ex command")))
578 ((eq ex-token-type 'end-mark)
579 (setq cont nil))
580 ((eq ex-token-type 'whole)
581 (error "Trailing address expected"))
582 ((eq ex-token-type 'comma)
583 (error "`%s': %s" ex-token vip-SpuriousText))
584 (t (let ((ans (vip-get-ex-address-subr address (point-marker))))
585 (if ans (setq address ans))))))
586 address))
587
588(defun vip-get-ex-address-subr (old-address dot)
589 "Returns an address as a point."
590 (let ((address nil))
591 (if (null old-address) (setq old-address dot))
592 (cond ((eq ex-token-type 'dot)
593 (setq address dot))
594 ((eq ex-token-type 'add-number)
595 (save-excursion
596 (goto-char old-address)
597 (forward-line (if (= old-address 0) (1- ex-token) ex-token))
598 (setq address (point-marker))))
599 ((eq ex-token-type 'sub-number)
600 (save-excursion
601 (goto-char old-address)
602 (forward-line (- ex-token))
603 (setq address (point-marker))))
604 ((eq ex-token-type 'abs-number)
605 (save-excursion
606 (goto-char (point-min))
607 (if (= ex-token 0) (setq address 0)
608 (forward-line (1- ex-token))
609 (setq address (point-marker)))))
610 ((eq ex-token-type 'end)
611 (setq address (point-max-marker)))
612 ((eq ex-token-type 'plus) t);; do nothing
613 ((eq ex-token-type 'minus) t);; do nothing
614 ((eq ex-token-type 'search-forward)
615 (save-excursion
616 (ex-search-address t)
617 (setq address (point-marker))))
618 ((eq ex-token-type 'search-backward)
619 (save-excursion
620 (ex-search-address nil)
621 (setq address (point-marker))))
622 ((eq ex-token-type 'goto-mark)
623 (save-excursion
624 (if (null ex-token)
625 (exchange-point-and-mark)
626 (goto-char (vip-register-to-point
627 (1+ (- ex-token ?a)) 'enforce-buffer)))
628 (setq address (point-marker)))))
629 address))
630
631
632(defun ex-search-address (forward)
633 "Search pattern and set address."
634 (if (string= ex-token "")
635 (if (null vip-s-string)
636 (error vip-NoPrevSearch)
637 (setq ex-token vip-s-string))
638 (setq vip-s-string ex-token))
639 (if forward
640 (progn
641 (forward-line 1)
642 (re-search-forward ex-token))
643 (forward-line -1)
644 (re-search-backward ex-token)))
645
646(defun vip-get-ex-buffer ()
647 "Get a buffer name and set `ex-count' and `ex-flag' if found."
648 (setq ex-buffer nil)
649 (setq ex-count nil)
650 (setq ex-flag nil)
651 (save-window-excursion
652 (set-buffer vip-ex-work-buf)
653 (skip-chars-forward " \t")
654 (if (looking-at "[a-zA-Z]")
655 (progn
656 (setq ex-buffer (following-char))
657 (forward-char 1)
658 (skip-chars-forward " \t")))
659 (if (looking-at "[0-9]")
660 (progn
661 (set-mark (point))
662 (re-search-forward "[0-9][0-9]*")
663 (setq ex-count (string-to-int (buffer-substring (point) (mark t))))
664 (skip-chars-forward " \t")))
665 (if (looking-at "[pl#]")
666 (progn
667 (setq ex-flag t)
668 (forward-char 1)))
669 (if (not (looking-at "[\n|]"))
670 (error "`%s': %s" ex-token vip-SpuriousText))))
671
672(defun vip-get-ex-count ()
673 (setq ex-variant nil
674 ex-count nil
675 ex-flag nil)
676 (save-window-excursion
677 (set-buffer vip-ex-work-buf)
678 (skip-chars-forward " \t")
679 (if (looking-at "!")
680 (progn
681 (setq ex-variant t)
682 (forward-char 1)))
683 (skip-chars-forward " \t")
684 (if (looking-at "[0-9]")
685 (progn
686 (set-mark (point))
687 (re-search-forward "[0-9][0-9]*")
688 (setq ex-count (string-to-int (buffer-substring (point) (mark t))))
689 (skip-chars-forward " \t")))
690 (if (looking-at "[pl#]")
691 (progn
692 (setq ex-flag t)
693 (forward-char 1)))
694 (if (not (looking-at "[\n|]"))
695 (error "`%s': %s"
696 (buffer-substring (point-min) (1- (point-max))) vip-BadExCommand))))
697
698(defun ex-expand-filsyms (cmd buf)
699 "Expand \% and \# in ex command."
700 (let (cf pf ret)
701 (save-excursion
702 (set-buffer buf)
703 (setq cf buffer-file-name)
704 (setq pf (ex-next nil t))) ;; this finds alternative file name
705 (if (and (null cf) (string-match "[^\\]%\\|\\`%" cmd))
706 (error "No current file to substitute for `\%'"))
707 (if (and (null pf) (string-match "[^\\]#\\|\\`#" cmd))
708 (error "No alternate file to substitute for `#'"))
709 (save-excursion
710 (set-buffer (get-buffer-create " ex-tmp"))
711 (insert cmd)
712 (goto-char (point-min))
713 (while (re-search-forward "%\\|#" nil t)
714 (let ((data (match-data))
715 (char (buffer-substring (match-beginning 0) (match-end 0))))
716 (if (vip-looking-back (concat "\\\\" char))
717 (replace-match char)
718 (store-match-data data)
719 (if (string= char "%")
720 (replace-match cf)
721 (replace-match pf)))))
722 (end-of-line)
723 (setq ret (buffer-substring (point-min) (point)))
724 (kill-buffer (current-buffer))
725 (message "%s" ret))
726 ret))
727
728(defun vip-get-ex-file ()
729 "Get a file name and set ex-variant, `ex-append' and `ex-offset' if found."
730 (let (prompt)
731 (setq ex-file nil
732 ex-variant nil
733 ex-append nil
734 ex-offset nil
735 ex-cmdfile nil)
736 (save-excursion
737 (save-window-excursion
738 (set-buffer vip-ex-work-buf)
739 (skip-chars-forward " \t")
740 (if (looking-at "!")
741 (if (not (vip-looking-back "[ \t]"))
742 (progn
743 (setq ex-variant t)
744 (forward-char 1)
745 (skip-chars-forward " \t"))
746 (setq ex-cmdfile t)
747 (forward-char 1)
748 (skip-chars-forward " \t")))
749 (if (looking-at ">>")
750 (progn
751 (setq ex-append t
752 ex-variant t)
753 (forward-char 2)
754 (skip-chars-forward " \t")))
755 (if (looking-at "+")
756 (progn
757 (forward-char 1)
758 (set-mark (point))
759 (re-search-forward "[ \t\n]")
760 (backward-char 1)
761 (setq ex-offset (buffer-substring (point) (mark t)))
762 (forward-char 1)
763 (skip-chars-forward " \t")))
764 ;; this takes care of :r, :w, etc., when they get file names
765 ;; from the history list
766 (if (member ex-token '("read" "write" "edit" "visual"))
767 (progn
768 (setq ex-file (buffer-substring (point) (1- (point-max))))
769 (setq ex-file
770 ;; For :e, match multiple non-white strings separated
771 ;; by white. For others, find the first non-white string
772 (if (string-match
773 (if (string= ex-token "edit")
774 "[^ \t\n]+\\([ \t]+[^ \t\n]+\\)*"
775 "[^ \t\n]+")
776 ex-file)
777 (progn
778 ;; if file name comes from history, don't leave
779 ;; minibuffer when the user types space
780 (setq vip-incomplete-ex-cmd nil)
781 ;; this must be the last clause in this progn
782 (substring ex-file (match-beginning 0) (match-end 0))
783 )
784 ""))
785 ;; this leaves only the command name in the work area
786 ;; file names are gone
787 (delete-region (point) (1- (point-max)))
788 ))
789 (goto-char (point-max))
790 (skip-chars-backward " \t\n")
791 (setq prompt (buffer-substring (point-min) (point)))
792 ))
793
794 (setq vip-last-ex-prompt prompt)
795
796 ;; If we just finished reading command, redisplay prompt
797 (if vip-incomplete-ex-cmd
798 (setq ex-file (vip-ex-read-file-name (format ":%s " prompt)))
799 ;; file was typed in-line
800 (setq ex-file (or ex-file "")))
801 ))
802
803
804;; Completes file name or exits minibuffer. If Ex command accepts multiple
805;; file names, arranges to re-enter the minibuffer.
806(defun vip-complete-filename-or-exit ()
807 (interactive)
808 (setq vip-keep-reading-filename t)
809 ;; don't exit if directory---ex-commands don't
810 (cond ((ex-cmd-accepts-multiple-files-p ex-token) (exit-minibuffer))
811 (t (minibuffer-complete-word))))
812
813
814(defun ex-cmd-accepts-multiple-files-p (token)
815 (member token '("edit" "next" "Next")))
816
817;; If user doesn't enter anything, then "" is returned, i.e., the
818;; prompt-directory is not returned.
819(defun vip-ex-read-file-name (prompt)
820 (let* ((str "")
821 (minibuffer-local-completion-map
822 (copy-keymap minibuffer-local-completion-map))
823 beg end cont val)
824
825 (vip-add-keymap ex-read-filename-map minibuffer-local-completion-map)
826
827 (setq cont (setq vip-keep-reading-filename t))
828 (while cont
829 (setq vip-keep-reading-filename nil
830 val (read-file-name (concat prompt str) nil default-directory)
831 str (concat str (if (equal val "") "" " ")
832 val (if (equal val "") "" " ")))
833
834 ;; Only edit, next, and Next commands accept multiple files.
835 ;; vip-keep-reading-filename is set in the anonymous function that is
836 ;; bound to " " in ex-read-filename-map.
837 (setq cont (and vip-keep-reading-filename
838 (ex-cmd-accepts-multiple-files-p ex-token)))
839 )
840
841 (setq beg (string-match "[^ \t]" str) ;; delete leading blanks
842 end (string-match "[ \t]*$" str)) ;; delete trailing blanks
843 (if (member ex-token '("read" "write"))
844 (if (string-match "[\t ]*!" str)
845 ;; this is actually a shell command
846 (progn
847 (setq ex-cmdfile t)
848 (setq beg (1+ beg))
849 (setq vip-last-ex-prompt (concat vip-last-ex-prompt " !")))))
850 (substring str (or beg 0) end)))
851
852(defun vip-execute-ex-command ()
853 "Execute ex command using the value of addresses."
854 (vip-deactivate-mark)
855 (cond ((string= ex-token "args") (ex-args))
856 ((string= ex-token "copy") (ex-copy nil))
857 ((string= ex-token "cd") (ex-cd))
858 ((string= ex-token "chdir") (ex-cd))
859 ((string= ex-token "delete") (ex-delete))
860 ((string= ex-token "edit") (ex-edit))
861 ((string= ex-token "file") (vip-info-on-file))
862 ((string= ex-token "goto") (ex-goto))
863 ((string= ex-token "help") (ex-help))
864 ((string= ex-token "join") (ex-line "join"))
865 ((string= ex-token "kmark") (ex-mark))
866 ((string= ex-token "mark") (ex-mark))
867 ((string= ex-token "map") (ex-map))
868 ((string= ex-token "move") (ex-copy t))
869 ((string= ex-token "next") (ex-next ex-cycle-other-window))
870 ((string= ex-token "Next") (ex-next (not ex-cycle-other-window)))
871 ((string= ex-token "RelatedFile") (ex-next-related-buffer 1))
872 ((string= ex-token "put") (ex-put))
873 ((string= ex-token "pwd") (ex-pwd))
874 ((string= ex-token "preserve") (ex-preserve))
875 ((string= ex-token "PreviousRelatedFile") (ex-next-related-buffer -1))
876 ((string= ex-token "quit") (ex-quit))
877 ((string= ex-token "read") (ex-read))
878 ((string= ex-token "recover") (ex-recover))
879 ((string= ex-token "rewind") (ex-rewind))
880 ((string= ex-token "submitReport") (vip-submit-report))
881 ((string= ex-token "set") (ex-set))
882 ((string= ex-token "shell") (ex-shell))
883 ((string= ex-token "source") (ex-source))
884 ((string= ex-token "sr") (ex-substitute t t))
885 ((string= ex-token "substitute") (ex-substitute))
886 ((string= ex-token "suspend") (suspend-emacs))
887 ((string= ex-token "stop") (suspend-emacs))
888 ((string= ex-token "transfer") (ex-copy nil))
889 ((string= ex-token "buffer") (if ex-cycle-other-window
890 (vip-switch-to-buffer-other-window)
891 (vip-switch-to-buffer)))
892 ((string= ex-token "Buffer") (if ex-cycle-other-window
893 (vip-switch-to-buffer)
894 (vip-switch-to-buffer-other-window)))
895 ((string= ex-token "tag") (ex-tag))
896 ((string= ex-token "undo") (vip-undo))
897 ((string= ex-token "unmap") (ex-unmap))
898 ((string= ex-token "version") (vip-version))
899 ((string= ex-token "visual") (ex-edit))
900 ((string= ex-token "write") (ex-write nil))
901 ((string= ex-token "Write") (save-some-buffers))
902 ((string= ex-token "wq") (ex-write t))
903 ((string= ex-token "WWrite") (save-some-buffers t)) ; don't ask
904 ((string= ex-token "xit") (ex-write t))
905 ((string= ex-token "yank") (ex-yank))
906 ((string= ex-token "!") (ex-command))
907 ((string= ex-token "=") (ex-line-no))
908 ((string= ex-token ">") (ex-line "right"))
909 ((string= ex-token "<") (ex-line "left"))
910 ((string= ex-token "&") (ex-substitute t))
911 ((string= ex-token "~") (ex-substitute t t))
912 ((or (string= ex-token "append")
913 (string= ex-token "change")
914 (string= ex-token "insert")
915 (string= ex-token "open"))
916 (error
917 (format "`%s': Obsolete command, not supported by Viper"
918 ex-token)))
919 ((or (string= ex-token "abbreviate")
920 (string= ex-token "unabbreviate"))
921 (error
922 (format
923 "`%s': Vi's abbrevs are obsolete. Use more powerful Emacs' abbrevs"
924 ex-token)))
925 ((or (string= ex-token "list")
926 (string= ex-token "print")
927 (string= ex-token "z")
928 (string= ex-token "#"))
929 (error
930 (format "`%s': Command not implemented in Viper" ex-token)))
931 (t (error (format "`%s': %s" ex-token vip-BadExCommand)))))
932
933(defun vip-undisplayed-files ()
934 (mapcar
935 (function
936 (lambda (b)
937 (if (null (get-buffer-window b))
938 (let ((f (buffer-file-name b)))
939 (if f f
940 (if ex-cycle-through-non-files
941 (let ((s (buffer-name b)))
942 (if (string= " " (substring s 0 1))
943 nil
944 s))
945 nil)))
946 nil)))
947 (buffer-list)))
948
949
950(defun ex-args ()
951 (let ((l (vip-undisplayed-files))
952 (args "")
953 (file-count 1))
954 (while (not (null l))
955 (if (car l)
956 (setq args (format "%s %d) %s\n" args file-count (car l))
957 file-count (1+ file-count)))
958 (setq l (cdr l)))
959 (if (string= args "")
960 (message "All files are already displayed")
961 (save-excursion
962 (save-window-excursion
963 (with-output-to-temp-buffer " *vip-info*"
964 (princ "\n\nThese files are not displayed in any window.\n")
965 (princ "\n=============\n")
966 (princ args)
967 (princ "\n=============\n")
968 (princ "\nThe numbers can be given as counts to :next. ")
969 (princ "\n\nPress any key to continue...\n\n"))
970 (vip-read-char-exclusive))))))
971
972(defun ex-cd ()
973 "Ex cd command. Default directory of this buffer changes."
974 (vip-get-ex-file)
975 (if (string= ex-file "")
976 (setq ex-file "~"))
977 (setq default-directory (file-name-as-directory (expand-file-name ex-file))))
978
979(defun ex-copy (del-flag)
980 "Ex copy and move command. DEL-FLAG means delete."
981 (vip-default-ex-addresses)
982 (let ((address (vip-get-ex-address))
983 (end (car ex-addresses)) (beg (car (cdr ex-addresses))))
984 (goto-char end)
985 (save-excursion
986 (push-mark beg t)
987 (vip-enlarge-region (mark t) (point))
988 (if del-flag
989 (kill-region (point) (mark t))
990 (copy-region-as-kill (point) (mark t)))
991 (if ex-flag
992 (progn
993 (with-output-to-temp-buffer "*copy text*"
994 (princ
995 (if (or del-flag ex-g-flag ex-g-variant)
996 (current-kill 0)
997 (buffer-substring (point) (mark t)))))
998 (condition-case nil
999 (progn
1000 (read-string "[Hit return to continue] ")
1001 (save-excursion (kill-buffer "*copy text*")))
1002 (quit (save-excursion (kill-buffer "*copy text*"))
1003 (signal 'quit nil))))))
1004 (if (= address 0)
1005 (goto-char (point-min))
1006 (goto-char address)
1007 (forward-line 1))
1008 (insert (current-kill 0))))
1009
1010(defun ex-delete ()
1011 "Ex delete command."
1012 (vip-default-ex-addresses)
1013 (vip-get-ex-buffer)
1014 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses))))
1015 (if (> beg end) (error vip-FirstAddrExceedsSecond))
1016 (save-excursion
1017 (vip-enlarge-region beg end)
1018 (exchange-point-and-mark)
1019 (if ex-count
1020 (progn
1021 (set-mark (point))
1022 (forward-line (1- ex-count)))
1023 (set-mark end))
1024 (vip-enlarge-region (point) (mark t))
1025 (if ex-flag
1026 ;; show text to be deleted and ask for confirmation
1027 (progn
1028 (with-output-to-temp-buffer " *delete text*"
1029 (princ (buffer-substring (point) (mark t))))
1030 (condition-case nil
1031 (read-string "[Hit return to continue] ")
1032 (quit
1033 (save-excursion (kill-buffer " *delete text*"))
1034 (error "")))
1035 (save-excursion (kill-buffer " *delete text*")))
1036 (if ex-buffer
1037 (cond ((vip-valid-register ex-buffer '(Letter))
1038 (vip-append-to-register
1039 (downcase ex-buffer) (point) (mark t)))
1040 ((vip-valid-register ex-buffer)
1041 (copy-to-register ex-buffer (point) (mark t) nil))
1042 (t (error vip-InvalidRegister ex-buffer))))
1043 (kill-region (point) (mark t))))))
1044
1045
1046
1047(defun ex-edit (&optional file)
1048 "Ex edit command.
1049In Viper, `e' and `e!' behave identically. In both cases, the user is
1050asked if current buffer should really be discarded.
1051This command can take multiple file names. It replaces the current buffer
1052with the first file in its argument list."
1053 (if (not file)
1054 (vip-get-ex-file))
1055 (cond ((and (string= ex-file "") buffer-file-name)
1056 (setq ex-file (abbreviate-file-name (buffer-file-name))))
1057 ((string= ex-file "")
1058 (error vip-NoFileSpecified)))
1059
1060 (let (msg do-edit)
1061 (if buffer-file-name
1062 (cond ((buffer-modified-p)
1063 (setq msg
1064 (format "Buffer %s is modified. Edit buffer? "
1065 (buffer-name))
1066 do-edit t))
1067 ((not (verify-visited-file-modtime (current-buffer)))
1068 (setq msg
1069 (format "File %s changed on disk. Reread from disk? "
1070 buffer-file-name)
1071 do-edit t))
1072 (t (setq do-edit nil))))
1073
1074 (if do-edit
1075 (if (yes-or-no-p msg)
1076 (progn
1077 (set-buffer-modified-p nil)
1078 (kill-buffer (current-buffer)))
1079 (message "Buffer %s was left intact" (buffer-name))))
1080 ) ; let
1081
1082 (if (null (setq file (get-file-buffer ex-file)))
1083 (progn
1084 (ex-find-file ex-file)
1085 (vip-change-state-to-vi)
1086 (goto-char (point-min)))
1087 (switch-to-buffer file))
1088 (if ex-offset
1089 (progn
1090 (save-window-excursion
1091 (set-buffer vip-ex-work-buf)
1092 (delete-region (point-min) (point-max))
1093 (insert ex-offset "\n")
1094 (goto-char (point-min)))
1095 (goto-char (vip-get-ex-address))
1096 (beginning-of-line)))
1097 (ex-fixup-history vip-last-ex-prompt ex-file))
1098
1099;; splits the string FILESPEC into substrings separated by newlines `\012'
1100;; each line assumed to be a file name. find-file's each file thus obtained.
1101(defun ex-find-file (filespec)
1102 (let (s f filebuf)
1103 (if (string-match "[^a-zA-Z0-9_.-/]" filespec)
1104 (progn
1105 (save-excursion
1106 (set-buffer (get-buffer-create " ex-tmp"))
1107 (call-process ex-find-file-shell nil t nil
1108 ex-find-file-shell-options
1109 "-c"
1110 (format "echo %s | tr ' ' '\\012'" filespec))
1111 (goto-char (point-min))
1112 (while (not (eobp))
1113 (setq s (point))
1114 (end-of-line)
1115 (setq f (buffer-substring s (point)))
1116 (setq filebuf (find-file-noselect f))
1117 (forward-to-indentation 1))
1118 (kill-buffer (current-buffer))))
1119 (setq filebuf (find-file-noselect (setq f filespec))))
1120 (switch-to-buffer filebuf)
1121 ))
1122
1123(defun ex-global (variant)
1124 "Ex global command."
1125 (let ((gcommand ex-token))
1126 (if (or ex-g-flag ex-g-variant)
1127 (error "`%s' within `global' is not allowed" gcommand)
1128 (if variant
1129 (setq ex-g-flag nil
1130 ex-g-variant t)
1131 (setq ex-g-flag t
1132 ex-g-variant nil)))
1133 (vip-get-ex-pat)
1134 (if (null ex-token)
1135 (error "`%s': Missing regular expression" gcommand)))
1136
1137 (if (string= ex-token "")
1138 (if (null vip-s-string)
1139 (error vip-NoPrevSearch)
1140 (setq ex-g-pat vip-s-string))
1141 (setq ex-g-pat ex-token
1142 vip-s-string ex-token))
1143 (if (null ex-addresses)
1144 (setq ex-addresses (list (point-max) (point-min)))
1145 (vip-default-ex-addresses))
1146 (let ((marks nil) (mark-count 0)
1147 com-str (end (car ex-addresses)) (beg (car (cdr ex-addresses))))
1148 (if (> beg end) (error vip-FirstAddrExceedsSecond))
1149 (save-excursion
1150 (vip-enlarge-region beg end)
1151 (exchange-point-and-mark)
1152 (let ((cont t) (limit (point-marker)))
1153 (exchange-point-and-mark)
1154 ;; skip the last line if empty
1155 (beginning-of-line)
1156 (if (eobp) (vip-backward-char-carefully))
1157 (while (and cont (not (bobp)) (>= (point) limit))
1158 (beginning-of-line)
1159 (set-mark (point))
1160 (end-of-line)
1161 (let ((found (re-search-backward ex-g-pat (mark t) t)))
1162 (if (or (and ex-g-flag found)
1163 (and ex-g-variant (not found)))
1164 (progn
1165 (end-of-line)
1166 (setq mark-count (1+ mark-count))
1167 (setq marks (cons (point-marker) marks)))))
1168 (beginning-of-line)
1169 (if (bobp) (setq cont nil)
1170 (forward-line -1)
1171 (end-of-line)))))
1172 (save-window-excursion
1173 (set-buffer vip-ex-work-buf)
1174 (setq com-str (buffer-substring (1+ (point)) (1- (point-max)))))
1175 (while marks
1176 (goto-char (car marks))
1177 (vip-ex com-str)
1178 (setq mark-count (1- mark-count))
1179 (setq marks (cdr marks)))))
1180
1181(defun ex-goto ()
1182 "Ex goto command."
1183 (if (null ex-addresses)
1184 (setq ex-addresses (cons (point) nil)))
1185 (push-mark (point) t)
1186 (goto-char (car ex-addresses))
1187 (beginning-of-line))
1188
1189(defun ex-line (com)
1190 "Ex line commands. COM is join, shift-right or shift-left."
1191 (vip-default-ex-addresses)
1192 (vip-get-ex-count)
1193 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses))) point)
1194 (if (> beg end) (error vip-FirstAddrExceedsSecond))
1195 (save-excursion
1196 (vip-enlarge-region beg end)
1197 (exchange-point-and-mark)
1198 (if ex-count
1199 (progn
1200 (set-mark (point))
1201 (forward-line ex-count)))
1202 (if ex-flag
1203 ;; show text to be joined and ask for confirmation
1204 (progn
1205 (with-output-to-temp-buffer " *text*"
1206 (princ (buffer-substring (point) (mark t))))
1207 (condition-case nil
1208 (progn
1209 (read-string "[Hit return to continue] ")
1210 (ex-line-subr com (point) (mark t)))
1211 (quit (ding)))
1212 (save-excursion (kill-buffer " *text*")))
1213 (ex-line-subr com (point) (mark t)))
1214 (setq point (point)))
1215 (goto-char (1- point))
1216 (beginning-of-line)))
1217
1218(defun ex-line-subr (com beg end)
1219 (cond ((string= com "join")
1220 (goto-char (min beg end))
1221 (while (and (not (eobp)) (< (point) (max beg end)))
1222 (end-of-line)
1223 (if (and (<= (point) (max beg end)) (not (eobp)))
1224 (progn
1225 (forward-line 1)
1226 (delete-region (point) (1- (point)))
1227 (if (not ex-variant) (fixup-whitespace))))))
1228 ((or (string= com "right") (string= com "left"))
1229 (indent-rigidly
1230 (min beg end) (max beg end)
1231 (if (string= com "right") vip-shift-width (- vip-shift-width)))
1232 (goto-char (max beg end))
1233 (end-of-line)
1234 (vip-forward-char-carefully))))
1235
1236
1237(defun ex-mark ()
1238 "Ex mark command."
1239 (let (char)
1240 (if (null ex-addresses)
1241 (setq ex-addresses
1242 (cons (point) nil)))
1243 (save-window-excursion
1244 (set-buffer vip-ex-work-buf)
1245 (skip-chars-forward " \t")
1246 (if (looking-at "[a-z]")
1247 (progn
1248 (setq char (following-char))
1249 (forward-char 1)
1250 (skip-chars-forward " \t")
1251 (if (not (looking-at "[\n|]"))
1252 (error "`%s': %s" ex-token vip-SpuriousText)))
1253 (error "`%s' requires a following letter" ex-token)))
1254 (save-excursion
1255 (goto-char (car ex-addresses))
1256 (point-to-register (1+ (- char ?a))))))
1257
1258
1259
1260;; Alternate file is the file next to the first one in the buffer ring
1261(defun ex-next (cycle-other-window &optional find-alt-file)
1262 (catch 'ex-edit
1263 (let (count l)
1264 (if (not find-alt-file)
1265 (progn
1266 (vip-get-ex-file)
1267 (if (or (char-or-string-p ex-offset)
1268 (not (string= "" ex-file)))
1269 ;(and (not (string= "" ex-file))
1270 ; (not (string-match "[0-9]+" ex-file))))
1271 (progn
1272 (ex-edit t)
1273 (throw 'ex-edit nil))
1274 (setq count (string-to-int ex-file))
1275 (if (= count 0) (setq count 1))
1276 (if (< count 0) (error "Usage: `next <count>' (count >= 0)"))))
1277 (setq count 1))
1278 (setq l (vip-undisplayed-files))
1279 (while (> count 0)
1280 (while (and (not (null l)) (null (car l)))
1281 (setq l (cdr l)))
1282 (setq count (1- count))
1283 (if (> count 0)
1284 (setq l (cdr l))))
1285 (if find-alt-file (car l)
1286 (progn
1287 (if (car l)
1288 (let* ((w (if cycle-other-window
1289 (get-lru-window) (selected-window)))
1290 (b (window-buffer w)))
1291 (set-window-buffer w (get-file-buffer (car l)))
1292 (bury-buffer b))
1293 (error "Not that many undisplayed files")))))))
1294
1295
1296(defun ex-next-related-buffer (direction &optional no-recursion)
1297
1298 (vip-ring-rotate1 vip-related-files-and-buffers-ring direction)
1299
1300 (let ((file-or-buffer-name
1301 (vip-current-ring-item vip-related-files-and-buffers-ring))
1302 (old-ring vip-related-files-and-buffers-ring)
1303 (old-win (selected-window))
1304 skip-rest buf wind)
1305
1306 (or (and (ring-p vip-related-files-and-buffers-ring)
1307 (> (ring-length vip-related-files-and-buffers-ring) 0))
1308 (error "This buffer has no related files or buffers"))
1309
1310 (or (stringp file-or-buffer-name)
1311 (error
1312 "File and buffer names must be strings, %S" file-or-buffer-name))
1313
1314 (setq buf (cond ((get-buffer file-or-buffer-name))
1315 ((file-exists-p file-or-buffer-name)
1316 (find-file-noselect file-or-buffer-name))
1317 ))
1318
1319 (if (not (vip-buffer-live-p buf))
1320 (error "Didn't find buffer %S or file %S"
1321 file-or-buffer-name
1322 (abbreviate-file-name (expand-file-name file-or-buffer-name))))
1323
1324 (if (equal buf (current-buffer))
1325 (or no-recursion
1326 ;; try again
1327 (setq skip-rest t)
1328 (ex-next-related-buffer direction 'norecursion)))
1329
1330 (if skip-rest
1331 ()
1332 ;; setup buffer
1333 (if (setq wind (vip-get-visible-buffer-window buf))
1334 ()
1335 (setq wind (get-lru-window (if vip-xemacs-p nil 'visible)))
1336 (set-window-buffer wind buf))
1337
1338 (if window-system
1339 (progn
1340 (vip-raise-frame (vip-window-frame wind))
1341 (if (equal (vip-window-frame wind) (vip-window-frame old-win))
1342 (save-window-excursion (select-window wind) (sit-for 1))
1343 (select-window wind)))
1344 (save-window-excursion (select-window wind) (sit-for 1)))
1345
1346 (save-excursion
1347 (set-buffer buf)
1348 (setq vip-related-files-and-buffers-ring old-ring))
1349
1350 (setq vip-local-search-start-marker (point-marker))
1351 )))
1352
1353
1354(defun ex-preserve ()
1355 "Force auto save."
1356 (message "Autosaving all buffers that need to be saved...")
1357 (do-auto-save t))
1358
1359(defun ex-put ()
1360 "Ex put."
1361 (let ((point (if (null ex-addresses) (point) (car ex-addresses))))
1362 (vip-get-ex-buffer)
1363 (setq vip-use-register ex-buffer)
1364 (goto-char point)
1365 (if (bobp) (vip-Put-back 1) (vip-put-back 1))))
1366
1367(defun ex-pwd ()
1368 "Ex print working directory."
1369 (message default-directory))
1370
1371(defun ex-quit ()
1372 "Ex quit command."
1373 (if (< vip-expert-level 3)
1374 (save-buffers-kill-emacs)
1375 (kill-buffer (current-buffer))))
1376
1377
1378(defun ex-read ()
1379 "Ex read command."
1380 (vip-get-ex-file)
1381 (let ((point (if (null ex-addresses) (point) (car ex-addresses))))
1382 (goto-char point)
1383 (vip-add-newline-at-eob-if-necessary)
1384 (if (not (or (bobp) (eobp))) (forward-line 1))
1385 (if (and (not ex-variant) (string= ex-file ""))
1386 (progn
1387 (if (null buffer-file-name)
1388 (error vip-NoFileSpecified))
1389 (setq ex-file buffer-file-name)))
1390 (if ex-cmdfile
1391 (shell-command ex-file t)
1392 (insert-file-contents ex-file)))
1393 (ex-fixup-history vip-last-ex-prompt ex-file))
1394
1395;; this function fixes ex-history for some commands like ex-read, ex-edit
1396(defun ex-fixup-history (&rest args)
1397 (setq vip-ex-history
1398 (cons (mapconcat 'identity args " ") (cdr vip-ex-history))))
1399
1400
1401(defun ex-recover ()
1402 "Ex recover from emacs \#file\#."
1403 (vip-get-ex-file)
1404 (if (or ex-append ex-offset)
1405 (error "`recover': %s" vip-SpuriousText))
1406 (if (string= ex-file "")
1407 (progn
1408 (if (null buffer-file-name)
1409 (error "This buffer isn't visiting any file"))
1410 (setq ex-file buffer-file-name))
1411 (setq ex-file (expand-file-name ex-file)))
1412 (if (and (not (string= ex-file (buffer-file-name)))
1413 (buffer-modified-p)
1414 (not ex-variant))
1415 (error "No write since last change \(:rec! overrides\)"))
1416 (recover-file ex-file))
1417
1418(defun ex-rewind ()
1419 "Tell that `rewind' is obsolete and that one should use `:next count'"
1420 (message
1421 "Use `:n <count>' instead. Counts are obtained from the `:args' command"))
1422
1423
1424;; read variable name for ex-set
1425(defun ex-set-read-variable ()
1426 (let ((minibuffer-local-completion-map
1427 (copy-keymap minibuffer-local-completion-map))
1428 (cursor-in-echo-area t)
1429 str batch)
1430 (define-key
1431 minibuffer-local-completion-map " " 'minibuffer-complete-and-exit)
1432 (define-key minibuffer-local-completion-map "=" 'exit-minibuffer)
1433 (if (vip-set-unread-command-events
1434 (ex-get-inline-cmd-args "[ \t]*[a-zA-Z]*[ \t]*" nil "\C-m"))
1435 (progn
1436 (setq batch t)
1437 (vip-set-unread-command-events ?\C-m)))
1438 (message ":set <Variable> [= <Value>]")
1439 (or batch (sit-for 2))
1440
1441 (while (string-match "^[ \\t\\n]*$"
1442 (setq str
1443 (completing-read ":set " ex-variable-alist)))
1444 (message ":set <Variable> ")
1445 ;; if there are unread events, don't wait
1446 (or (vip-set-unread-command-events "") (sit-for 2))
1447 ) ; while
1448 str))
1449
1450
1451(defun ex-set ()
1452 (let ((var (ex-set-read-variable))
1453 (val 0)
1454 (set-cmd "setq")
1455 (ask-if-save t)
1456 (auto-cmd-label "; don't touch or else...")
1457 (delete-turn-on-auto-fill-pattern
1458 "([ \t]*add-hook[ \t]+'vip-insert-state-hooks[ \t]+'turn-on-auto-fill.*)")
1459 actual-lisp-cmd lisp-cmd-del-pattern
1460 val2 orig-var)
1461 (setq orig-var var)
1462 (cond ((member var '("ai" "autoindent"))
1463 (setq var "vip-auto-indent"
1464 val "t"))
1465 ((member var '("noai" "noautoindent"))
1466 (setq var "vip-auto-indent"
1467 val "nil"))
1468 ((member var '("ic" "ignorecase"))
1469 (setq var "vip-case-fold-search"
1470 val "t"))
1471 ((member var '("noic" "noignorecase"))
1472 (setq var "vip-case-fold-search"
1473 val "nil"))
1474 ((member var '("ma" "magic"))
1475 (setq var "vip-re-search"
1476 val "t"))
1477 ((member var '("noma" "nomagic"))
1478 (setq var "vip-re-search"
1479 val "nil"))
1480 ((member var '("ro" "readonly"))
1481 (setq var "buffer-read-only"
1482 val "t"))
1483 ((member var '("noro" "noreadonly"))
1484 (setq var "buffer-read-only"
1485 val "nil"))
1486 ((member var '("sm" "showmatch"))
1487 (setq var "blink-matching-paren"
1488 val "t"))
1489 ((member var '("nosm" "noshowmatch"))
1490 (setq var "blink-matching-paren"
1491 val "nil"))
1492 ((member var '("ws" "wrapscan"))
1493 (setq var "vip-search-wrap-around-t"
1494 val "t"))
1495 ((member var '("nows" "nowrapscan"))
1496 (setq var "vip-search-wrap-around-t"
1497 val "nil")))
1498 (if (eq val 0) ;; value must be set by the user
1499 (let ((cursor-in-echo-area t))
1500 (message (format ":set %s = <Value>" var))
1501 ;; if there are unread events, don't wait
1502 (or (vip-set-unread-command-events "") (sit-for 2))
1503 (setq val (read-string (format ":set %s = " var)))
1504 (ex-fixup-history "set" orig-var val)
1505
1506 ;; check numerical values
1507 (if (member var
1508 '("sw" "shiftwidth" "ts" "tabstop" "wm" "wrapmargin"))
1509 (condition-case nil
1510 (or (numberp (setq val2 (car (read-from-string val))))
1511 (error "%s: Invalid value, numberp, %S" var val))
1512 (error
1513 (error "%s: Invalid value, numberp, %S" var val))))
1514
1515 (cond
1516 ((member var '("sw" "shiftwidth"))
1517 (setq var "vip-shift-width"))
1518 ((member var '("ts" "tabstop"))
1519 ;; make it take effect in curr buff and new bufs
1520 (kill-local-variable 'tab-width)
1521 (setq var "tab-width"
1522 set-cmd "setq-default"))
1523 ((member var '("tsl" "tab-stop-local"))
1524 (setq var "tab-width"
1525 set-cmd "setq"
1526 ask-if-save nil))
1527 ((member var '("wm" "wrapmargin"))
1528 ;; make it take effect in curr buff and new bufs
1529 (kill-local-variable 'fill-column)
1530 (setq var "fill-column"
1531 val (format "(- (window-width) %s)" val)
1532 set-cmd "setq-default"))
1533 ((member var '("sh" "shell"))
1534 (setq var "explicit-shell-file-name"
1535 val (format "\"%s\"" val)))))
1536 (ex-fixup-history "set" orig-var))
1537
1538 (setq actual-lisp-cmd (format "\n(%s %s %s) %s"
1539 set-cmd var val auto-cmd-label))
1540 (setq lisp-cmd-del-pattern
1541 (format "^\n?[ \t]*([ \t]*%s[ \t]+%s[ \t].*)[ \t]*%s"
1542 set-cmd var auto-cmd-label))
1543
1544 (if (and ask-if-save
1545 (y-or-n-p (format "Do you want to save this setting in %s "
1546 vip-custom-file-name)))
1547 (progn
1548 (vip-save-string-in-file
1549 actual-lisp-cmd vip-custom-file-name
1550 ;; del pattern
1551 lisp-cmd-del-pattern)
1552 (if (string= var "fill-column")
1553 (if (> val2 0)
1554 (vip-save-string-in-file
1555 (concat
1556 "(add-hook 'vip-insert-state-hooks 'turn-on-auto-fill) "
1557 auto-cmd-label)
1558 vip-custom-file-name
1559 delete-turn-on-auto-fill-pattern)
1560 (vip-save-string-in-file
1561 nil vip-custom-file-name delete-turn-on-auto-fill-pattern)
1562 (vip-save-string-in-file
1563 nil vip-custom-file-name
1564 ;; del pattern
1565 lisp-cmd-del-pattern)
1566 ))
1567 ))
1568
1569 (message (format "%s %s %s" set-cmd var (if (string-match "^[ \t]*$" val)
1570 (format "%S" val)
1571 val)))
1572 (eval (car (read-from-string actual-lisp-cmd)))
1573 (if (string= var "fill-column")
1574 (if (> val2 0)
1575 (auto-fill-mode 1)
1576 (auto-fill-mode -1)))
1577
1578 ))
1579
1580;; In inline args, skip regex-forw and (optionally) chars-back.
1581;; Optional 3d arg is a string that should replace ' ' to prevent its
1582;; special meaning
1583(defun ex-get-inline-cmd-args (regex-forw &optional chars-back replace-str)
1584 (save-excursion
1585 (set-buffer vip-ex-work-buf)
1586 (goto-char (point-min))
1587 (re-search-forward regex-forw nil t)
1588 (let ((beg (point))
1589 end)
1590 (goto-char (point-max))
1591 (if chars-back
1592 (skip-chars-backward chars-back)
1593 (skip-chars-backward " \t\n\C-m"))
1594 (setq end (point))
1595 ;; replace SPC with `=' to suppress the special meaning SPC has
1596 ;; in Ex commands
1597 (goto-char beg)
1598 (if replace-str
1599 (while (re-search-forward " +" nil t)
1600 (replace-match replace-str nil t)
1601 (vip-forward-char-carefully)))
1602 (goto-char end)
1603 (buffer-substring beg end))))
1604
1605
1606(defun ex-shell ()
1607 "Ex shell command."
1608 (shell))
1609
1610(defun ex-help ()
1611 "Viper help. Invokes Info."
1612 (condition-case nil
1613 (progn
1614 (pop-to-buffer (get-buffer-create "*info*"))
1615 (info vip-info-file-name)
1616 (message "Type `i' to search for a specific topic"))
1617 (error (beep 1)
1618 (with-output-to-temp-buffer " *vip-info*"
1619 (princ "The Info file for Viper does not seem to be installed.
1620
1621This file is part of the distribution of Viper. If you do not
1622have the full distribution, please obtain it from the `anonymous'
1623FTP account at `archive.cis.ohio-state.edu':
1624
1625 /pub/gnu/emacs/elisp-archive/modes/viper.shar
1626
1627The Info files for Viper should be installed as <name>, <name>-1, etc.,
1628where <name> is the value of `vip-info-file-name'.")))))
1629
1630(defun ex-source ()
1631 "Ex source command. Loads the file specified as argument or `~/.vip'."
1632 (vip-get-ex-file)
1633 (if (string= ex-file "")
1634 (load vip-custom-file-name)
1635 (load ex-file)))
1636
1637(defun ex-substitute (&optional repeat r-flag)
1638 "Ex substitute command.
1639If REPEAT use previous regexp which is ex-reg-exp or vip-s-string"
1640 (let ((opt-g nil)
1641 (opt-c nil)
1642 (matched-pos nil)
1643 (case-fold-search vip-case-fold-search)
1644 delim pat repl)
1645 (if repeat (setq ex-token nil) (setq delim (vip-get-ex-pat)))
1646 (if (null ex-token)
1647 (setq pat (if r-flag vip-s-string ex-reg-exp)
1648 repl ex-repl
1649 delim (string-to-char pat))
1650 (setq pat (if (string= ex-token "") vip-s-string ex-token))
1651 (setq vip-s-string pat
1652 ex-reg-exp pat)
1653 (setq delim (vip-get-ex-pat))
1654 (if (null ex-token)
1655 (setq ex-token ""
1656 ex-repl "")
1657 (setq repl ex-token
1658 ex-repl ex-token)))
1659 (while (vip-get-ex-opt-gc delim)
1660 (if (string= ex-token "g") (setq opt-g t) (setq opt-c t)))
1661 (vip-get-ex-count)
1662 (if ex-count
1663 (save-excursion
1664 (if ex-addresses (goto-char (car ex-addresses)))
1665 (set-mark (point))
1666 (forward-line (1- ex-count))
1667 (setq ex-addresses (cons (point) (cons (mark t) nil))))
1668 (if (null ex-addresses)
1669 (setq ex-addresses (cons (point) (cons (point) nil)))
1670 (if (null (cdr ex-addresses))
1671 (setq ex-addresses (cons (car ex-addresses) ex-addresses)))))
1672 ;(setq G opt-g)
1673 (let ((beg (car ex-addresses))
1674 (end (car (cdr ex-addresses)))
1675 eol-mark)
1676 (save-excursion
1677 (vip-enlarge-region beg end)
1678 (let ((limit (save-excursion
1679 (goto-char (max (point) (mark t)))
1680 (point-marker))))
1681 (goto-char (min (point) (mark t)))
1682 (while (< (point) limit)
1683 (end-of-line)
1684 (setq eol-mark (point-marker))
1685 (beginning-of-line)
1686 (if opt-g
1687 (progn
1688 (while (and (not (eolp))
1689 (re-search-forward pat eol-mark t))
1690 (if (or (not opt-c) (y-or-n-p "Replace? "))
1691 (progn
1692 (setq matched-pos (point))
1693 (if (not (stringp repl))
1694 (error "Can't perform Ex substitution: No previous replacement pattern"))
1695 (replace-match repl t t))))
1696 (end-of-line)
1697 (vip-forward-char-carefully))
1698 (if (null pat)
1699 (error
1700 "Can't repeat Ex substitution: No previous regular expression"))
1701 (if (and (re-search-forward pat eol-mark t)
1702 (or (not opt-c) (y-or-n-p "Replace? ")))
1703 (progn
1704 (setq matched-pos (point))
1705 (if (not (stringp repl))
1706 (error "Can't perform Ex substitution: No previous replacement pattern"))
1707 (replace-match repl t t)))
1708 (end-of-line)
1709 (vip-forward-char-carefully))))))
1710 (if matched-pos (goto-char matched-pos))
1711 (beginning-of-line)
1712 (if opt-c (message "done"))))
1713
1714(defun ex-tag ()
1715 "Ex tag command."
1716 (let (tag)
1717 (save-window-excursion
1718 (set-buffer vip-ex-work-buf)
1719 (skip-chars-forward " \t")
1720 (set-mark (point))
1721 (skip-chars-forward "^ |\t\n")
1722 (setq tag (buffer-substring (mark t) (point))))
1723 (if (not (string= tag "")) (setq ex-tag tag))
1724 (vip-change-state-to-emacs)
1725 (condition-case conds
1726 (progn
1727 (if (string= tag "")
1728 (find-tag ex-tag t)
1729 (find-tag-other-window ex-tag))
1730 (vip-change-state-to-vi))
1731 (error
1732 (vip-change-state-to-vi)
1733 (vip-message-conditions conds)))))
1734
1735(defun ex-write (q-flag)
1736 "Ex write command."
1737 (vip-default-ex-addresses t)
1738 (vip-get-ex-file)
1739 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses)))
1740 temp-buf writing-same-file region
1741 file-exists writing-whole-file)
1742 (if (> beg end) (error vip-FirstAddrExceedsSecond))
1743 (if ex-cmdfile
1744 (progn
1745 (vip-enlarge-region beg end)
1746 (shell-command-on-region (point) (mark t) ex-file))
1747 (if (and (string= ex-file "") (not (buffer-file-name)))
1748 (setq ex-file
1749 (read-file-name
1750 (format "Buffer %s isn't visiting any file. File to save in: "
1751 (buffer-name)))))
1752
1753 (setq writing-whole-file (and (= (point-min) beg) (= (point-max) end))
1754 ex-file (if (string= ex-file "")
1755 (buffer-file-name)
1756 (expand-file-name ex-file))
1757 file-exists (file-exists-p ex-file)
1758 writing-same-file (string= ex-file (buffer-file-name)))
1759 (if (and writing-whole-file writing-same-file)
1760 (if (not (buffer-modified-p))
1761 (message "(No changes need to be saved)")
1762 (save-buffer)
1763 (ex-write-info file-exists ex-file beg end))
1764 ;; writing some other file or portion of the currents
1765 ;; file---create temp buffer for it
1766 ;; disable undo in that buffer, for efficiency
1767 (buffer-disable-undo (setq temp-buf (create-file-buffer ex-file)))
1768 (unwind-protect
1769 (save-excursion
1770 (if (and file-exists
1771 (not writing-same-file)
1772 (not (yes-or-no-p
1773 (format "File %s exists. Overwrite? " ex-file))))
1774 (error "Quit")
1775 (vip-enlarge-region beg end)
1776 (setq region (buffer-substring (point) (mark t)))
1777 (set-buffer temp-buf)
1778 (set-visited-file-name ex-file)
1779 (erase-buffer)
1780 (if (and file-exists ex-append)
1781 (insert-file-contents ex-file))
1782 (goto-char (point-max))
1783 (insert region)
1784 (save-buffer)
1785 (ex-write-info file-exists ex-file (point-min) (point-max))
1786 )
1787 (set-buffer temp-buf)
1788 (set-buffer-modified-p nil)
1789 (kill-buffer temp-buf)
1790 )
1791 ))
1792 ;; this prevents the loss of data if writing part of the buffer
1793 (if (and (buffer-file-name) writing-same-file)
1794 (set-visited-file-modtime))
1795 (or writing-whole-file
1796 (not writing-same-file)
1797 (set-buffer-modified-p t))
1798 (if q-flag
1799 (if (< vip-expert-level 2)
1800 (save-buffers-kill-emacs)
1801 (kill-buffer (current-buffer))))
1802 )))
1803
1804
1805(defun ex-write-info (exists file-name beg end)
1806 (message "`%s'%s %d lines, %d characters"
1807 (abbreviate-file-name file-name)
1808 (if exists "" " [New file]")
1809 (count-lines beg (min (1+ end) (point-max)))
1810 (- end beg)))
1811
1812(defun ex-yank ()
1813 "Ex yank command."
1814 (vip-default-ex-addresses)
1815 (vip-get-ex-buffer)
1816 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses))))
1817 (if (> beg end) (error vip-FirstAddrExceedsSecond))
1818 (save-excursion
1819 (vip-enlarge-region beg end)
1820 (exchange-point-and-mark)
1821 (if (or ex-g-flag ex-g-variant)
1822 (error "Can't execute `yank' within `global'"))
1823 (if ex-count
1824 (progn
1825 (set-mark (point))
1826 (forward-line (1- ex-count)))
1827 (set-mark end))
1828 (vip-enlarge-region (point) (mark t))
1829 (if ex-flag (error "`yank': %s" vip-SpuriousText))
1830 (if ex-buffer
1831 (cond ((vip-valid-register ex-buffer '(Letter))
1832 (vip-append-to-register
1833 (downcase ex-buffer) (point) (mark t)))
1834 ((vip-valid-register ex-buffer)
1835 (copy-to-register ex-buffer (point) (mark t) nil))
1836 (t (error vip-InvalidRegister ex-buffer))))
1837 (copy-region-as-kill (point) (mark t)))))
1838
1839(defun ex-command ()
1840 "Execute shell command."
1841 (let (command)
1842 (save-window-excursion
1843 (set-buffer vip-ex-work-buf)
1844 (skip-chars-forward " \t")
1845 (setq command (buffer-substring (point) (point-max)))
1846 (end-of-line))
1847 (setq command (ex-expand-filsyms command (current-buffer)))
1848 (if (and (> (length command) 0) (string= "!" (substring command 0 1)))
1849 (if vip-ex-last-shell-com
1850 (setq command (concat vip-ex-last-shell-com (substring command 1)))
1851 (error "No previous shell command")))
1852 (setq vip-ex-last-shell-com command)
1853 (if (null ex-addresses)
1854 (shell-command command)
1855 (let ((end (car ex-addresses)) (beg (car (cdr ex-addresses))))
1856 (if (null beg) (setq beg end))
1857 (save-excursion
1858 (goto-char beg)
1859 (set-mark end)
1860 (vip-enlarge-region (point) (mark t))
1861 (shell-command-on-region (point) (mark t) command t))
1862 (goto-char beg)))))
1863
1864(defun ex-line-no ()
1865 "Print line number."
1866 (message "%d"
1867 (1+ (count-lines
1868 (point-min)
1869 (if (null ex-addresses) (point-max) (car ex-addresses))))))
1870
1871(defun vip-info-on-file ()
1872 "Give information on the file visited by the current buffer."
1873 (interactive)
1874 (message "%s: pos=%d(%d) line=%d(%d) col=%d %s"
1875 (if (buffer-file-name)
1876 (abbreviate-file-name (buffer-file-name))
1877 "[No visited file]")
1878 (point) (1- (point-max))
1879 (count-lines (point-min) (vip-line-pos 'end))
1880 (count-lines (point-min) (point-max))
1881 (1+ (current-column))
1882 (if (buffer-modified-p) "[Modified]" "[Unchanged]")
1883 ))
1884
1885
1886(provide 'viper-ex)
1887
1888;;; viper-ex.el ends here