+(defconst viper-ex-work-buf-name " *ex-working-space*")
+(defvar viper-ex-work-buf (get-buffer-create viper-ex-work-buf-name))
+(defconst viper-ex-tmp-buf-name " *ex-tmp*")
+(defconst viper-ex-print-buf-name " *ex-print*")
+(defvar viper-ex-print-buf (get-buffer-create viper-ex-print-buf-name))
+
+
+;;; ex-commands...
+
+(defun ex-cmd-obsolete (name)
+ (error "`%s': Obsolete command, not supported by Viper" name))
+
+(defun ex-cmd-not-yet (name)
+ (error "`%s': Command not implemented in Viper" name))
+
+;; alist entries: name (in any order), command, cont(??)
+;; If command is a string, then that is an alias to the real command
+;; to execute (for instance, ":m" -> ":move").
+;; command attributes:
+;; is-mashed: the command's args may be jammed right up against the command
+;; one-letter: this is a one-letter token. Any text appearing after
+;; the name gets appended as an argument for the command
+;; i.e. ":kabc" gets turned into (ex-mark "abc")
+(defconst ex-token-alist '(
+ ("!" (ex-command))
+ ("&" (ex-substitute t))
+ ("=" (ex-line-no))
+ (">" (ex-line "right"))
+ ("<" (ex-line "left"))
+ ("Buffer" (if ex-cycle-other-window
+ (viper-switch-to-buffer)
+ (viper-switch-to-buffer-other-window)))
+ ("Next" (ex-next (not ex-cycle-other-window)))
+ ("PreviousRelatedFile" (ex-next-related-buffer -1))
+ ("RelatedFile" (ex-next-related-buffer 1))
+ ("W" "Write")
+ ("WWrite" (save-some-buffers t))
+ ("Write" (save-some-buffers))
+ ("a" "append")
+ ("args" (ex-args))
+ ("buffer" (if ex-cycle-other-window
+ (viper-switch-to-buffer-other-window)
+ (viper-switch-to-buffer)))
+ ("c" "change")
+ ;; ch should be "change" but maintain old viper compatibility
+ ("ch" "chdir")
+ ("cd" (ex-cd))
+ ("chdir" (ex-cd))
+ ("copy" (ex-copy nil))
+ ("customize" (customize-group "viper"))
+ ("delete" (ex-delete))
+ ("edit" (ex-edit))
+ ("file" (ex-set-visited-file-name))
+ ("g" "global")
+ ("global" (ex-global nil) is-mashed)
+ ("goto" (ex-goto))
+ ("help" (ex-help))
+ ("join" (ex-line "join"))
+ ("k" (ex-mark) one-letter)
+ ("kmark" (ex-mark))
+ ("m" "move")
+ ("make" (ex-compile))
+ ; old viper doesn't specify a default for "ma" so leave it undefined
+ ("map" (ex-map))
+ ("mark" (ex-mark))
+ ("move" (ex-copy t))
+ ("next" (ex-next ex-cycle-other-window))
+ ("p" "print")
+ ("preserve" (ex-preserve))
+ ("print" (ex-print))
+ ("put" (ex-put))
+ ("pwd" (ex-pwd))
+ ("quit" (ex-quit))
+ ("r" "read")
+ ("re" "read")
+ ("read" (ex-read))
+ ("recover" (ex-recover))
+ ("rewind" (ex-rewind))
+ ("s" "substitute")
+ ("su" "substitute")
+ ("sub" "substitute")
+ ("set" (ex-set))
+ ("shell" (ex-shell))
+ ("source" (ex-source))
+ ("stop" (suspend-emacs))
+ ("sr" (ex-substitute t t))
+ ("submitReport" (viper-submit-report))
+ ("substitute" (ex-substitute) is-mashed)
+ ("suspend" (suspend-emacs))
+ ("t" "transfer")
+ ("tag" (ex-tag))
+ ("transfer" (ex-copy nil))
+ ("u" "undo")
+ ("un" "undo")
+ ("undo" (viper-undo))
+ ("unmap" (ex-unmap))
+ ("v" "vglobal")
+ ("version" (viper-version))
+ ("vglobal" (ex-global t) is-mashed)
+ ("visual" (ex-edit))
+ ("w" "write")
+ ("wq" (ex-write t))
+ ("write" (ex-write nil))
+ ("xit" (ex-write t))
+ ("yank" (ex-yank))
+ ("~" (ex-substitute t t))
+
+ ("append" (ex-cmd-obsolete "append"))
+ ("change" (ex-cmd-obsolete "change"))
+ ("insert" (ex-cmd-obsolete "insert"))
+ ("open" (ex-cmd-obsolete "open"))
+
+ ("list" (ex-cmd-not-yet "list"))
+ ("z" (ex-cmd-not-yet "z"))
+ ("#" (ex-cmd-not-yet "#"))
+
+ ("abbreviate" (error "`%s': Vi abbreviations are obsolete. Use the more powerful Emacs abbrevs" ex-token))
+ ("unabbreviate" (error "`%s': Vi abbreviations are obsolete. Use the more powerful Emacs abbrevs" ex-token))
+ ))
+
+;; No code should touch anything in the alist entry! (other than the name,
+;; "car entry", of course) This way, changing this data structure
+;; requires changing only the following ex-cmd functions...
+
+;; Returns cmd if the command may be jammed right up against its
+;; arguments, nil if there must be a space.
+;; examples of mashable commands: g// g!// v// s// sno// sm//
+(defun ex-cmd-is-mashed-with-args (cmd)
+ (if (eq 'is-mashed (car (nthcdr 2 cmd))) cmd))
+
+;; Returns true if this is a one-letter command that may be followed
+;; by anything, no whitespace needed. This is a special-case for ":k".
+(defun ex-cmd-is-one-letter (cmd)
+ (if (eq 'one-letter (car (nthcdr 2 cmd))) cmd))
+
+;; Executes the function associated with the command
+(defun ex-cmd-execute (cmd)
+ (eval (cadr cmd)))
+
+;; If this is a one-letter magic command, splice in args.
+(defun ex-splice-args-in-1-letr-cmd (key list)
+ (let ((oneletter (ex-cmd-is-one-letter (assoc (substring key 0 1) list))))
+ (if oneletter
+ (list key
+ (append (cadr oneletter)
+ (if (< 1 (length key)) (list (substring key 1))))
+ (car (cdr (cdr oneletter))) ))
+ ))
+
+
+;; Returns the alist entry for the appropriate key.
+;; Tries to complete the key before using it in the alist.
+;; If there is no appropriate key (no match or duplicate matches) return nil
+(defun ex-cmd-assoc (key list)
+ (let ((entry (try-completion key list))
+ result)
+ (setq result (cond
+ ((eq entry t) (assoc key list))
+ ((stringp entry) (or (ex-splice-args-in-1-letr-cmd key list)
+ (assoc entry list)))
+ ((eq entry nil) (ex-splice-args-in-1-letr-cmd key list))
+ (t nil)
+ ))
+ ;; If we end up with an alias, look up the alias...
+ (if (stringp (cadr result))
+ (setq result (ex-cmd-assoc (cadr result) list)))
+ ;; and return the corresponding alist entry
+ result
+ ))
+