emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/evil f84d3453b3: Read Ex commands from buffer


From: ELPA Syncer
Subject: [nongnu] elpa/evil f84d3453b3: Read Ex commands from buffer
Date: Mon, 17 Jul 2023 16:01:39 -0400 (EDT)

branch: elpa/evil
commit f84d3453b312bd8ec0a1c092d075bbc3d91e157b
Author: Axel Forsman <axelsfor@gmail.com>
Commit: Axel Forsman <axelsfor@gmail.com>

    Read Ex commands from buffer
    
    When lexing a string with string-match and its START argument, there
    is no way to anchor matches to the START position. Instead, one must
    either allocate substrings - as done prior to commit
    56b43b6f7e014e905f85df1c542c67f46ea99566 - or use looking-at etc.,
    instead. This commit opts for the latter.
    
    The Ex completion-at-point functions are also rewritten in order to
    avoid having to add ex-index text properties to the command string,
    since evil--ex-syntactic-context could be extended to provide that
    information just as easily.
---
 evil-command-window.el |   7 +
 evil-commands.el       |  33 +-
 evil-common.el         |  27 +-
 evil-core.el           |   4 +-
 evil-ex.el             | 830 +++++++++++++++++++++----------------------------
 evil-maps.el           |  11 +-
 evil-tests.el          |  98 +++---
 evil-vars.el           |  33 +-
 8 files changed, 450 insertions(+), 593 deletions(-)

diff --git a/evil-command-window.el b/evil-command-window.el
index 21299a2dc0..23ac189477 100644
--- a/evil-command-window.el
+++ b/evil-command-window.el
@@ -124,6 +124,13 @@ function to execute."
     (let ((evil-ex-current-buffer evil-command-window-current-buffer))
       (evil-ex-execute result))))
 
+(defun evil-ex-command-window-execute (config result)
+  (select-window (active-minibuffer-window) t)
+  (set-window-configuration config)
+  (delete-minibuffer-contents)
+  (insert result)
+  (exit-minibuffer))
+
 (defun evil-command-window-search-forward ()
   "Open a command line window for forward searches."
   (interactive)
diff --git a/evil-commands.el b/evil-commands.el
index fb8263ba90..f8b9cd12ee 100644
--- a/evil-commands.el
+++ b/evil-commands.el
@@ -3560,14 +3560,13 @@ the previous shell command is executed instead."
              (if (or current-prefix-arg (evil-visual-state-p))
                  current-prefix-arg
                (goto-char (min beg end))
-               (setq current-prefix-arg (count-lines beg end))))
+               (count-lines beg end)))
             (evil-ex-initial-input "!"))
         (call-interactively #'evil-ex))
-    (when command
-      (setq command (evil-ex-replace-special-filenames command)))
     (if (zerop (length command))
         (when previous (setq command evil-previous-shell-command))
-      (setq evil-previous-shell-command command))
+      (setq command (evil-ex-replace-special-filenames command)
+            evil-previous-shell-command command))
     (cond
      ((zerop (length command))
       (user-error "No%s shell command" (if previous " previous" "")))
@@ -3577,10 +3576,9 @@ the previous shell command is executed instead."
         (let ((output-buffer (generate-new-buffer " *temp*"))
               (error-buffer (generate-new-buffer " *temp*")))
           (unwind-protect
-              (if (zerop (shell-command-on-region beg end
-                                                  command
-                                                  output-buffer nil
-                                                  error-buffer))
+              (if (zerop (shell-command-on-region
+                          beg end command
+                          output-buffer nil error-buffer))
                   (progn
                     (delete-region beg end)
                     (insert-buffer-substring output-buffer)
@@ -3794,7 +3792,7 @@ Signal an error if the file does not exist."
   "A valid evil state."
   :ex-arg state
   (list (when (and (evil-ex-p) evil-ex-argument)
-          (intern evil-ex-argument))))
+          (intern-soft evil-ex-argument))))
 
 ;; TODO: should we merge this command with `evil-set-initial-state'?
 (evil-define-command evil-ex-set-initial-state (state)
@@ -4304,25 +4302,20 @@ range. The given argument is passed straight to
   (evil-with-single-undo
     (let (markers evil-ex-current-buffer prefix-arg current-prefix-arg)
       (goto-char beg)
-      (while
-          (and (< (point) end)
-               (progn
-                 (push (move-marker (make-marker) (line-beginning-position))
-                       markers)
-                 (and (= (forward-line) 0) (bolp)))))
+      (beginning-of-line)
+      (while (when (< (point) end)
+               (push (point-marker) markers)
+               (and (= (forward-line) 0) (bolp))))
       (setq markers (nreverse markers))
       (deactivate-mark)
       (evil-force-normal-state)
       ;; replace ^[ by escape
       (setq commands
             (vconcat
-             (mapcar #'(lambda (ch) (if (equal ch ?) 'escape ch))
-                     (append commands nil))))
+             (mapcar (lambda (ch) (if (eq ch ?) 'escape ch)) commands)))
       (dolist (marker markers)
         (goto-char marker)
-        (condition-case nil
-            (execute-kbd-macro commands)
-          (error nil))
+        (ignore-errors (execute-kbd-macro commands))
         (evil-force-normal-state)
         (set-marker marker nil)))))
 
diff --git a/evil-common.el b/evil-common.el
index 7e6b568c24..51d55b03bb 100644
--- a/evil-common.el
+++ b/evil-common.el
@@ -811,14 +811,13 @@ Inhibits echo area messages, mode line updates and cursor 
changes."
      ,@body))
 
 (defun evil-count-lines (beg end)
-  "Return absolute line-number-difference betweeen `beg` and `end`.
-This should give the same results no matter where on the line `beg`
-and `end` are."
+  "Return absolute line-number-difference betweeen BEG and END.
+This should give the same results no matter where on the line BEG
+and END are."
   (if (= beg end)
       0
-    (let* ((last (max beg end))
-           (end-at-bol (save-excursion (goto-char last)
-                                       (bolp))))
+    (let ((end-at-bol (save-excursion (goto-char (max beg end))
+                                      (bolp))))
       (if end-at-bol
           (count-lines beg end)
         (1- (count-lines beg end))))))
@@ -978,14 +977,13 @@ argument.  Movement is constrained to the current field."
   "Return the position of LINE.
 If COLUMN is specified, return its position on the line.
 A negative number means the end of the line."
-  (declare-function evil-goto-line "evil-commands")
   (save-excursion
-    (evil-goto-line line)
-    (if (numberp column)
-        (if (< column 0)
-            (beginning-of-line 2)
-          (move-to-column column))
-      (beginning-of-line))
+    (goto-char (point-min))
+    (forward-line (1- line))
+    (when (numberp column)
+      (if (< column 0)
+          (forward-line)
+        (move-to-column column)))
     (point)))
 
 (defun evil-column (&optional pos)
@@ -2007,8 +2005,7 @@ The following special registers are supported.
                         (t search-ring)))
                   (user-error "No previous regular expression")))
              ((eq register ?:)
-              (or (car-safe evil-ex-history)
-                  (user-error "No previous command line")))
+              (or (car evil-ex-history) (user-error "No previous command 
line")))
              ((eq register ?.)
               evil-last-insertion)
              ((eq register ?-)
diff --git a/evil-core.el b/evil-core.el
index fabb050223..533d94f6f5 100644
--- a/evil-core.el
+++ b/evil-core.el
@@ -848,7 +848,7 @@ does not already exist."
       (let ((map (make-sparse-keymap)))
         (evil-set-keymap-prompt
          map (format "Minor-mode keymap for %s in %s"
-                     (symbol-name mode)
+                     mode
                      (or (evil-state-property state :name)
                          (format "%s state" state))))
         (if state-entry
@@ -973,7 +973,7 @@ mode, in which case `evil-define-minor-mode-key' is used."
   (declare (indent defun))
   (cond ((member keymap '('global 'local))
          `(evil-define-key* ,state ,keymap ,key ,def ,@bindings))
-        ((and (consp keymap) (eq (car keymap) 'quote))
+        ((eq (car-safe keymap) 'quote)
          `(evil-define-minor-mode-key ,state ,keymap ,key ,def ,@bindings))
         (t
          `(evil-delay ',(if (symbolp keymap)
diff --git a/evil-ex.el b/evil-ex.el
index 7b42930551..fa3aada0e2 100644
--- a/evil-ex.el
+++ b/evil-ex.el
@@ -33,7 +33,7 @@
 ;; to an interactive function.  It is also possible to define key
 ;; sequences which execute a command immediately when entered:
 ;; such shortcuts go in `evil-ex-map'.
-;;
+
 ;; To provide buffer and filename completion, as well as interactive
 ;; feedback, Ex defines the concept of an argument handler, specified
 ;; with `evil-ex-define-argument-type'.  In the case of the
@@ -71,34 +71,27 @@
         #'(let ((l1 $1))
             (save-excursion
               (and l1 (string= $2 ";") (goto-line l1))
-              (evil-ex-range (or l1 (evil-ex-current-line)) $3))))
+              (evil-ex-range l1 $3))))
        (line #'evil-ex-range)
        ("`" marker-name ",`" marker-name
         #'(evil-ex-char-marker-range $2 $4)))
       (line
-       (base (\? offset) search (\? offset)
-             #'(let ((tmp (evil-ex-line $1 $2)))
-                 (save-excursion
-                   (goto-line tmp)
-                   (evil-ex-line $3 $4))))
-       ((\? base) offset search (\? offset)
+       ((\? base) (\? offset) search (\? offset)
         #'(let ((tmp (evil-ex-line $1 $2)))
             (save-excursion
               (goto-line tmp)
               (evil-ex-line $3 $4))))
        (base (\? offset) #'evil-ex-line)
-       ((\? base) offset #'evil-ex-line))
+       (nil offset #'evil-ex-line))
       (base
        number
-       marker
+       ("'" marker-name #'(evil-ex-marker $2))
        search
        ("\\^" #'(evil-ex-first-line))
        ("\\$" #'(evil-ex-last-line))
        ("\\." #'(evil-ex-current-line)))
       (offset
        (+ signed-number #'+))
-      (marker
-       ("'" marker-name #'(evil-ex-marker $2)))
       ;; TODO - handle offset & ;next-pattern search elements
       (search
        forward
@@ -107,9 +100,9 @@
        prev
        subst)
       (forward
-       ("/" "\\(?:[\\].\\|[^/]\\)+" "/\\|$" #'(evil-ex-re-fwd $2)))
+       ("/" "\\(?:\\\\.\\|[^/]\\)+" "/\\|$" #'(evil-ex-re-fwd $2)))
       (backward
-       ("\\?" "\\(?:[\\].\\|[^?]\\)+" "\\?\\|$" #'(evil-ex-re-bwd $2)))
+       ("?" "\\(?:\\\\.\\|[^?]\\)+" "?\\|$" #'(evil-ex-re-bwd $2)))
       (marker-name
        "[]\\[-a-zA-Z_<>'}{)(]")
       (next
@@ -121,11 +114,11 @@
       (signed-number
        (sign (\? number) #'evil-ex-signed-number))
       (sign
-       "\\+\\|-" #'intern)
+       "[+-]" #'intern)
       (number
        "[0-9]+" #'string-to-number)
       (sexp
-       "(.*)" #'(car-safe (read-from-string $0))))
+       "(.*)?" #'(car (read-from-string $0))))
     "Grammar for Ex.
 An association list of syntactic symbols and their definitions.
 The first entry is the start symbol.  A symbol's definition may
@@ -143,18 +136,15 @@ Given e.g. $4, return 4."
 
   (defmacro evil-parser (grammar &rest entrypoints)
     "Construct a parser for GRAMMAR with ENTRYPOINTS.
-The result is a function taking the arguments STRING, SYMBOL and
-SYNTAX, that parses STRING. SYMBOL should be one of ENTRYPOINTS.
+The result is a function taking the arguments SYMBOL and SYNTAX, that
+parses the text after point. SYMBOL should be one of ENTRYPOINTS.
 
-If the parse succeeds, the return value is a cons cell
-\(RESULT . END), where RESULT is a parse tree and END is the start of
-the remainder of STRING. Otherwise, the return value is nil.
+If parsing succeeds, point is moved to the end of the parsed text and
+a 1-tuple (RESULT) is returned. Otherwise, the return value is nil.
 
 GRAMMAR is an association list of symbols and their definitions.
 A definition is a list of production rules, which are tried in
-succession.
-
-A production rule can be one of the following:
+succession. A production rule can be one of the following:
 
     nil matches the empty string.
     A regular expression matches a substring.
@@ -203,33 +193,24 @@ The following symbols have reserved meanings within a 
grammar:
 `\\?', `*', `+', `&', `!', `function', `alt', `seq' and nil."
     (cl-labels
         ;; Return code for parsing PRODUCTION.
-        ;; Assumes the variable POS stores the current offset into
-        ;; STRING.
         ((compile
           (production)
           (pcase production
-            ((or 'nil "") '(cons (when syntax "") pos)) ; Epsilon
+            ('nil '(list (when syntax (point)))) ; Epsilon
             ((and (pred stringp) regexp) ; Token
-             `(when
-                  ;; Ignore leading whitespace
-                  (let ((start (string-match-p "[^ \f\t\n\r\v]\\|\\'" string 
pos)))
-                    (equal (string-match ,regexp string start) start))
-                (cons (if syntax (substring string pos (match-end 0))
-                        (match-string 0 string))
-                      (match-end 0))))
+             `(when (progn
+                      (skip-chars-forward " \t\n\r") ; Ignore leading 
whitespace
+                      (looking-at ,regexp))
+                (goto-char (match-end 0))
+                (list (if syntax (point) (match-string 0)))))
             ((and (pred symbolp) symbol) ; Symbol
-             `(let ((pair (,symbol string pos syntax)))
-                (and syntax pair
-                     (setcar
-                      pair
-                      (let ((result (car pair)))
-                        (cons ',symbol
-                              (if (listp result) result (list result))))))
-                pair))
+             `(let ((result (,symbol syntax)))
+                (and syntax result (push ',symbol (car result)))
+                result))
             (`(function ,fun) ; Function
-             `(let ((pair (funcall #',fun string pos)))
-                (and pair syntax (setcar pair (substring string pos (cdr 
pair))))
-                pair))
+             `(let ((result (funcall #',fun)))
+                (and syntax result (setcar result (point)))
+                result))
             ;; Positive lookahead
             (`(& . ,rule) `(when ,(compile rule) ,(compile nil)))
             ;; Negative lookahead
@@ -244,44 +225,40 @@ The following symbols have reserved meanings within a 
grammar:
              (let ((func (unless (eq symbol 'alt) #'list)))
                (pcase (when (> (length rules) 1) (car (last rules)))
                  (`(function ,x) (setq func x
-                                       rules (butlast (copy-sequence rules)))))
-               `(let ((pair
+                                       rules (butlast rules))))
+               `(let ((cell
                        ,(pcase symbol
                           ('+ ; One or more
                            (when (cdr rules) (error "Too many `+' rules"))
-                           `(let ((pos pos) result)
-                              (while (let ((x ,(compile (car rules))))
-                                       (when x
-                                         (push (car x) result)
-                                         (< (setq pos (cdr x)) (length 
string)))))
-                              (when result (cons (nreverse result) pos))))
+                           `(cl-loop for x = ,(compile (car rules))
+                                     while x collect (car x) into result until 
(eobp)
+                                     finally return (when result (list 
result))))
                           ('alt `(or ,@(mapcar #'compile rules)))
                           ('seq
                            (cl-loop
                             for rule in rules collect
-                            `(let ((x ,(compile rule)))
-                               (when x
-                                 (setq pos (cdr x))
-                                 ,(if (memq (car-safe rule) '(& !)) t
-                                    `(push (car x) result))))
+                            (macroexp-let2 nil x (compile rule)
+                              (if (memq (car-safe rule) '(& !)) x
+                                `(when ,x (push (car ,x) result))))
                             into items finally return
-                            `(let ((pos pos) result)
-                               (and ,@items (cons (nreverse result) pos))))))))
+                            `(let ((pos (point)) result)
+                               (if (and ,@items) (list (nreverse result))
+                                 (goto-char pos)
+                                 nil)))))))
                   ;; Semantic action
-                  ,(when func
-                     `(when (and pair (not syntax))
-                        (let ((result (car pair)))
-                          (ignore result) ; Suppress unused var warning
-                          (setcar
-                           pair
-                           ,(pcase func
-                              ;; Dollar expression
-                              ((or (pred evil-parser--dexp) (pred listp))
-                               (dval func))
-                              ((pred symbolp)
-                               `(,(if (eq symbol 'alt) 'list 'cons) #',func 
result))
-                              (_ (error "Invalid semantic action `%S'" 
func)))))))
-                  pair)))))
+                  (when (and ',func cell (not syntax))
+                    (setcar
+                     cell
+                     (let ((result (car cell)))
+                       (ignore result) ; Suppress unused var warning
+                       ,(pcase func
+                          ;; Dollar expression
+                          ((or (pred evil-parser--dexp) (pred listp))
+                           (dval func))
+                          ((pred symbolp)
+                           `(,(if (eq symbol 'alt) #'list #'cons) #',func 
result))
+                          (_ (error "Invalid semantic action `%S'" func))))))
+                  cell)))))
          ;; Substitute all dollar-sign symbols in X.
          ;; Each dollar-sign symbol is replaced with the corresponding
          ;; element in RESULT, so that $1 becomes the first element, etc.
@@ -290,46 +267,58 @@ The following symbols have reserved meanings within a 
grammar:
           (x)
           (if (listp x) (cons #'list (mapcar #'dval x))
             (let ((num (evil-parser--dexp x)))
-              (cond ((null num) `(quote ,x))
+              (cond ((null num) `',x)
                     ((eq num 0) 'result)
                     (t `(nth (1- ,num) result)))))))
-      `(lambda (string symbol &optional syntax)
+      `(lambda (symbol &optional syntax)
          (cl-labels
-             (,@(cl-loop
-                 for (symbol . def) in (eval grammar t) collect
-                 `(,symbol (string pos syntax) ,(compile `(alt . ,def))))
+             (,@(cl-loop for (symbol . def) in (eval grammar t) collect
+                         `(,symbol (syntax) ,(compile `(alt . ,def))))
               (evil-ex-parse-command
-               (string pos)
-               (let ((result (binding string pos nil)) command end)
-                 (when result
-                   (setq command (car result)
-                         end (cdr result))
-                   (cond
-                    ;; check whether the parsed command is followed by a slash,
-                    ;; dash or number and either the part before is NOT known 
to be
-                    ;; a binding, or the complete string IS known to be a 
binding
-                    ((and (< end (length string))
-                          (let ((ch (aref string end)))
-                            (or (memq ch '(?- ?/)) (<= ?0 ch ?9)))
-                          (or (evil-ex-binding
-                               (concat command (substring string end)) t)
-                              (not (evil-ex-binding command t))))
-                     (emacs-binding string pos nil))
-                    ;; parse a following "!" as bang only if the
-                    ;; command has the property :ex-bang t
-                    ((and (evil-ex-command-force-p command)
-                          (< end (length string))
-                          (eq (aref string end) ?!))
-                     (cons (concat command "!") (1+ end)))
-                    (t result))))))
+               ()
+               (let* ((result (binding nil))
+                      (command (car result)))
+                 (cond
+                  ((not result) nil)
+                  ;; Check whether the parsed command is followed by a slash,
+                  ;; dash or number and either the part before is NOT known to 
be
+                  ;; a binding, or the complete string IS known to be a 
binding.
+                  ((and (let ((ch (char-after)))
+                          (and ch (or (memq ch '(?- ?/)) (<= ?0 ch ?9))))
+                        (or (evil-ex-binding
+                             (concat command (buffer-substring (point) 
(point-max))) t)
+                            (not (evil-ex-binding command t))))
+                   (backward-char (length command))
+                   (emacs-binding nil))
+                  ;; Parse a following "!" as bang only if the command
+                  ;; has the property :ex-bang t.
+                  ((when (eq (char-after) ?!)
+                     (let ((binding (evil-ex-completed-binding command t)))
+                       (and binding (evil-get-command-property binding 
:ex-bang))))
+                   (forward-char)
+                   (setcar result (concat command "!"))
+                   result)
+                  (t result)))))
            (pcase symbol
-             ,@(cl-loop
-                for sym in entrypoints collect
-                `(',sym (let ((pos 0)) ,(compile sym))))
+             ,@(cl-loop for sym in entrypoints collect `(',sym ,(compile sym)))
              (_ (error "Unknown entrypoint `%s'" symbol))))))))
 
-(defvar evil-ex-echo-overlay nil
-  "Overlay used for displaying info messages during Ex.")
+(defvar evil-ex-argument-types nil
+  "Association list of argument handlers.")
+
+(defvar evil-ex-reverse-range nil
+  "Whether the current Ex range was entered reversed.")
+
+(defvar evil--ex-expression nil
+  "The Ex evaluation tree.")
+
+(defvar evil--ex-cmd nil
+  "The current Ex command string.")
+
+(defvar evil-ex-argument-handler nil
+  "The argument handler for the current Ex command.")
+
+(define-error 'evil-ex-error "Ex syntax error")
 
 (defun evil-ex-p ()
   "Whether Ex is currently active."
@@ -337,15 +326,12 @@ The following symbols have reserved meanings within a 
grammar:
 
 (evil-define-command evil-ex (&optional initial-input)
   "Enter an Ex command.
-The ex command line is initialized with the value of
-INITIAL-INPUT. If the command is called interactively the initial
-input depends on the current state. If the current state is
-normal state and no count argument is given then the initial
-input is empty. If a prefix count is given the initial input is
-.,.+count. If the current state is visual state then the initial
-input is the visual region '<,'> or `<,`>. If the value of the
-global variable `evil-ex-initial-input' is non-nil, its content
-is appended to the line."
+The Ex command line is initialized with the value of INITIAL-INPUT. If
+the command is called interactively the initial input depends on the
+current state. In Normal state if a prefix count is given then the
+initial input is \".,.+count\", otherwise it is empty. In Visual state
+the initial input is the visual region '<,'> or `<,`>. The variable
+`evil-ex-initial-input', if non-nil, is appended to the line."
   :keep-visual t
   :repeat abort
   (interactive
@@ -364,47 +350,57 @@ is appended to the line."
              evil-ex-initial-input)))
      (list (unless (string= s "") s))))
   (let ((evil-ex-current-buffer (current-buffer))
-        (evil-ex-previous-command (unless initial-input
-                                    (car evil-ex-history)))
-        evil-ex-argument-handler result)
+        (previous-command (when evil-want-empty-ex-last-command
+                            (car evil-ex-history)))
+        evil--ex-expression evil--ex-cmd evil-ex-argument-handler s)
     (minibuffer-with-setup-hook
         (lambda ()
           (evil-ex-setup)
-          (when initial-input (evil-ex-update)))
-      (setq result
-            (read-from-minibuffer
-             ":"
-             (or initial-input
-                 (and evil-ex-previous-command
-                      evil-want-empty-ex-last-command
-                      (propertize evil-ex-previous-command 'face 'shadow)))
-             evil-ex-completion-map
-             nil
-             'evil-ex-history
-             (when evil-want-empty-ex-last-command evil-ex-previous-command)
-             t)))
-    (evil-ex-execute result)))
-
-(defun evil-ex-execute (result)
-  "Execute RESULT as an ex command on `evil-ex-current-buffer'."
-  ;; empty input means repeating the previous command
-  (when (and (zerop (length result))
-             evil-want-empty-ex-last-command)
-    (setq result evil-ex-previous-command))
-  ;; parse data
-  (evil-ex-update nil nil nil result)
-  ;; execute command
-  (unless (zerop (length result))
-    (eval (or evil-ex-expression (user-error "Ex: syntax error")))))
+          (if initial-input (evil--ex-update)
+            (when previous-command
+              (add-hook 'pre-command-hook #'evil-ex-remove-default nil t))))
+      (setq s (read-from-minibuffer
+               ":"
+               (or initial-input
+                   (and previous-command (propertize previous-command 'face 
'shadow)))
+               evil-ex-completion-map nil 'evil-ex-history nil t)))
+    (and (string= s "") previous-command (setq s previous-command))
+    (unless (string= s "") (evil-ex-execute s))))
+
+(defun evil-ex-execute (string)
+  "Execute STRING as an Ex command on `evil-ex-current-buffer'."
+  (let ((evil-ex-current-buffer (or evil-ex-current-buffer (current-buffer)))
+        (expr (or evil--ex-expression (evil-ex-parse string)
+                  (signal 'evil-ex-error string))))
+    (eval expr t)))
+
+(defun evil-ex-parse (string &optional syntax entrypoint)
+  "Parse STRING as an Ex expression and return an evaluation tree.
+If STRING is nil, parse the text after point instead.  If SYNTAX is
+non-nil, return a syntax tree instead.  ENTRYPOINT is the start
+symbol, which defaults to `expression'."
+  (let ((parse
+         (lambda ()
+           (let ((result (funcall (evil-parser evil-ex-grammar expression 
range)
+                                  (or entrypoint 'expression) syntax)))
+             (and result
+                  ;; Disallow incomplete matches (ignore trailing WS)
+                  (not (search-forward "[^ \t\n\r]" nil t))
+                  (car result))))))
+    (if (not string) (funcall parse)
+      (with-temp-buffer
+        (insert string)
+        (goto-char (point-min))
+        (funcall parse)))))
 
 (defun evil-ex-delete-backward-char ()
   "Close the minibuffer if it is empty.
 Otherwise behaves like `delete-backward-char'."
   (interactive)
   (call-interactively
-   (if (zerop (length (minibuffer-contents-no-properties)))
-       #'abort-recursive-edit
-     #'delete-backward-char)))
+   (if (< (minibuffer-prompt-end) (point-max))
+       #'delete-backward-char
+     #'abort-recursive-edit)))
 
 (defun evil-ex-abort ()
   "Cancel Ex state when another buffer is selected."
@@ -412,36 +408,17 @@ Otherwise behaves like `delete-backward-char'."
               (memq this-command '(mouse-drag-region choose-completion)))
     (abort-recursive-edit)))
 
-(defun evil-ex-command-window-execute (config result)
-  (select-window (active-minibuffer-window) t)
-  (set-window-configuration config)
-  (delete-minibuffer-contents)
-  (insert result)
-  (exit-minibuffer))
-
-(defun evil--ex-elisp-p ()
-  "Return whether an Elisp expression is being entered on the Ex command line."
-  (string-prefix-p "(" (minibuffer-contents-no-properties)))
-
-(defun evil-ex-elisp-completion-at-point ()
-  "Complete an `evil-ex' Elisp expression."
-  (and (evil--ex-elisp-p)
-       (fboundp 'elisp-completion-at-point)
-       (elisp-completion-at-point)))
+(define-obsolete-function-alias
+  'evil-ex-elisp-completion-at-point #'elisp-completion-at-point "1.15.0")
 
 (defun evil-ex-setup ()
   "Initialize Ex minibuffer.
 This function registers hooks that are used for the interactive
 actions during Ex state."
   (add-hook 'post-command-hook #'evil-ex-abort)
-  (add-hook 'after-change-functions #'evil-ex-update nil t)
+  (add-hook 'after-change-functions #'evil--ex-update nil t)
   (add-hook 'minibuffer-exit-hook #'evil-ex-teardown nil t)
-  (when evil-ex-previous-command
-    (add-hook 'pre-command-hook #'evil-ex-remove-default nil t))
-  (set (make-local-variable 'completion-at-point-functions)
-       '(evil-ex-elisp-completion-at-point
-         evil-ex-command-completion-at-point
-         evil-ex-argument-completion-at-point)))
+  (add-hook 'completion-at-point-functions #'evil-ex-completion-at-point nil 
t))
 
 (defun evil-ex-teardown ()
   "Deinitialize Ex minibuffer.
@@ -450,114 +427,100 @@ Clean up everything set up by `evil-ex-setup'."
   (when evil-ex-argument-handler
     (let ((runner (evil-ex-argument-handler-runner
                    evil-ex-argument-handler)))
-      (when runner
-        (funcall runner 'stop)))))
+      (when runner (funcall runner 'stop)))))
 (put 'evil-ex-teardown 'permanent-local-hook t)
 
-(defun evil-ex-update (&optional beg _end _len string)
+(defsubst evil--ex-bang-p (command)
+  "Return non-nil if the string COMMAND has a \"!\" suffix."
+  (and (> (length command) 1) (eq (aref command (1- (length command))) ?!)))
+
+(defun evil--ex-update (&optional beg _end _len string)
   "Update Ex variables when the minibuffer changes.
 This function is usually called from `after-change-functions'
 hook. If BEG is non-nil (which is the case when called from
 `after-change-functions'), then an error description is shown
 in case of incomplete or unknown commands."
-  (let* ((prompt (minibuffer-prompt-end))
-         (string (or string (minibuffer-contents-no-properties)))
-         arg bang cmd count expr func handler range type)
-    (if (and (eq this-command #'self-insert-command)
-             (commandp (setq cmd (lookup-key evil-ex-map string))))
-        (progn
-          (setq evil-ex-expression `(call-interactively #',cmd))
-          (when (minibufferp) (exit-minibuffer)))
-      (setq cmd nil)
-      ;; store the buffer position of each character
-      ;; as the `ex-index' text property
-      (dotimes (i (length string))
-        (put-text-property i (1+ i) 'ex-index (+ i prompt) string))
+  (when (and beg (eq this-command #'self-insert-command))
+    (let ((cmd (lookup-key evil-ex-map (minibuffer-contents-no-properties))))
+      (when (commandp cmd)
+        (setq evil--ex-expression `(call-interactively #',cmd))
+        (exit-minibuffer))))
+
+  (setq evil--ex-expression (save-excursion (goto-char (minibuffer-prompt-end))
+                                            (evil-ex-parse string))
+        evil--ex-cmd nil)
+  (when (eq (car evil--ex-expression) #'evil-ex-call-command)
+    (let (current-prefix-arg func handler evil-ex-range evil-ex-bang 
evil-ex-argument)
       (with-current-buffer evil-ex-current-buffer
-        (setq expr (evil-ex-parse string))
-        (when (eq (car expr) #'evil-ex-call-command)
-          (setq count (eval (nth 1 expr))
-                cmd (eval (nth 2 expr))
-                arg (eval (nth 3 expr))
-                range (cond
-                       ((evil-range-p count) count)
-                       ((numberp count) (evil-ex-range count count)))
-                bang (when (string-match-p ".!$" cmd) t))))
-      (setq evil-ex-expression expr
-            evil-ex-range range
-            evil-ex-cmd cmd
-            evil-ex-bang bang
-            evil-ex-argument arg)
-      ;; test the current command
-      (when (and cmd (minibufferp))
-        (setq func (evil-ex-completed-binding cmd t))
-        (cond
-         ;; update argument-handler
-         (func
-          (when (setq type (evil-get-command-property func :ex-arg))
-            (setq handler (cdr-safe
-                           (assoc type
-                                  evil-ex-argument-types))))
-          (unless (eq handler evil-ex-argument-handler)
-            (let ((runner (and evil-ex-argument-handler
-                               (evil-ex-argument-handler-runner
-                                evil-ex-argument-handler))))
-              (when runner (funcall runner 'stop)))
-            (setq evil-ex-argument-handler handler)
-            (let ((runner (and evil-ex-argument-handler
-                               (evil-ex-argument-handler-runner
-                                evil-ex-argument-handler))))
-              (when runner (funcall runner 'start evil-ex-argument))))
-          (let ((runner (and evil-ex-argument-handler
-                             (evil-ex-argument-handler-runner
-                              evil-ex-argument-handler))))
-            (when runner (funcall runner 'update evil-ex-argument))))
-         ;; show error message only when called from `after-change-functions'
-         (beg
-          (let ((prefix (try-completion cmd (evil-ex-completion-table))))
-            (cond
-             ((stringp prefix) (evil-ex-echo "Incomplete command"))
-             ((null prefix) (evil-ex-echo "Unknown command"))))))))))
+        (let* ((range (eval (nth 1 evil--ex-expression) t))
+               (count (when (integerp range) range)))
+          (setq current-prefix-arg count
+                evil-ex-range (if count (evil-ex-range count count) range)
+                evil--ex-cmd (eval (nth 2 evil--ex-expression) t)
+                evil-ex-bang (evil--ex-bang-p evil--ex-cmd)
+                evil-ex-argument (eval (nth 3 evil--ex-expression) t))))
+      (cond
+       ((not beg))
+       ;; Test the current command when called from `after-change-functions'
+       ((setq func (evil-ex-completed-binding evil--ex-cmd t))
+        ;; Update argument handler
+        (let ((type (evil-get-command-property func :ex-arg)))
+          (when type (setq handler (cdr (assq type evil-ex-argument-types)))))
+        (if (eq handler evil-ex-argument-handler)
+            (let ((runner (evil-ex-argument-handler-runner handler)))
+              (when runner (funcall runner 'update evil-ex-argument)))
+          (let ((runner (evil-ex-argument-handler-runner
+                         evil-ex-argument-handler)))
+            (when runner (funcall runner 'stop)))
+          (setq evil-ex-argument-handler handler)
+          (let ((runner (evil-ex-argument-handler-runner handler)))
+            (when runner (funcall runner 'start evil-ex-argument)))))
+       (t (let* ((evil-ex-complete-emacs-commands 'in-turn)
+                 (prefix (try-completion evil--ex-cmd 
(evil-ex-completion-table))))
+            (cond ((stringp prefix) (evil-ex-echo "Incomplete command"))
+                  ((null prefix) (evil-ex-echo "Unknown command")))))))))
 
 (defun evil-ex-echo (string &rest args)
   "Display a message after the current Ex command."
-  (with-selected-window (minibuffer-window)
-    (unless (or evil-no-display (zerop (length string)))
-      (let ((string (format " [%s]" (apply #'format string args)))
-            (ov (or evil-ex-echo-overlay
-                    (setq evil-ex-echo-overlay (make-overlay (point-min) 
(point-max) nil t t))))
-            after-change-functions before-change-functions)
-        (put-text-property 0 (length string) 'face 'evil-ex-info string)
-        ;; The following "trick" causes point to be shown before the
-        ;; message instead of behind. It is shamelessly stolen from
-        ;; the implementation of `minibuffer-message'.
-        (put-text-property 0 1 'cursor t string)
-        (move-overlay ov (point-max) (point-max))
-        (overlay-put ov 'after-string string)
-        (add-hook 'pre-command-hook #'evil--ex-remove-echo-overlay nil t)))))
-
-(defun evil--ex-remove-echo-overlay ()
-  "Remove echo overlay from Ex minibuffer."
-  (when evil-ex-echo-overlay
-    (delete-overlay evil-ex-echo-overlay)
-    (setq evil-ex-echo-overlay nil))
-  (remove-hook 'pre-command-hook #'evil--ex-remove-echo-overlay t))
-
-(defun evil-ex-completion ()
-  "Complete the current Ex command or argument."
-  (interactive)
-  (let (after-change-functions)
-    (evil-ex-update)
-    (completion-at-point)
-    (remove-list-of-text-properties
-     (minibuffer-prompt-end) (point-max) '(face evil))))
-
-(defun evil-ex-command-completion-at-point ()
-  (let ((beg (if evil-ex-cmd
-                 (get-text-property 0 'ex-index evil-ex-cmd)
-               (point)))
-        (end (point)))
-    (list beg end (evil-ex-completion-table) :exclusive 'no)))
+  (unless (or evil-no-display (string= string ""))
+    (let ((message (concat " [" (apply #'format string args) "]")))
+      (add-face-text-property 1 (length message) 'evil-ex-info nil message)
+      (minibuffer-message message))))
+
+(define-obsolete-function-alias 'evil-ex-completion #'completion-at-point 
"1.15.0")
+
+(cl-defun evil-ex-completion-at-point ()
+  "Function used for `completion-at-point-functions' in Ex state."
+  (cl-flet ((fix-beg (b) (min (save-excursion
+                                (+ (goto-char b) (skip-chars-forward " 
\t\n\r")))
+                              (point))))
+    (pcase (nreverse (evil--ex-syntactic-context))
+      ((or (and 'nil (let beg (minibuffer-prompt-end)))
+           `((expression) (command . ,beg) . ,_)
+           (and `((expression) (line) . ,_)
+                (guard (looking-at-p "[ \t\n\r]*\\'"))
+                (let beg (point))))
+       (list (fix-beg beg) (point) (evil-ex-completion-table)))
+      (`((expression) (argument . ,beg))
+       (setq beg (fix-beg beg))
+       ;; If it's an autoload, load the function; this allows external
+       ;; packages to register autoloaded Ex commands which will be
+       ;; loaded when ex argument completion is triggered.
+       (let ((binding (evil-ex-binding evil--ex-cmd t))) (autoload-do-load 
binding))
+
+       (let* ((binding (evil-ex-completed-binding evil--ex-cmd))
+              (arg-type (evil-get-command-property binding :ex-arg))
+              (arg-handler (cdr (assq arg-type evil-ex-argument-types))))
+         (pcase (evil-ex-argument-handler-completer arg-handler)
+           (`(collection . ,table) (list beg (point-max) table))
+           (`(completion-at-point . ,completer)
+            (save-restriction (narrow-to-region beg (point-max))
+                              (funcall completer))))))
+      (`((expression) (sexp . ,_))
+       (when (fboundp 'elisp-completion-at-point) 
(elisp-completion-at-point))))))
+
+(define-obsolete-function-alias
+  'evil-ex-command-completion-at-point #'evil-ex-completion-at-point "1.15.0")
 
 (defun evil-ex-completion-table ()
   (let ((ex-cmds
@@ -567,19 +530,16 @@ in case of incomplete or unknown commands."
           ;; Append ! to all commands that may take a bang argument
           when (evil-get-command-property fun :ex-bang)
           collect (concat cmd "!")))
-        (emacs-cmds
-         (lambda (str pred action)
-           (completion-table-with-predicate
-            obarray #'commandp t str pred action))))
-    (when (eq evil-ex-complete-emacs-commands t)
-      (setq ex-cmds
-            (mapcar (lambda (str) (propertize str 'face 'evil-ex-commands))
-                    ex-cmds)))
+        (emacs-cmds (lambda (str pred action)
+                      (completion-table-with-predicate
+                       obarray #'commandp t str pred action))))
     (cond
      ((null evil-ex-complete-emacs-commands) ex-cmds)
      ((eq evil-ex-complete-emacs-commands 'in-turn)
       (completion-table-in-turn ex-cmds emacs-cmds))
-     (t (evil-completion-table-concat ex-cmds emacs-cmds)))))
+     (t (cl-loop for s in-ref ex-cmds do
+                 (setf s (propertize s 'face 'evil-ex-commands)))
+        (evil-completion-table-concat ex-cmds emacs-cmds)))))
 
 (defun evil-completion-table-concat (table1 table2)
   (lambda (string pred action)
@@ -605,65 +565,26 @@ in case of incomplete or unknown commands."
 
 (defun evil-ex-sort-completions (completions)
   (sort completions
-        #'(lambda (str1 str2)
-            (let ((p1 (eq 'evil-ex-commands (get-text-property 0 'face str1)))
-                  (p2 (eq 'evil-ex-commands (get-text-property 0 'face str2))))
-              (if (equal p1 p2)
-                  (string< str1 str2)
-                p1)))))
-
-(defun evil-ex-command-collection (string predicate action)
-  (declare (obsolete evil-ex-completion-table "1.15.0"))
-  (let* (evil-ex-complete-emacs-commands
-         (commands (evil-ex-completion-table)))
-    (cond
-     ((eq action nil) (try-completion string commands predicate))
-     ((eq action t) (all-completions string commands predicate))
-     ((eq action 'lambda) (test-completion string commands))
-     ((eq (car-safe action) 'boundaries)
-      `(boundaries 0 . ,(length (cdr action)))))))
-
-(defun evil-ex-argument-completion-at-point ()
-  (let ((context (evil-ex-syntactic-context)))
-    (when (memq 'argument context)
-      ;; if it's an autoload, load the function; this allows external
-      ;; packages to register autoloaded ex commands which will be
-      ;; loaded when ex argument completion is triggered
-      (let ((binding-definition (symbol-function (evil-ex-binding 
evil-ex-cmd))))
-        (when (autoloadp binding-definition)
-          (autoload-do-load binding-definition)))
-
-      (let* ((beg (or (and evil-ex-argument
-                           (get-text-property 0 'ex-index evil-ex-argument))
-                      (point)))
-             (end (1+ (or (and evil-ex-argument
-                               (get-text-property (1- (length 
evil-ex-argument))
-                                                  'ex-index evil-ex-argument))
-                          (1- (point)))))
-             (binding (evil-ex-completed-binding evil-ex-cmd))
-             (arg-type (evil-get-command-property binding :ex-arg))
-             (arg-handler (assoc arg-type evil-ex-argument-types))
-             (completer (when arg-handler
-                          (evil-ex-argument-handler-completer
-                           (cdr arg-handler)))))
-        (when completer
-          (if (eq (car completer) 'collection)
-              (list beg end (cdr completer))
-            (save-restriction
-              (narrow-to-region beg (point-max))
-              (funcall (cdr completer)))))))))
+        (lambda (str1 str2)
+          (let ((p1 (eq (get-text-property 0 'face str1) 'evil-ex-commands))
+                (p2 (eq (get-text-property 0 'face str2) 'evil-ex-commands)))
+            (if (eq p1 p2) (string< str1 str2) p1)))))
+
+(defalias 'evil-ex-argument-completion-at-point #'ignore)
+(make-obsolete
+ 'evil-ex-argument-completion-at-point #'evil-ex-completion-at-point "1.15.0")
 
 (defun evil-ex-define-cmd (cmd function)
   "Bind the function FUNCTION to the command CMD."
-  (if (string-match "^[^][]*\\(\\[\\(.*\\)\\]\\)[^][]*$" cmd)
-      (let ((abbrev (replace-match "" nil t cmd 1))
-            (full (replace-match "\\2" nil nil cmd 1)))
+  (if (string-match "\\[\\(.*\\)\\]" cmd)
+      (let ((abbrev (replace-match "" nil t cmd))
+            (full (replace-match "\\1" nil nil cmd)))
         (evil--add-to-alist evil-ex-commands
                             full function
                             abbrev full))
     (evil--add-to-alist evil-ex-commands cmd function)))
 
-(defun evil-ex-make-argument-handler (runner completer)
+(defsubst evil-ex-make-argument-handler (runner completer)
   (list runner completer))
 
 (defun evil-ex-argument-handler-runner (arg-handler)
@@ -674,8 +595,8 @@ in case of incomplete or unknown commands."
 
 (defmacro evil-ex-define-argument-type (arg-type doc &rest body)
   "Define a new handler for argument-type ARG-TYPE.
-DOC is the documentation string. It is followed by a list of
-keywords and function:
+DOC is the documentation string. It is followed by a list of keywords
+and function:
 
 :collection COLLECTION
 
@@ -683,25 +604,23 @@ keywords and function:
 
 :completion-at-point FUNC
 
-  Function to be called to initialize a potential
-  completion. FUNC must match the requirements as described for
-  the variable `completion-at-point-functions'. When FUNC is
-  called the minibuffer content is narrowed to exactly match the
-  argument.
+  Function to be called to initialize a potential completion. FUNC
+  must match the requirements as described for the variable
+  `completion-at-point-functions'. When FUNC is called the minibuffer
+  content is narrowed to exactly match the argument.
 
 :runner FUNC
 
-  Function to be called when the type of the current argument
-  changes or when the content of this argument changes. This
-  function should take one obligatory argument FLAG followed by
-  an optional argument ARG. FLAG is one of three symbol 'start,
-  'stop or 'update. When the argument type is recognized for the
-  first time and this handler is started the FLAG is 'start. If
-  the argument type changes to something else or ex state
-  finished the handler FLAG is 'stop. If the content of the
-  argument has changed FLAG is 'update. If FLAG is either 'start
-  or 'update then ARG is the current value of this argument. If
-  FLAG is 'stop then arg is nil."
+  Function to be called when the type of the current argument changes
+  or when the content of this argument changes. This function should
+  take one obligatory argument FLAG followed by an optional argument
+  ARG. FLAG is one of three symbol `start', `stop' or `update'. When
+  the argument type is recognized for the first time and this handler
+  is started the FLAG is `start'. If the argument type changes to
+  something else or ex state finished the handler FLAG is `stop'. If
+  the content of the argument has changed FLAG is `update'. If FLAG is
+  either `start' or `update' then ARG is the current value of this
+  argument. If FLAG is `stop' then arg is nil."
   (declare (indent defun)
            (doc-string 2)
            (debug (&define name
@@ -709,7 +628,7 @@ keywords and function:
                            [&rest [keywordp function-form]])))
   (unless (stringp doc) (push doc body))
   (let (runner completer)
-    (while (keywordp (car-safe body))
+    (while (keywordp (car body))
       (let ((key (pop body))
             (func (pop body)))
         (cond
@@ -719,9 +638,8 @@ keywords and function:
           (setq completer (cons 'collection func)))
          ((eq key :completion-at-point)
           (setq completer (cons 'completion-at-point func))))))
-    `(eval-and-compile
-       (evil--add-to-alist evil-ex-argument-types
-                           ',arg-type '(,runner ,completer)))))
+    `(evil--add-to-alist evil-ex-argument-types
+                         ',arg-type '(,runner ,completer))))
 
 (evil-ex-define-argument-type file
   "Handle a file argument."
@@ -734,23 +652,23 @@ keywords and function:
 (declare-function comint-completion-at-point "comint")
 (declare-function shell-completion-vars "shell" ())
 
+(defvar-local evil--ex-shell-argument-initialized nil
+  "This variable is set to t if shell command completion has been initialized.
+See `evil-ex-init-shell-argument-completion'.")
+
 (defun evil-ex-init-shell-argument-completion (flag &optional _arg)
   "Prepare the current minibuffer for completion of shell commands.
 This function must be called from the :runner function of some
 argument handler that requires shell completion."
   (when (and (eq flag 'start)
-             (not evil-ex-shell-argument-initialized))
-    (set (make-local-variable 'evil-ex-shell-argument-initialized) t)
+             (not evil--ex-shell-argument-initialized))
+    (setq evil--ex-shell-argument-initialized t)
     (require 'shell)
     ;; Set up Comint for Shell mode, except
     ;; `comint-completion-at-point' will be called manually.
     (let (completion-at-point-functions)
       (shell-completion-vars))))
 
-(define-obsolete-function-alias
-  'evil-ex-shell-command-completion-at-point
-  'comint-completion-at-point "1.2.13")
-
 (evil-ex-define-argument-type shell
   "Shell argument type, supports completion."
   :completion-at-point comint-completion-at-point
@@ -773,16 +691,14 @@ works accordingly."
 
 (defun evil-ex-binding (command &optional noerror)
   "Return the final binding of COMMAND."
-  (string-match "^\\(.+?\\)\\!?$" command)
-  (let ((binding (match-string 1 command)))
+  (let ((binding (if (evil--ex-bang-p command) (substring command 0 -1)
+                   command)))
     (while (stringp
             (setq binding (cdr (assoc binding evil-ex-commands)))))
-    (unless binding
-      (setq binding (intern-soft command)))
+    (unless binding (setq binding (intern-soft command)))
     (if (commandp binding)
         (or (command-remapping binding) binding)
-      (unless noerror
-        (user-error "Unknown command: `%s'" command)))))
+      (unless noerror (user-error "Unknown command: `%s'" command)))))
 
 (defun evil-ex-completed-binding (command &optional noerror)
   "Return the final binding of the completion of COMMAND."
@@ -791,7 +707,7 @@ works accordingly."
                        (or completion command))
                      noerror)))
 
-;;; TODO: extensions likes :p :~ <cfile> ...
+;; TODO: extensions like :p :~ <cfile> ...
 (defun evil-ex-replace-special-filenames (file-name)
   "Replace special symbols in FILE-NAME.
 Replaces % by the current file-name,
@@ -833,68 +749,46 @@ This function interprets special file names like # and %."
   (when count
     (goto-char (point-min))
     (forward-line (1- count)))
-  (let ((evil-ex-current-buffer (current-buffer))
-        (hist evil-ex-history))
-    (while hist
-      (let ((evil-ex-last-cmd (pop hist)))
-        (when evil-ex-last-cmd
-          (evil-ex-update nil nil nil evil-ex-last-cmd)
-          (let ((binding (evil-ex-binding evil-ex-cmd)))
-            (unless (eq binding #'evil-ex-repeat)
-              (setq hist nil)
-              (eval (or evil-ex-expression
-                        (user-error "Ex: syntax error"))))))))))
+  (cl-loop
+   with evil-ex-current-buffer = (current-buffer)
+   for last-cmd in evil-ex-history do
+   (evil--ex-update nil nil nil last-cmd)
+   (let ((expr (or evil--ex-expression (signal 'evil-ex-error last-cmd))))
+     (unless (eq (evil-ex-binding evil--ex-cmd) #'evil-ex-repeat)
+       (cl-return (eval expr t))))))
 
 (defun evil-ex-call-command (range command argument)
   "Execute the given command COMMAND."
-  (let* ((count (when (numberp range) range))
-         (range (when (evil-range-p range) range))
-         (bang (when (string-match-p ".!$" command) t))
-         (evil-ex-point (point))
-         (evil-ex-range
-          (or range (and count (evil-ex-range count count))))
-         (evil-ex-command (evil-ex-completed-binding command))
-         (restore-point (when (evil-get-command-property evil-ex-command 
:restore-point)
-                          (if (evil-visual-state-p)
-                              (min (point) (or (mark) most-positive-fixnum))
-                            (point))))
-         (evil-ex-bang bang)
-         (evil-ex-argument (copy-sequence argument))
+  (let* ((count (when (integerp range) range))
+         (evil-ex-range (if count (evil-ex-range count count) range))
+         (cmd (evil-ex-completed-binding command))
+         (evil-ex-bang (evil--ex-bang-p command))
+         (evil-ex-argument argument)
          (evil-this-type (evil-type evil-ex-range))
-         (current-prefix-arg count)
-         (prefix-arg current-prefix-arg))
-    (when (stringp evil-ex-argument)
-      (set-text-properties
-       0 (length evil-ex-argument) nil evil-ex-argument))
+         (evil-ex-point (point))
+         (restore-point
+          (and evil-ex-range
+               (evil-get-command-property cmd :restore-point)
+               (if (evil-visual-state-p) evil-visual-beginning evil-ex-point)))
+         (current-prefix-arg count))
     (when evil-ex-reverse-range
       (setq evil-ex-reverse-range nil)
       (unless (y-or-n-p "Backward range given, OK to swap? ")
         (user-error "")))
-    (let ((buf (current-buffer)))
-      (unwind-protect
-          (cond
-           ((not evil-ex-range)
-            (setq this-command evil-ex-command)
-            (evil-exit-visual-state)
-            (run-hooks 'pre-command-hook)
-            (call-interactively evil-ex-command)
-            (run-hooks 'post-command-hook))
-           (t
-            ;; set visual selection to match the region if an explicit
-            ;; range has been specified
-            (cl-destructuring-bind (beg end &rest)
-                (evil-expand-range evil-ex-range t)
-              (setq this-command evil-ex-command)
-              (run-hooks 'pre-command-hook)
-              (set-mark end)
-              (goto-char beg)
-              (activate-mark)
-              (call-interactively evil-ex-command)
-              (run-hooks 'post-command-hook)
-              (when restore-point (goto-char restore-point)))))
-        (when (buffer-live-p buf)
-          (with-current-buffer buf
-            (deactivate-mark)))))))
+
+    (evil-exit-visual-state)
+    (deactivate-mark)
+    (when evil-ex-range
+      ;; Set region if an explicit range has been specified
+      (cl-destructuring-bind (beg end &rest) (evil-expand-range evil-ex-range 
t)
+        (goto-char beg)
+        (set-marker (mark-marker) end)))
+    (setq this-command cmd)
+    (let ((mark-active evil-ex-range))
+      (run-hooks 'pre-command-hook)
+      (call-interactively cmd))
+    (run-hooks 'post-command-hook)
+    (when restore-point (goto-char restore-point))))
 
 (defun evil-ex-line (base &optional offset)
   "Return the line number of BASE plus OFFSET."
@@ -919,14 +813,14 @@ This function interprets special file names like # and %."
 
 (defun evil-ex-range (beg-line &optional end-line)
   "Return the first and last position of the current range."
-  (when (and end-line (< end-line beg-line))
-    (setq evil-ex-reverse-range t)
-    (evil-swap beg-line end-line))
-  (evil-range
-   (evil-line-position beg-line)
-   (evil-line-position (or end-line beg-line) -1)
-   'line
-   :expanded t))
+  (let* ((beg (if beg-line (evil-line-position beg-line)
+                (line-beginning-position)))
+         (end (if end-line (evil-line-position (1+ end-line))
+                (save-excursion (goto-char beg) (line-beginning-position 2)))))
+    (when (< end beg)
+      (setq evil-ex-reverse-range t)
+      (evil-swap beg end))
+    (evil-range beg end 'line :expanded t)))
 
 (defun evil-ex-full-range ()
   "Return a range encompassing the whole buffer."
@@ -948,13 +842,13 @@ Signal an error if MARKER is in a different buffer."
 (defun evil-ex-char-marker-range (beg end)
   (setq beg (evil-get-marker (if (stringp beg) (aref beg 0) beg))
         end (evil-get-marker (if (stringp end) (aref end 0) end)))
-  (if (and (numberp beg) (numberp end))
-      (evil-expand-range
-       (evil-range beg end
-                   (if (evil-visual-state-p)
-                       (evil-visual-type)
-                     'inclusive)))
-    (user-error "Ex does not support markers in other files")))
+  (unless (and (numberp beg) (numberp end))
+    (user-error "Ex does not support markers in other files"))
+  (evil-expand-range
+   (evil-range beg end
+               (if (evil-visual-state-p)
+                   (evil-visual-type)
+                 'inclusive))))
 
 (declare-function evil-ex-make-search-pattern "evil-search")
 
@@ -963,17 +857,17 @@ Signal an error if MARKER is in a different buffer."
 Return the line number of the match."
   (when evil-ex-search-vim-style-regexp
     (setq pattern (evil-transform-vim-style-regexp pattern)))
+  (set-text-properties 0 (length pattern) nil pattern)
   (setq evil-ex-search-pattern (evil-ex-make-search-pattern pattern)
         evil-ex-search-direction 'forward)
   (condition-case err
       (save-excursion
-        (set-text-properties 0 (length pattern) nil pattern)
-        (evil-move-end-of-line)
+        (evil-move-beginning-of-line 2)
         (when (or (re-search-forward pattern nil t)
                   (progn
                     (goto-char (point-min))
                     (re-search-forward pattern nil t)))
-          (line-number-at-pos (1- (match-end 0)))))
+          (line-number-at-pos (match-beginning 0))))
     (invalid-regexp
      (evil-ex-echo (cadr err))
      nil)))
@@ -983,11 +877,11 @@ Return the line number of the match."
 Return the line number of the match."
   (when evil-ex-search-vim-style-regexp
     (setq pattern (evil-transform-vim-style-regexp pattern)))
+  (set-text-properties 0 (length pattern) nil pattern)
   (setq evil-ex-search-pattern (evil-ex-make-search-pattern pattern)
         evil-ex-search-direction 'backward)
   (condition-case err
       (save-excursion
-        (set-text-properties 0 (length pattern) nil pattern)
         (evil-move-beginning-of-line)
         (when (or (re-search-backward pattern nil t)
                   (progn
@@ -1006,52 +900,38 @@ Return the line number of the match."
 NUMBER defaults to 1."
   (funcall sign (or number 1)))
 
-;; function `evil-ex-eval' has been superseded by `evil-ex-parse' plus `eval'
-(make-obsolete 'evil-ex-eval 'evil-ex-parse "1.2.14")
-
-(defun evil-ex-parse (string &optional syntax start)
-  "Parse STRING as an Ex expression and return an evaluation tree.
-If SYNTAX is non-nil, return a syntax tree instead.
-START is the start symbol, which defaults to `expression'."
-  (let ((result (funcall
-                 (evil-parser evil-ex-grammar expression range)
-                 string (or start 'expression) syntax)))
-    (and result
-         ;; Disallow incomplete matches (ignore trailing WS)
-         (not (string-match-p "[^ \f\t\n\r\v]" string (cdr result)))
-         (car result))))
-
 (defun evil-ex-command-force-p (command)
   "Whether COMMAND accepts the bang argument."
+  (declare (obsolete evil-get-command-property "1.15.0"))
   (let ((binding (evil-ex-completed-binding command t)))
     (when binding
       (evil-get-command-property binding :ex-bang))))
 
-(defun evil-ex-syntactic-context (&optional pos)
-  "Return the syntactical context of the character at POS.
+(cl-defun evil--ex-syntactic-context
+    (&optional (pos (point))
+               (tree (save-excursion (goto-char (minibuffer-prompt-end))
+                                     (evil-ex-parse nil t)))
+               &aux i result)
+  "Return the syntactical context in TREE of the character at POS.
 POS defaults to the current position of point."
-  (setq pos (max (- (or pos (point)) (minibuffer-prompt-end)) 0))
-  (let* ((tree (evil-ex-parse (minibuffer-contents-no-properties) t))
-         (i 0) j result)
-    ;; Iterate over syntax tree leaves (i.e. the strings), and return
-    ;; the path to the leaf containing the cursor. Or, if not found,
-    ;; e.g. because of trailing whitespace, the last leaf allowed to
-    ;; be one past the rightmost non-empty string.
-    (cl-labels
-        ((traverse
-          (tree path)
-          (when (symbolp (car tree)) (setq path (cons (pop tree) path)))
-          (dolist (child tree)
-            (if (not (stringp child))
-                (traverse child path)
-              (setq i (+ i (length child)))
-              (when (cond ((>= i pos) (throw 'done path))
-                          ((null result) (setq j i))
-                          ((>= i j) (setq j (1+ j))))
-                (setq result path))))))
-      (catch 'done
-        (traverse tree nil)
-        result))))
+  ;; Iterate over syntax tree leaves (i.e. the strings), and return
+  ;; the path to the leaf containing the cursor. Or, if not found,
+  ;; e.g. because of trailing whitespace, the leaf at most one char
+  ;; past the rightmost non-empty string.
+  (cl-labels
+      ((traverse
+        (node path)
+        (while (and (consp node) (symbolp (car node)))
+          (push (cons (pop node) i) path))
+        (if (listp node)
+            (dolist (child node) (traverse child path))
+          ;; NODE is the end position of a parsed string
+          (when (>= node pos) (cl-return-from evil--ex-syntactic-context path))
+          (when (or (null result) (> node i))
+            (setq i node
+                  result path)))))
+    (traverse tree ()))
+  result)
 
 (provide 'evil-ex)
 
diff --git a/evil-maps.el b/evil-maps.el
index 81b2790b48..678ebfd80d 100644
--- a/evil-maps.el
+++ b/evil-maps.el
@@ -609,17 +609,16 @@ included in `evil-insert-state-bindings' by default."
 
 ;; ex command line
 (define-key evil-ex-completion-map "\d" #'evil-ex-delete-backward-char)
-(define-key evil-ex-completion-map "\t" #'evil-ex-completion)
-(define-key evil-ex-completion-map [tab] #'evil-ex-completion)
-(define-key evil-ex-completion-map [remap completion-at-point] 
#'evil-ex-completion)
-(define-key evil-ex-completion-map "\C-a" 'evil-ex-completion)
+(define-key evil-ex-completion-map "\t" 'completion-at-point)
+(define-key evil-ex-completion-map [tab] 'completion-at-point)
+(define-key evil-ex-completion-map "\C-a" 'completion-at-point)
 (define-key evil-ex-completion-map "\C-b" 'move-beginning-of-line)
 (define-key evil-ex-completion-map "\C-c" 'abort-recursive-edit)
-(define-key evil-ex-completion-map "\C-d" 'evil-ex-completion)
+(define-key evil-ex-completion-map "\C-d" 'completion-at-point)
 (define-key evil-ex-completion-map "\C-f" 'evil-ex-command-window)
 (define-key evil-ex-completion-map "\C-g" 'abort-recursive-edit)
 (define-key evil-ex-completion-map "\C-k" 'evil-insert-digraph)
-(define-key evil-ex-completion-map "\C-l" 'evil-ex-completion)
+(define-key evil-ex-completion-map "\C-l" 'completion-at-point)
 (define-key evil-ex-completion-map "\C-p" #'previous-complete-history-element)
 (define-key evil-ex-completion-map "\C-r" 'evil-paste-from-register)
 (define-key evil-ex-completion-map "\C-n" #'next-complete-history-element)
diff --git a/evil-tests.el b/evil-tests.el
index a6c76bce10..9d8e42e170 100644
--- a/evil-tests.el
+++ b/evil-tests.el
@@ -7513,12 +7513,12 @@ golf h[o]>tel")))
                                   (string-to-number "1"))))))
                      (save-excursion
                        (and l1 (string= "," ";") (goto-line l1))
-                       (evil-ex-range (or l1 (evil-ex-current-line))
-                                      (evil-ex-line
-                                       nil
-                                       (+ (evil-ex-signed-number
-                                           (intern "+")
-                                           (string-to-number "2")))))))
+                       (evil-ex-range
+                        l1 (evil-ex-line
+                            nil
+                            (+ (evil-ex-signed-number
+                                (intern "+")
+                                (string-to-number "2")))))))
                    "t"
                    "-1"))))
 
@@ -7531,8 +7531,7 @@ golf h[o]>tel")))
         `(let ((l1 ,(when a `(evil-ex-line ,@a))))
            (save-excursion
              (and l1 (string= ,(if relative ";" ",") ";") (goto-line l1))
-             (evil-ex-range (or l1 (evil-ex-current-line))
-                            ,(when b `(evil-ex-line ,@b)))))))
+             (evil-ex-range l1 ,(when b `(evil-ex-line ,@b)))))))
     (should (equal (evil-ex-parse "%" nil 'range)
                    '(evil-ex-full-range)))
     (should (equal (evil-ex-parse "*" nil 'range)
@@ -8974,44 +8973,51 @@ parameter set."
 
 (ert-deftest evil-test-parser ()
   "Test `evil-parser'"
-  (cl-flet ((parse
-             (evil-parser
-              '((number "[0-9]+" #'string-to-number)
-                (plus "\\+" #'intern)
-                (minus "-" #'intern)
-                (operator
-                 plus
-                 minus)
-                (sign (\? operator))
-                (signed-number (sign number))
-                (expr
-                 ("foo" (& "bar"))
-                 ("xxx" (! "yyy"))
-                 (number operator number))
-                (epsilon nil))
-              expr operator plus signed-number number epsilon)))
-    (ert-info ("Nothing")
-      (should (equal (parse "1+2" 'epsilon) '(nil . 0))))
-    (ert-info ("Symbols")
-      (should (equal (parse "+" 'plus) '((intern "+") . 1)))
-      (should (equal (parse "+" 'operator) '((intern "+") . 1))))
-    (ert-info ("Leading whitespace")
-      (should (equal (parse " 1" 'number) '((string-to-number "1") . 2))))
-    (ert-info ("Syntax tree")
-      (should (equal (parse "1" 'signed-number t)
-                     '((signed-number (sign "") (number "1")) . 1))))
-    (ert-info ("Lookahead")
-      (should (equal (parse "foobar" 'expr) '((list "foo") . 3)))
-      (should (equal (parse "foobaz" 'expr) nil))
-      (should (equal (parse "xxxyyy" 'expr) nil))
-      (should (equal (parse "xxxzzz" 'expr) '((list "xxx") . 3))))
-    (ert-info ("Semantic actions")
-      (should (equal (parse "1+1" 'expr)
-                     '((list
-                        (string-to-number "1")
-                        (intern "+")
-                        (string-to-number "1"))
-                       . 3))))))
+  (with-temp-buffer
+    (cl-flet
+        ((parse
+          (s &rest args)
+          (erase-buffer)
+          (insert s)
+          (goto-char (point-min))
+          (apply
+           (evil-parser
+            '((number "[0-9]+" #'string-to-number)
+              (plus "\\+" #'intern)
+              (minus "-" #'intern)
+              (operator
+               plus
+               minus)
+              (sign (\? operator))
+              (signed-number (sign number))
+              (expr
+               ("foo" (& "bar"))
+               ("xxx" (! "yyy"))
+               (number operator number))
+              (epsilon nil))
+            expr operator plus signed-number number epsilon)
+           args)))
+      (ert-info ("Nothing")
+        (should (equal (parse "1+2" 'epsilon) '(nil))))
+      (ert-info ("Symbols")
+        (should (equal (parse "+" 'plus) '((intern "+"))))
+        (should (equal (parse "+" 'operator) '((intern "+")))))
+      (ert-info ("Leading whitespace")
+        (should (equal (parse " 1" 'number) '((string-to-number "1")))))
+      (ert-info ("Syntax tree")
+        (should (equal (parse "1" 'signed-number t)
+                       '((signed-number (sign . 1) (number . 2))))))
+      (ert-info ("Lookahead")
+        (should (equal (parse "foobar" 'expr) '((list "foo"))))
+        (should (equal (parse "foobaz" 'expr) nil))
+        (should (equal (parse "xxxyyy" 'expr) nil))
+        (should (equal (parse "xxxzzz" 'expr) '((list "xxx")))))
+      (ert-info ("Semantic actions")
+        (should (equal (parse "1+1" 'expr)
+                       '((list
+                          (string-to-number "1")
+                          (intern "+")
+                          (string-to-number "1")))))))))
 
 (ert-deftest evil-test-delimited-arguments ()
   "Test `evil-delimited-arguments'"
diff --git a/evil-vars.el b/evil-vars.el
index 2d8532a165..3b3a3d0c48 100644
--- a/evil-vars.el
+++ b/evil-vars.el
@@ -1900,13 +1900,9 @@ Key sequences bound in this map are immediately 
executed.")
   "Completion keymap for Ex.")
 
 (defvar evil-ex-initial-input nil
-  "Additional initial content of the ex command line.
-This content of this variable is appended to the ex command line
-if ex is started interactively.")
-
-(defvar evil-ex-shell-argument-initialized nil
-  "This variable is set to t if shell command completion has been initialized.
-See `evil-ex-init-shell-argument-completion'.")
+  "Additional initial content of the Ex command line.
+This content of this variable is appended to the Ex command line
+when Ex is started interactively.")
 
 (defvar evil-ex-commands nil
   "Association list of command bindings and functions.")
@@ -1917,23 +1913,8 @@ See `evil-ex-init-shell-argument-completion'.")
 (defvar evil-ex-current-buffer nil
   "The buffer from which Ex was started.")
 
-(defvar evil-ex-expression nil
-  "The evaluation tree.")
-
-(defvar evil-ex-reverse-range nil
-  "Whether the current ex range was entered reversed.")
-
-(defvar evil-ex-command nil
-  "The current Ex command.")
-
-(defvar evil-ex-previous-command nil
-  "The previously executed Ex command.")
-
-(defvar evil-ex-cmd nil
-  "The current Ex command string.")
-
 (defvar evil-ex-point nil
-  "The position of `point' when the ex command has been called.")
+  "The point position when the Ex command was called.")
 
 (defvar evil-ex-range nil
   "The current range of the Ex command.")
@@ -1944,12 +1925,6 @@ See `evil-ex-init-shell-argument-completion'.")
 (defvar evil-ex-argument nil
   "The current argument of the Ex command.")
 
-(defvar evil-ex-argument-handler nil
-  "The argument handler for the current Ex command.")
-
-(defvar evil-ex-argument-types nil
-  "Association list of argument handlers.")
-
 (defvar evil-previous-shell-command nil
   "The last shell command.")
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]