root/trunk/lisp/emulation/viper-ex.el

Revision 4220, 73.5 kB (checked in by miyoshi, 9 months ago)

Sync up with Emacs22.2.

  • Property svn:eol-style set to LF
Line 
1 ;;; viper-ex.el --- functions implementing the Ex commands for Viper
2
3 ;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003,
4 ;;   2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
6 ;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 3, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 ;; Boston, MA 02110-1301, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 (provide 'viper-ex)
30
31 ;; Compiler pacifier
32 (defvar read-file-name-map)
33 (defvar viper-use-register)
34 (defvar viper-s-string)
35 (defvar viper-shift-width)
36 (defvar viper-ex-history)
37 (defvar viper-related-files-and-buffers-ring)
38 (defvar viper-local-search-start-marker)
39 (defvar viper-expert-level)
40 (defvar viper-custom-file-name)
41 (defvar viper-case-fold-search)
42 (defvar explicit-shell-file-name)
43 (defvar compile-command)
44
45 ;; loading happens only in non-interactive compilation
46 ;; in order to spare non-viperized emacs from being viperized
47 (if noninteractive
48     (eval-when-compile
49       (let ((load-path (cons (expand-file-name ".") load-path)))
50         (or (featurep 'viper-util)
51             (load "viper-util.el" nil nil 'nosuffix))
52         (or (featurep 'viper-keym)
53             (load "viper-keym.el" nil nil 'nosuffix))
54         (or (featurep 'viper-cmd)
55             (load "viper-cmd.el" nil nil 'nosuffix))
56         )))
57 ;; end pacifier
58
59 (require 'viper-util)
60
61 (defgroup viper-ex nil
62   "Viper support for Ex commands."
63   :prefix "ex-"
64   :group 'viper)
65
66
67
68 ;;; Variables
69
70 (defconst viper-ex-work-buf-name " *ex-working-space*")
71 (defvar viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
72 (defconst viper-ex-tmp-buf-name " *ex-tmp*")
73 (defconst viper-ex-print-buf-name " *ex-print*")
74 (defvar viper-ex-print-buf (get-buffer-create viper-ex-print-buf-name))
75
76
77 ;;; ex-commands...
78
79 (defun ex-cmd-obsolete (name)
80   (error "`%s': Obsolete command, not supported by Viper" name))
81
82 (defun ex-cmd-not-yet (name)
83   (error "`%s': Command not implemented in Viper" name))
84
85 ;; alist entries: name (in any order), command, cont(??)
86 ;; If command is a string, then that is an alias to the real command
87 ;; to execute (for instance, ":m" -> ":move").
88 ;; command attributes:
89 ;;   is-mashed: the command's args may be jammed right up against the command
90 ;;   one-letter: this is a one-letter token.  Any text appearing after
91 ;;                   the name gets appended as an argument for the command
92 ;;               i.e. ":kabc" gets turned into (ex-mark "abc")
93 (defconst ex-token-alist '(
94         ("!"                    (ex-command))
95         ("&"                    (ex-substitute t))
96         ("="                    (ex-line-no))
97         (">"                    (ex-line "right"))
98         ("<"                    (ex-line "left"))
99         ("Buffer"               (if ex-cycle-other-window
100                                     (viper-switch-to-buffer)
101                                   (viper-switch-to-buffer-other-window)))
102         ("Next"                 (ex-next (not ex-cycle-other-window)))
103         ("PreviousRelatedFile"  (ex-next-related-buffer -1))
104         ("RelatedFile"          (ex-next-related-buffer 1))
105         ("W"                    "Write")
106         ("WWrite"               (save-some-buffers t))
107         ("Write"                (save-some-buffers))
108         ("a"                    "append")
109         ("args"                 (ex-args))
110         ("buffer"               (if ex-cycle-other-window
111                                     (viper-switch-to-buffer-other-window)
112                                   (viper-switch-to-buffer)))
113         ("c"                    "change")
114         ;; ch should be "change" but maintain old viper compatibility
115         ("ch"                   "chdir")
116         ("cd"                   (ex-cd))
117         ("chdir"                (ex-cd))
118         ("copy"                 (ex-copy nil))
119         ("customize"            (customize-group "viper"))
120         ("delete"               (ex-delete))
121         ("edit"                 (ex-edit))
122         ("file"                 (ex-set-visited-file-name))
123         ("g"                    "global")
124         ("global"               (ex-global nil) is-mashed)
125         ("goto"                 (ex-goto))
126         ("help"                 (ex-help))
127         ("join"                 (ex-line "join"))
128         ("k"                    (ex-mark) one-letter)
129         ("kmark"                (ex-mark))
130         ("m"                    "move")
131         ("make"                 (ex-compile))
132         ; old viper doesn't specify a default for "ma" so leave it undefined
133         ("map"                  (ex-map))
134         ("mark"                 (ex-mark))
135         ("move"                 (ex-copy t))
136         ("next"                 (ex-next ex-cycle-other-window))
137         ("p"                    "print")
138         ("preserve"             (ex-preserve))
139         ("print"                (ex-print))
140         ("put"                  (ex-put))
141         ("pwd"                  (ex-pwd))
142         ("quit"                 (ex-quit))
143         ("r"                    "read")
144         ("re"                   "read")
145         ("read"                 (ex-read))
146         ("recover"              (ex-recover))
147         ("rewind"               (ex-rewind))
148         ("s"                    "substitute")
149         ("su"                   "substitute")
150         ("sub"                  "substitute")
151         ("set"                  (ex-set))
152         ("shell"                (ex-shell))
153         ("source"               (ex-source))
154         ("stop"                 (suspend-emacs))
155         ("sr"                   (ex-substitute t t))
156         ("submitReport"         (viper-submit-report))
157         ("substitute"           (ex-substitute) is-mashed)
158         ("suspend"              (suspend-emacs))
159         ("t"                    "transfer")
160         ("tag"                  (ex-tag))
161         ("transfer"             (ex-copy nil))
162         ("u"                    "undo")
163         ("un"                   "undo")
164         ("undo"                 (viper-undo))
165         ("unmap"                (ex-unmap))
166         ("v"                    "vglobal")
167         ("version"              (viper-version))
168         ("vglobal"              (ex-global t) is-mashed)
169         ("visual"               (ex-edit))
170         ("w"                    "write")
171         ("wq"                   (ex-write t))
172         ("write"                (ex-write nil))
173         ("xit"                  (ex-write t))
174         ("yank"                 (ex-yank))
175         ("~"                    (ex-substitute t t))
176
177         ("append"               (ex-cmd-obsolete "append"))
178         ("change"               (ex-cmd-obsolete "change"))
179         ("insert"               (ex-cmd-obsolete "insert"))
180         ("open"                 (ex-cmd-obsolete "open"))
181
182         ("list"                 (ex-cmd-not-yet "list"))
183         ("z"                    (ex-cmd-not-yet "z"))
184         ("#"                    (ex-cmd-not-yet "#"))
185
186         ("abbreviate"           (error "`%s': Vi abbreviations are obsolete.  Use the more powerful Emacs abbrevs" ex-token))
187         ("unabbreviate"         (error "`%s': Vi abbreviations are obsolete.  Use the more powerful Emacs abbrevs" ex-token))
188         ))
189
190 ;; No code should touch anything in the alist entry!  (other than the name,
191 ;; "car entry", of course)   This way, changing this data structure
192 ;; requires changing only the following ex-cmd functions...
193
194 ;; Returns cmd if the command may be jammed right up against its
195 ;; arguments, nil if there must be a space.
196 ;; examples of mashable commands: g// g!// v// s// sno// sm//
197 (defun ex-cmd-is-mashed-with-args (cmd)
198   (if (eq 'is-mashed (car (nthcdr 2 cmd))) cmd))
199
200 ;; Returns true if this is a one-letter command that may be followed
201 ;; by anything, no whitespace needed.  This is a special-case for ":k".
202 (defun ex-cmd-is-one-letter (cmd)
203   (if (eq 'one-letter (car (nthcdr 2 cmd))) cmd))
204
205 ;; Executes the function associated with the command
206 (defun ex-cmd-execute (cmd)
207   (eval (cadr cmd)))
208
209 ;; If this is a one-letter magic command, splice in args.
210 (defun ex-splice-args-in-1-letr-cmd (key list)
211   (let ((oneletter (ex-cmd-is-one-letter (assoc (substring key 0 1) list))))
212     (if oneletter
213         (list key
214               (append (cadr oneletter)
215                       (if (< 1 (length key)) (list (substring key 1))))
216               (car (cdr (cdr oneletter))) ))
217         ))
218
219
220 ;; Returns the alist entry for the appropriate key.
221 ;; Tries to complete the key before using it in the alist.
222 ;; If there is no appropriate key (no match or duplicate matches) return nil
223 (defun ex-cmd-assoc (key list)
224   (let ((entry (try-completion key list))
225         result)
226     (setq result (cond
227                   ((eq entry t)     (assoc key list))
228                   ((stringp entry)  (or (ex-splice-args-in-1-letr-cmd key list)
229                                         (assoc entry list)))
230                   ((eq entry nil)   (ex-splice-args-in-1-letr-cmd key list))
231                   (t nil)
232                   ))
233     ;; If we end up with an alias, look up the alias...
234     (if (stringp (cadr result))
235         (setq result (ex-cmd-assoc (cadr result) list)))
236     ;; and return the corresponding alist entry
237     result
238     ))
239
240
241 ;; A-list of Ex variables that can be set using the :set command.
242 (defconst ex-variable-alist
243   '(("wrapscan") ("ws") ("wrapmargin") ("wm")
244     ("tabstop-global") ("ts-g") ("tabstop") ("ts")
245     ("showmatch") ("sm") ("shiftwidth") ("sw") ("shell") ("sh")
246     ("readonly") ("ro")
247     ("nowrapscan") ("nows") ("noshowmatch") ("nosm")
248     ("noreadonly") ("noro") ("nomagic") ("noma")
249     ("noignorecase") ("noic")
250     ("noautoindent-global") ("noai-g") ("noautoindent") ("noai")
251     ("magic") ("ma") ("ignorecase") ("ic")
252     ("autoindent-global") ("ai-g") ("autoindent") ("ai")
253     ("all")
254     ))
255
256
257
258 ;; Token recognized during parsing of Ex commands (e.g., "read", "comma")
259 (defvar ex-token nil)
260
261 ;; Type of token.
262 ;; If non-nil, gives type of address; if nil, it is a command.
263 (defvar ex-token-type nil)
264
265 ;; List of addresses passed to Ex command
266 (defvar ex-addresses nil)
267
268 ;; This flag is supposed to be set only by `#', `print', and `list',
269 ;; none of which is implemented.  So, it and the pices of the code it
270 ;; controls are dead weight.  We keep it just in case this might be
271 ;; needed in the future.
272 (defvar ex-flag nil)
273
274 ;; "buffer" where Ex commands keep deleted data.
275 ;; In Emacs terms, this is a register.
276 (defvar ex-buffer nil)
277
278 ;; Value of ex count.
279 (defvar ex-count nil)
280
281 ;; Flag indicating that :global Ex command is being executed.
282 (defvar ex-g-flag nil)
283 ;; Flag indicating that :vglobal Ex command is being executed.
284 (defvar ex-g-variant nil)
285 ;; Marks to operate on during a :global Ex command.
286 (defvar ex-g-marks nil)
287
288 ;; Save reg-exp used in substitute.
289 (defvar ex-reg-exp nil)
290
291
292 ;; Replace pattern for substitute.
293 (defvar ex-repl nil)
294
295 ;; Pattern for global command.
296 (defvar ex-g-pat nil)
297
298 (defcustom ex-unix-type-shell
299   (let ((case-fold-search t))
300     (and (stringp shell-file-name)
301          (string-match
302           (concat
303            "\\("
304            "csh$\\|csh.exe$"
305            "\\|"
306            "ksh$\\|ksh.exe$"
307            "\\|"
308            "^sh$\\|sh.exe$"
309            "\\|"
310            "[^a-z]sh$\\|[^a-z]sh.exe$"
311            "\\|"
312            "bash$\\|bash.exe$"
313            "\\)")
314           shell-file-name)))
315   "Is the user using a unix-type shell under a non-OS?"
316   :type 'boolean
317   :group 'viper-ex)
318
319 (defcustom ex-unix-type-shell-options
320   (let ((case-fold-search t))
321     (if ex-unix-type-shell
322         (cond ((string-match "\\(csh$\\|csh.exe$\\)" shell-file-name)
323                "-f") ; csh: do it fast
324               ((string-match "\\(bash$\\|bash.exe$\\)" shell-file-name)
325                "-noprofile") ; bash: ignore .profile
326               )))
327   "Options to pass to the Unix-style shell.
328 Don't put `-c' here, as it is added automatically."
329   :type '(choice (const nil) string)
330   :group 'viper-ex)
331
332 (defcustom ex-compile-command "make"
333   "The command to run when the user types :make."
334   :type 'string
335   :group 'viper-ex)
336
337 (defcustom viper-glob-function
338   (cond (ex-unix-type-shell 'viper-glob-unix-files)
339         ((eq system-type 'emx) 'viper-glob-mswindows-files) ; OS/2
340         (viper-ms-style-os-p 'viper-glob-mswindows-files) ; Microsoft OS
341         (viper-vms-os-p 'viper-glob-unix-files) ; VMS
342         (t  'viper-glob-unix-files) ; presumably UNIX
343         )
344   "Expand the file spec containing wildcard symbols.
345 The default tries to set this variable to work with Unix, Windows,
346 OS/2, and VMS.
347
348 However, if it doesn't work right for some types of Unix shells or some OS,
349 the user should supply the appropriate function and set this variable to the
350 corresponding function symbol."
351   :type 'symbol
352   :group 'viper-ex)
353
354
355 ;; Remembers the previous Ex tag.
356 (defvar ex-tag nil)
357
358 ;; file used by Ex commands like :r, :w, :n
359 (defvar ex-file nil)
360
361 ;; If t, tells Ex that this is a variant-command, i.e., w>>, r!, etc.
362 (defvar ex-variant nil)
363
364 ;; Specified the offset of an Ex command, such as :read.
365 (defvar ex-offset nil)
366
367 ;; Tells Ex that this is a w>> command.
368 (defvar ex-append nil)
369
370 ;; File containing the shell command to be executed at Ex prompt,
371 ;; e.g., :r !date
372 (defvar ex-cmdfile nil)
373 (defvar ex-cmdfile-args "")
374
375 ;; flag used in viper-ex-read-file-name to indicate that we may be reading
376 ;; multiple file names.  Used for :edit and :next
377 (defvar viper-keep-reading-filename nil)
378
379 (defcustom ex-cycle-other-window t
380   "*If t, :n and :b cycles through files and buffers in other window.
381 Then :N and :B cycles in the current window.  If nil, this behavior is
382 reversed."
383   :type 'boolean
384   :group 'viper-ex)
385
386 (defcustom ex-cycle-through-non-files nil
387   "*Cycle through *scratch* and other buffers that don't visit any file."
388   :type 'boolean
389   :group 'viper-ex)
390
391 ;; Last shell command executed with :! command.
392 (defvar viper-ex-last-shell-com nil)
393
394 ;; Indicates if Minibuffer was exited temporarily in Ex-command.
395 (defvar viper-incomplete-ex-cmd nil)
396
397 ;; Remembers the last ex-command prompt.
398 (defvar viper-last-ex-prompt "")
399
400
401 ;; Get a complete ex command
402 (defun viper-get-ex-com-subr ()
403   (let (cmd case-fold-search)
404     (set-mark (point))
405     (re-search-forward "[a-zA-Z][a-zA-Z]*")
406     (setq ex-token-type 'command)
407     (setq ex-token (buffer-substring (point) (mark t)))
408     (setq cmd (ex-cmd-assoc ex-token ex-token-alist))
409     (if cmd
410         (setq ex-token (car cmd))
411       (setq ex-token-type 'non-command))
412     ))
413
414 ;; Get an ex-token which is either an address or a command.
415 ;; A token has a type, \(command, address, end-mark\), and a value
416 (defun viper-get-ex-token ()
417   (save-window-excursion
418     (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
419     (set-buffer viper-ex-work-buf)
420     (skip-chars-forward " \t|")
421     (let ((case-fold-search t))
422       (cond ((looking-at "#")
423              (setq ex-token-type 'command)
424              (setq ex-token (char-to-string (following-char)))
425              (forward-char 1))
426             ((looking-at "[a-z]") (viper-get-ex-com-subr))
427             ((looking-at "\\.")
428              (forward-char 1)
429              (setq ex-token-type 'dot))
430             ((looking-at "[0-9]")
431              (set-mark (point))
432              (re-search-forward "[0-9]*")
433              (setq ex-token-type
434                    (cond ((eq ex-token-type 'plus) 'add-number)
435                          ((eq ex-token-type 'minus) 'sub-number)
436                          (t 'abs-number)))
437              (setq ex-token
438                    (string-to-number (buffer-substring (point) (mark t)))))
439             ((looking-at "\\$")
440              (forward-char 1)
441              (setq ex-token-type 'end))
442             ((looking-at "%")
443              (forward-char 1)
444              (setq ex-token-type 'whole))
445             ((looking-at "+")
446              (cond ((or (looking-at "+[-+]") (looking-at "+[\n|]"))
447                     (forward-char 1)
448                     (insert "1")
449                     (backward-char 1)
450                   (setq ex-token-type 'plus))
451                    ((looking-at "+[0-9]")
452                     (forward-char 1)
453                     (setq ex-token-type 'plus))
454                    (t
455                     (error viper-BadAddress))))
456             ((looking-at "-")
457              (cond ((or (looking-at "-[-+]") (looking-at "-[\n|]"))
458                     (forward-char 1)
459                     (insert "1")
460                     (backward-char 1)
461                     (setq ex-token-type 'minus))
462                    ((looking-at "-[0-9]")
463                     (forward-char 1)
464                     (setq ex-token-type 'minus))
465                    (t
466                     (error viper-BadAddress))))
467             ((looking-at "/")
468              (forward-char 1)
469              (set-mark (point))
470              (let ((cont t))
471                (while (and (not (eolp)) cont)
472                  ;;(re-search-forward "[^/]*/")
473                  (re-search-forward "[^/]*\\(/\\|\n\\)")
474                  (if (not (viper-looking-back "[^\\\\]\\(\\\\\\\\\\)*\\\\/"))
475                      (setq cont nil))))
476              (backward-char 1)
477              (setq ex-token (buffer-substring (point) (mark t)))
478              (if (looking-at "/") (forward-char 1))
479              (setq ex-token-type 'search-forward))
480             ((looking-at "\\?")
481              (forward-char 1)
482              (set-mark (point))
483              (let ((cont t))
484                (while (and (not (eolp)) cont)
485                  ;;(re-search-forward "[^\\?]*\\?")
486                  (re-search-forward "[^\\?]*\\(\\?\\|\n\\)")
487                  (if (not (viper-looking-back "[^\\\\]\\(\\\\\\\\\\)*\\\\\\?"))
488                      (setq cont nil))
489                  (backward-char 1)
490                  (if (not (looking-at "\n")) (forward-char 1))))
491              (setq ex-token-type 'search-backward)
492              (setq ex-token (buffer-substring (1- (point)) (mark t))))
493             ((looking-at ",")
494              (forward-char 1)
495              (setq ex-token-type 'comma))
496             ((looking-at ";")
497              (forward-char 1)
498              (setq ex-token-type 'semi-colon))
499             ((looking-at "[!=><&~]")
500              (setq ex-token-type 'command)
501              (setq ex-token (char-to-string (following-char)))
502              (forward-char 1))
503             ((looking-at "'")
504              (setq ex-token-type 'goto-mark)
505              (forward-char 1)
506              (cond ((looking-at "'") (setq ex-token nil))
507                    ((looking-at "[a-z]") (setq ex-token (following-char)))
508                    (t (error "Marks are ' and a-z")))
509              (forward-char 1))
510             ((looking-at "\n")
511              (setq ex-token-type 'end-mark)
512              (setq ex-token "goto"))
513             (t
514              (error viper-BadExCommand))))))
515
516 ;; Reads Ex command.  Tries to determine if it has to exit because command
517 ;; is complete or invalid.  If not, keeps reading command.
518 (defun ex-cmd-read-exit ()
519   (interactive)
520   (setq viper-incomplete-ex-cmd t)
521   (let ((quit-regex1 (concat
522                       "\\(" "set[ \t]*"
523                       "\\|" "edit[ \t]*"
524                       "\\|" "[nN]ext[ \t]*"
525                       "\\|" "unm[ \t]*"
526                       "\\|" "^[ \t]*rep"
527                       "\\)"))
528         (quit-regex2 (concat
529                       "[a-zA-Z][ \t]*"
530                       "\\(" "!" "\\|" ">>"
531                       "\\|" "\\+[0-9]+"
532                       "\\)"
533                       "*[ \t]*$"))
534         (stay-regex (concat
535                      "\\(" "^[ \t]*$"
536                      "\\|" "[?/].*"
537                      "\\|" "[ktgjmsz][ \t]*$"
538                      "\\|" "^[ \t]*ab.*"
539                      "\\|" "tr[ansfer \t]*"
540                      "\\|" "sr[ \t]*"
541                      "\\|" "mo.*"
542                      "\\|" "^[ \t]*k?ma[^p]*"
543                      "\\|" "^[ \t]*fi.*"
544                      "\\|" "v?gl.*"
545                      "\\|" "[vg][ \t]*$"
546                      "\\|" "jo.*"
547                      "\\|" "^[ \t]*ta.*"
548                      "\\|" "^[ \t]*una.*"
549                      ;; don't jump up in :s command
550                      "\\|" "^[ \t]*\\([`'][a-z]\\|[.,%]\\)*[ \t]*su.*"
551                      "\\|" "^[ \t]*\\([`'][a-z]\\|[.,%]\\)*[ \t]*s[^a-z].*"
552                      "\\|" "['`][a-z][ \t]*"
553                      ;; r! assumes that the next one is a shell command
554                      "\\|" "\\(r\\|re\\|rea\\|read\\)[ \t]*!"
555                      ;; w ! assumes that the next one is a shell command
556                      "\\|" "\\(w\\|wr\\|wri\\|writ.?\\)[ \t]+!"
557                      "\\|" "![ \t]*[a-zA-Z].*"
558                      "\\)"
559                      "!*")))
560
561     (save-window-excursion ;; put cursor at the end of the Ex working buffer
562       (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
563       (set-buffer viper-ex-work-buf)
564       (goto-char (point-max)))
565     (cond ((viper-looking-back quit-regex1) (exit-minibuffer))
566           ((viper-looking-back stay-regex)  (insert " "))
567           ((viper-looking-back quit-regex2) (exit-minibuffer))
568           (t (insert " ")))))
569
570 ;; complete Ex command
571 (defun ex-cmd-complete ()
572   (interactive)
573   (let (save-pos dist compl-list string-to-complete completion-result)
574
575     (save-excursion
576       (setq dist (skip-chars-backward "[a-zA-Z!=>&~]")
577             save-pos (point)))
578
579     (if (or (= dist 0)
580             (viper-looking-back "\\([ \t]*['`][ \t]*[a-z]*\\)")
581             (viper-looking-back
582              "^[ \t]*[a-zA-Z!=>&~][ \t]*[/?]*[ \t]+[a-zA-Z!=>&~]+"))
583         ;; Preceding characters are not the ones allowed in an Ex command
584         ;; or we have typed past command name.
585         ;; Note: we didn't do parsing, so there can be surprises.
586         (if (or (viper-looking-back "[a-zA-Z!=>&~][ \t]*[/?]*[ \t]*")
587                 (viper-looking-back "\\([ \t]*['`][ \t]*[a-z]*\\)")
588                 (looking-at "[^ \t\n\C-m]"))
589             nil
590           (with-output-to-temp-buffer "*Completions*"
591             (display-completion-list
592              (viper-alist-to-list ex-token-alist))))
593       ;; Preceding chars may be part of a command name
594       (setq string-to-complete (buffer-substring save-pos (point)))
595       (setq completion-result
596             (try-completion string-to-complete ex-token-alist))
597
598       (cond ((eq completion-result t)  ; exact match--do nothing
599              (viper-tmp-insert-at-eob " (Sole completion)"))
600             ((eq completion-result nil)
601              (viper-tmp-insert-at-eob " (No match)"))
602             (t  ;; partial completion
603              (goto-char save-pos)
604              (delete-region (point) (point-max))
605              (insert completion-result)
606              (let (case-fold-search)
607                (setq compl-list
608                      (viper-filter-alist (concat "^" completion-result)
609                                        ex-token-alist)))
610              (if (> (length compl-list) 1)
611                  (with-output-to-temp-buffer "*Completions*"
612                    (display-completion-list
613                     (viper-alist-to-list (reverse compl-list)))))))
614       )))
615
616
617 ;; Read Ex commands
618 ;; ARG is a prefix argument. If given, the ex command runs on the region
619 ;;(without the user having to specify the address :a,b
620 ;; STRING is the command to execute. If nil, then Viper asks you to enter the
621 ;; command.
622 (defun viper-ex (arg &optional string)
623   (interactive "P")
624   (or string
625       (setq ex-g-flag nil
626             ex-g-variant nil))
627   (let* ((map (copy-keymap minibuffer-local-map))
628          (address nil)
629          (cont t)
630          (dot (point))
631          reg-beg-line reg-end-line
632          reg-beg reg-end
633          initial-str
634          prev-token-type com-str)
635     (viper-add-keymap viper-ex-cmd-map map)
636
637     (if arg
638         (progn
639           (viper-enlarge-region (mark t) (point))
640           (if (> (point) (mark t))
641               (setq reg-beg (mark t)
642                     reg-end (point))
643             (setq reg-end (mark t)
644                   reg-beg (point)))
645           (save-excursion
646             (goto-char reg-beg)
647             (setq reg-beg-line (1+ (count-lines (point-min) (point)))
648                   reg-end-line
649                   (+ reg-beg-line (count-lines reg-beg reg-end) -1)))))
650     (if reg-beg-line
651         (setq initial-str (format "%d,%d" reg-beg-line reg-end-line)))
652
653     (setq com-str
654           (or string (viper-read-string-with-history
655                       ":"
656                       initial-str
657                       'viper-ex-history
658                       ;; no default when working on region
659                       (if initial-str
660                           nil
661                         (car viper-ex-history))
662                       map
663                       (if initial-str
664                           " [Type command to execute on current region]"))))
665     (save-window-excursion
666       ;; just a precaution
667       (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
668       (set-buffer viper-ex-work-buf)
669       (delete-region (point-min) (point-max))
670       (insert com-str "\n")
671       (goto-char (point-min)))
672     (setq ex-token-type nil
673           ex-addresses nil)
674     (while cont
675       (viper-get-ex-token)
676       (cond ((memq ex-token-type '(command end-mark))
677              (if address (setq ex-addresses (cons address ex-addresses)))
678              (viper-deactivate-mark)
679              (let ((cmd (ex-cmd-assoc ex-token ex-token-alist)))
680                (if (null cmd)
681                    (error "`%s': %s" ex-token viper-BadExCommand))
682                (ex-cmd-execute cmd)
683                (if (or (ex-cmd-is-mashed-with-args cmd)
684                        (ex-cmd-is-one-letter cmd))
685                    (setq cont nil)
686                  (save-excursion
687                    (save-window-excursion
688                      (setq viper-ex-work-buf
689                            (get-buffer-create viper-ex-work-buf-name))
690                      (set-buffer viper-ex-work-buf)
691                      (skip-chars-forward " \t")
692                      (cond ((looking-at "|")
693                             (forward-char 1))
694                            ((looking-at "\n")
695                             (setq cont nil))
696                            (t (error
697                                "`%s': %s" ex-token viper-SpuriousText)))
698                      )))
699                ))
700             ((eq ex-token-type 'non-command)
701              (error "`%s': %s" ex-token viper-BadExCommand))
702             ((eq ex-token-type 'whole)
703              (setq address nil)
704              (setq ex-addresses
705                    (if ex-addresses
706                        (cons (point-max) ex-addresses)
707                      (cons (point-max) (cons (point-min) ex-addresses)))))
708             ((eq ex-token-type 'comma)
709              (if (eq prev-token-type 'whole)
710                  (setq address (point-min)))
711              (setq ex-addresses
712                    (cons (if (null address) (point) address) ex-addresses)))
713             ((eq ex-token-type 'semi-colon)
714              (if (eq prev-token-type 'whole)
715                  (setq address (point-min)))
716              (if address (setq dot address))
717              (setq ex-addresses
718                    (cons (if (null address) (point) address) ex-addresses)))
719             (t (let ((ans (viper-get-ex-address-subr address dot)))
720                  (if ans (setq address ans)))))
721       (setq prev-token-type ex-token-type))))
722
723
724 ;; Get a regular expression and set `ex-variant', if found
725 ;; Viper doesn't parse the substitution or search patterns.
726 ;; In particular, it doesn't expand ~ into the last substitution.
727 (defun viper-get-ex-pat ()
728   (save-window-excursion
729     (setq viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
730     (set-buffer viper-ex-work-buf)
731     (skip-chars-forward " \t")
732     (if (looking-at "!")
733         ;; this is probably a variant command r!
734         (progn
735           (setq ex-g-variant (not ex-g-variant)
736                 ex-g-flag (not ex-g-flag))
737           (forward-char 1)
738           (skip-chars-forward " \t")))
739     (let ((c (following-char)))
740       (cond ((string-match "[0-9A-Za-z]" (format "%c" c))
741              (error
742               "Global regexp must be inside matching non-alphanumeric chars"))
743             ((= c ??) (error "`?' is not an allowed pattern delimiter here")))
744       (if (looking-at "[^\\\\\n]")
745           (progn