]> code.delx.au - gnu-emacs-elpa/commitdiff
Merge branch 'dgreensp-object-rest-spread'
authorDmitry Gutov <dgutov@yandex.ru>
Sun, 29 May 2016 00:06:57 +0000 (03:06 +0300)
committerDmitry Gutov <dgutov@yandex.ru>
Sun, 29 May 2016 00:06:57 +0000 (03:06 +0300)
Makefile
README.md
js2-mode.el
js2-old-indent.el
tests/externs.el
tests/indent.el
tests/parser.el

index dda51a3a8a0b9b40cda59441edeff9fcc51f43c7..7777a67c3a6c53c71363d53caf36fb554b42c040 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -19,4 +19,5 @@ clean:
 
 test:
        ${EMACS} $(BATCHFLAGS) -L . -l js2-mode.el -l js2-old-indent.el -l tests/parser.el\
-         -l tests/indent.el -l tests/externs.el -f ert-run-tests-batch-and-exit
+         -l tests/indent.el -l tests/externs.el -l tests/json-path.el \
+         -l tests/navigation.el -f ert-run-tests-batch-and-exit
index d005716823b7228a0346f51c091d4e0dd6761191..cbce9c37c376c9499715501f781c5b37c611f7bf 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-About [![Build Status](https://travis-ci.org/mooz/js2-mode.png?branch=master)](https://travis-ci.org/mooz/js2-mode) [![MELPA](https://melpa.org/packages/js2-mode-badge.svg)](https://melpa.org/#/js2-mode)
+About [![Build Status](https://travis-ci.org/mooz/js2-mode.svg?branch=master)](https://travis-ci.org/mooz/js2-mode) [![MELPA](https://melpa.org/packages/js2-mode-badge.svg)](https://melpa.org/#/js2-mode)
 ======
 
 Improved JavaScript editing mode for GNU Emacs ([description here](http://elpa.gnu.org/packages/js2-mode.html)).
index 22442b96f976d3b7608e16e2c2e4d91251598481..4d9bbf8da02e85ea35cb873dcb0bb879874c4951 100644 (file)
@@ -1076,6 +1076,7 @@ Not currently used."
   "Face used to highlight undeclared variable identifiers.")
 
 (defcustom js2-init-hook nil
+  ;; FIXME: We don't really need this anymore.
   "List of functions to be called after `js2-mode' or
 `js2-minor-mode' has initialized all variables, before parsing
 the buffer for the first time."
@@ -1455,7 +1456,7 @@ the correct number of ARGS must be provided."
          "Compilation produced %s syntax errors.")
 
 (js2-msg "msg.var.redecl"
-         "TypeError: redeclaration of var %s.")
+         "Redeclaration of var %s.")
 
 (js2-msg "msg.const.redecl"
          "TypeError: redeclaration of const %s.")
@@ -2551,7 +2552,9 @@ so many of its properties will be nil.
       (js2-print-from-clause from))
      (exports-list
       (js2-print-named-imports exports-list)))
-    (unless (and default (not (js2-assign-node-p default)))
+    (unless (or (and default (not (js2-assign-node-p default)))
+                (and declaration (or (js2-function-node-p declaration)
+                                     (js2-class-node-p declaration))))
       (insert ";\n"))))
 
 (cl-defstruct (js2-while-node
@@ -3477,6 +3480,7 @@ The type field inherited from `js2-node' holds the operator."
                (cons js2-INSTANCEOF "instanceof")
                (cons js2-DELPROP "delete")
                (cons js2-AWAIT "await")
+               (cons js2-VOID "void")
                (cons js2-COMMA ",")
                (cons js2-COLON ":")
                (cons js2-OR "||")
@@ -3578,7 +3582,8 @@ property is added if the operator follows the operand."
       (insert op))
     (if (or (= tt js2-TYPEOF)
             (= tt js2-DELPROP)
-            (= tt js2-AWAIT))
+            (= tt js2-AWAIT)
+            (= tt js2-VOID))
         (insert " "))
     (js2-print-ast (js2-unary-node-operand n) 0)
     (when postfix
@@ -3841,9 +3846,32 @@ You can tell the quote type by looking at the first character."
       (insert ",")))
   (insert "]"))
 
-(cl-defstruct (js2-class-node
+(cl-defstruct (js2-object-node
                (:include js2-node)
                (:constructor nil)
+               (:constructor make-js2-object-node (&key (type js2-OBJECTLIT)
+                                                        (pos js2-ts-cursor)
+                                                        len
+                                                        elems)))
+  "AST node for an object literal expression.
+`elems' is a list of `js2-object-prop-node'."
+  elems)
+
+(put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
+(put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node)
+
+(defun js2-visit-object-node (n v)
+  (dolist (e (js2-object-node-elems n))
+    (js2-visit-ast e v)))
+
+(defun js2-print-object-node (n i)
+  (insert (js2-make-pad i) "{")
+  (js2-print-list (js2-object-node-elems n))
+  (insert "}"))
+
+(cl-defstruct (js2-class-node
+               (:include js2-object-node)
+               (:constructor nil)
                (:constructor make-js2-class-node (&key (type js2-CLASS)
                                                        (pos js2-ts-cursor)
                                                        (form 'CLASS_STATEMENT)
@@ -3855,7 +3883,7 @@ optional `js2-expr-node'"
   form             ; CLASS_{STATEMENT|EXPRESSION}
   name             ; class name (a `js2-node-name', or nil if anonymous)
   extends          ; class heritage (a `js2-expr-node', or nil if none)
-  elems)
+  )
 
 (put 'cl-struct-js2-class-node 'js2-visitor 'js2-visit-class-node)
 (put 'cl-struct-js2-class-node 'js2-printer 'js2-print-class-node)
@@ -3887,29 +3915,6 @@ optional `js2-expr-node'"
         (js2-print-ast elem (1+ i))))
     (insert "\n" pad "}")))
 
-(cl-defstruct (js2-object-node
-               (:include js2-node)
-               (:constructor nil)
-               (:constructor make-js2-object-node (&key (type js2-OBJECTLIT)
-                                                        (pos js2-ts-cursor)
-                                                        len
-                                                        elems)))
-  "AST node for an object literal expression.
-`elems' is a list of `js2-object-prop-node'."
-  elems)
-
-(put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
-(put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node)
-
-(defun js2-visit-object-node (n v)
-  (dolist (e (js2-object-node-elems n))
-    (js2-visit-ast e v)))
-
-(defun js2-print-object-node (n i)
-  (insert (js2-make-pad i) "{")
-  (js2-print-list (js2-object-node-elems n))
-  (insert "}"))
-
 (cl-defstruct (js2-computed-prop-name-node
                (:include js2-node)
                (:constructor nil)
@@ -6902,15 +6907,18 @@ of a simple name.  Called before EXPR has a parent node."
            '("alias"
              "augments"
              "borrows"
+             "callback"
              "bug"
              "base"
              "config"
              "default"
              "define"
              "exception"
+             "func"
              "function"
              "member"
              "memberOf"
+             "method"
              "name"
              "namespace"
              "since"
@@ -6941,6 +6949,7 @@ of a simple name.  Called before EXPR has a parent node."
              "export"
              "fileoverview"
              "final"
+             "func"
              "function"
              "hidden"
              "ignore"
@@ -6949,6 +6958,7 @@ of a simple name.  Called before EXPR has a parent node."
              "inner"
              "interface"
              "license"
+             "method"
              "noalias"
              "noshadow"
              "notypecheck"
@@ -7249,11 +7259,12 @@ are ignored."
                js2-additional-externs)))
 
 (defun js2-get-jslint-globals ()
+  (js2-reparse)
   (cl-loop for node in (js2-ast-root-comments js2-mode-ast)
            when (and (eq 'block (js2-comment-node-format node))
                      (save-excursion
                        (goto-char (js2-node-abs-pos node))
-                       (looking-at "/\\*global ")))
+                       (looking-at "/\\* *global ")))
            append (js2-get-jslint-globals-in
                    (match-end 0)
                    (js2-node-abs-end node))))
@@ -7503,7 +7514,7 @@ For instance, processing a nested scope requires a parent function node."
   (let (result fn parent-qname p elem)
     (dolist (entry js2-imenu-recorder)
       ;; function node goes first
-      (cl-destructuring-bind (current-fn &rest (&whole chain head &rest)) entry
+      (cl-destructuring-bind (current-fn &rest (&whole chain head &rest _)) entry
         ;; Examine head's defining scope:
         ;; Pre-processed chain, or top-level/external, keep as-is.
         (if (or (stringp head) (js2-node-top-level-decl-p head))
@@ -7747,24 +7758,46 @@ string is NAME.  Returns nil and keeps current token otherwise."
     t))
 
 (defun js2-match-async-arrow-function ()
-  (when (and (js2-contextual-kwd-p (js2-current-token) "async")
-             (/= (js2-peek-token) js2-FUNCTION))
-    (js2-record-face 'font-lock-keyword-face)
-    (js2-get-token)
-    t))
+  (and (js2-contextual-kwd-p (js2-current-token) "async")
+       (/= (js2-peek-token) js2-FUNCTION)))
 
-(defun js2-match-await ()
-  (when (and (= tt js2-NAME)
-             (js2-contextual-kwd-p (js2-current-token) "await"))
-    (js2-record-face 'font-lock-keyword-face)
-    (let ((beg (js2-current-token-beg))
-          (end (js2-current-token-end)))
-      (js2-get-token)
-      (unless (and (js2-inside-function)
-                   (js2-function-node-async js2-current-script-or-fn))
-        (js2-report-error "msg.bad.await" nil
-                          beg (- end beg))))
-    t))
+(defsubst js2-inside-function ()
+  (cl-plusp js2-nesting-of-function))
+
+(defsubst js2-inside-async-function ()
+  (and (js2-inside-function)
+       (js2-function-node-async js2-current-script-or-fn)))
+
+(defun js2-parse-await-maybe (tt)
+  "Parse \"await\" as an AwaitExpression, if it is one."
+  (and (= tt js2-NAME)
+       (js2-contextual-kwd-p (js2-current-token) "await")
+       ;; Per the proposal, AwaitExpression consists of "await"
+       ;; followed by a UnaryExpression.  So look ahead for one.
+       (let ((ts-state (make-js2-ts-state))
+             (recorded-identifiers js2-recorded-identifiers)
+             (parsed-errors js2-parsed-errors)
+             (current-token (js2-current-token))
+             (beg (js2-current-token-beg))
+             (end (js2-current-token-end))
+             pn)
+         (js2-get-token)
+         (setq pn (js2-make-unary js2-AWAIT 'js2-parse-unary-expr))
+         (if (= (js2-node-type (js2-unary-node-operand pn)) js2-ERROR)
+             ;; The parse failed, so pretend like nothing happened and restore
+             ;; the previous parsing state.
+             (progn
+               (js2-ts-seek ts-state)
+               (setq js2-recorded-identifiers recorded-identifiers
+                     js2-parsed-errors parsed-errors)
+               ;; And ensure the caller knows about the failure.
+               nil)
+           ;; The parse was successful, so process and return the "await".
+           (js2-record-face 'font-lock-keyword-face current-token)
+           (unless (js2-inside-async-function)
+             (js2-report-error "msg.bad.await" nil
+                               beg (- end beg)))
+           pn))))
 
 (defun js2-get-prop-name-token ()
   (js2-get-token (and (>= js2-language-version 170) 'KEYWORD_IS_NAME)))
@@ -7816,9 +7849,6 @@ Returns t on match, nil if no match."
       (js2-unget-token))
     nil))
 
-(defsubst js2-inside-function ()
-  (cl-plusp js2-nesting-of-function))
-
 (defun js2-set-requires-activation ()
   (if (js2-function-node-p js2-current-script-or-fn)
       (setf (js2-function-node-needs-activation js2-current-script-or-fn) t)))
@@ -8072,12 +8102,17 @@ declared; probably to check them for errors."
         (let ((subexpr (cond
                         ((and (js2-infix-node-p elem)
                               (= js2-ASSIGN (js2-infix-node-type elem)))
+                         ;; Destructuring with default argument.
                          (js2-infix-node-left elem))
                         ((and (js2-infix-node-p elem)
                               (= js2-COLON (js2-infix-node-type elem)))
+                         ;; In regular destructuring {a: aa, b: bb},
+                         ;; the var is on the right.  In abbreviated
+                         ;; destructuring {a, b}, right == left.
                          (js2-infix-node-right elem))
                         ((and (js2-unary-node-p elem)
                               (= js2-TRIPLEDOT (js2-unary-node-type elem)))
+                         ;; Destructuring with spread.
                          (js2-unary-node-operand elem)))))
           (when subexpr
             (push (js2-define-destruct-symbols
@@ -8577,7 +8612,7 @@ imports or a namespace import that follows it.
               (js2-define-symbol
                js2-LET (js2-name-node-name name-node) name-node t))))))
      ((= (js2-peek-token) js2-NAME)
-      (let ((binding (js2-maybe-parse-export-binding)))
+      (let ((binding (js2-maybe-parse-export-binding t)))
         (let ((node-name (js2-export-binding-node-local-name binding)))
           (js2-define-symbol js2-LET (js2-name-node-name node-name) node-name t))
         (setf (js2-import-clause-node-default-binding clause) binding)
@@ -8610,50 +8645,47 @@ imports or a namespace import that follows it.
   "Parse a namespace import expression such as  '* as bar'.
 The current token must be js2-MUL."
   (let ((beg (js2-current-token-beg)))
-    (when (js2-must-match js2-NAME "msg.syntax")
-        (if (equal "as" (js2-current-token-string))
-            (when (js2-must-match-prop-name "msg.syntax")
-              (let ((node (make-js2-namespace-import-node
-                           :pos beg
-                           :len (- (js2-current-token-end) beg)
-                           :name (make-js2-name-node
-                                  :pos (js2-current-token-beg)
-                                  :len (js2-current-token-end)
-                                  :name (js2-current-token-string)))))
-                (js2-node-add-children node (js2-namespace-import-node-name node))
-                node))
-          (js2-unget-token)
-          (js2-report-error "msg.syntax")))))
+    (if (js2-match-contextual-kwd "as")
+        (when (js2-must-match-prop-name "msg.syntax")
+          (let ((node (make-js2-namespace-import-node
+                       :pos beg
+                       :len (- (js2-current-token-end) beg)
+                       :name (make-js2-name-node
+                              :pos (js2-current-token-beg)
+                              :len (js2-current-token-end)
+                              :name (js2-current-token-string)))))
+            (js2-node-add-children node (js2-namespace-import-node-name node))
+            node))
+      (js2-unget-token)
+      (js2-report-error "msg.syntax"))))
 
 
 (defun js2-parse-from-clause ()
   "Parse the from clause in an import or export statement. E.g. from 'src/lib'"
-  (when (js2-must-match-name "msg.mod.from.after.import.spec.set")
-    (let ((beg (js2-current-token-beg)))
-      (if (equal "from" (js2-current-token-string))
-          (cond
-           ((js2-match-token js2-STRING)
-            (make-js2-from-clause-node
-             :pos beg
-             :len (- (js2-current-token-end) beg)
-             :module-id (js2-current-token-string)
-             :metadata-p nil))
-           ((js2-match-token js2-THIS)
-            (when (js2-must-match-name "msg.mod.spec.after.from")
-              (if (equal "module" (js2-current-token-string))
-                  (make-js2-from-clause-node
-                   :pos beg
-                   :len (- (js2-current-token-end) beg)
-                   :module-id "this"
-                   :metadata-p t)
-                (js2-unget-token)
-                (js2-unget-token)
-                (js2-report-error "msg.mod.spec.after.from")
-                nil)))
-           (t (js2-report-error "msg.mod.spec.after.from") nil))
-        (js2-unget-token)
-        (js2-report-error "msg.mod.from.after.import.spec.set")
-        nil))))
+  (if (js2-match-contextual-kwd "from")
+      (let ((beg (js2-current-token-beg)))
+        (cond
+         ((js2-match-token js2-STRING)
+          (make-js2-from-clause-node
+           :pos beg
+           :len (- (js2-current-token-end) beg)
+           :module-id (js2-current-token-string)
+           :metadata-p nil))
+         ((js2-match-token js2-THIS)
+          (when (js2-must-match-name "msg.mod.spec.after.from")
+            (if (equal "module" (js2-current-token-string))
+                (make-js2-from-clause-node
+                 :pos beg
+                 :len (- (js2-current-token-end) beg)
+                 :module-id "this"
+                 :metadata-p t)
+              (js2-unget-token)
+              (js2-unget-token)
+              (js2-report-error "msg.mod.spec.after.from")
+              nil)))
+         (t (js2-report-error "msg.mod.spec.after.from") nil)))
+    (js2-report-error "msg.mod.from.after.import.spec.set")
+    nil))
 
 (defun js2-parse-export-bindings (&optional import-p)
   "Parse a list of export binding expressions such as {}, {foo, bar}, and
@@ -8661,7 +8693,7 @@ The current token must be js2-MUL."
 js2-LC. Return a lisp list of js2-export-binding-node"
   (let ((bindings (list)))
     (while
-        (let ((binding (js2-maybe-parse-export-binding)))
+        (let ((binding (js2-maybe-parse-export-binding import-p)))
           (when binding
             (push binding bindings))
           (js2-match-token js2-COMMA)))
@@ -8670,7 +8702,7 @@ js2-LC. Return a lisp list of js2-export-binding-node"
                                    "msg.mod.rc.after.export.spec.list"))
       (reverse bindings))))
 
-(defun js2-maybe-parse-export-binding ()
+(defun js2-maybe-parse-export-binding (&optional import-p)
   "Attempt to parse a binding expression found inside an import/export statement.
 This can take the form of either as single js2-NAME token as in 'foo' or as in a
 rebinding expression 'bar as foo'. If it matches, it will return an instance of
@@ -8682,45 +8714,49 @@ consumes no tokens."
         (is-reserved-name (or (= (js2-current-token-type) js2-RESERVED)
                               (aref js2-kwd-tokens (js2-current-token-type)))))
     (if extern-name
-        (let ((as (and (js2-match-token js2-NAME) (js2-current-token-string))))
-          (if (and as (equal "as" (js2-current-token-string)))
-              (let ((name
-                     (or
-                      (and (js2-match-token js2-DEFAULT) "default")
-                      (and (js2-match-token js2-NAME) (js2-current-token-string)))))
-                (if name
-                    (let ((node (make-js2-export-binding-node
-                                 :pos beg
-                                 :len (- (js2-current-token-end) beg)
-                                 :local-name (make-js2-name-node
-                                              :name name
-                                              :pos (js2-current-token-beg)
-                                              :len (js2-current-token-len))
-                                 :extern-name (make-js2-name-node
-                                               :name extern-name
-                                               :pos beg
-                                               :len extern-name-len))))
-                      (js2-node-add-children
-                       node
-                       (js2-export-binding-node-local-name node)
-                       (js2-export-binding-node-extern-name node))
-                      node)
-                  (js2-unget-token)
-                  nil))
-            (when as (js2-unget-token))
-            (let* ((name-node (make-js2-name-node
-                              :name (js2-current-token-string)
-                              :pos (js2-current-token-beg)
-                              :len (js2-current-token-len)))
-                  (node (make-js2-export-binding-node
-                         :pos (js2-current-token-beg)
-                         :len (js2-current-token-len)
-                         :local-name name-node
-                         :extern-name name-node)))
-              (when is-reserved-name
-                (js2-report-error "msg.mod.as.after.reserved.word" extern-name))
-              (js2-node-add-children node name-node)
-              node)))
+        (if (js2-match-contextual-kwd "as")
+            (let ((name
+                   (or
+                    (and (js2-match-token js2-DEFAULT) "default")
+                    (and (js2-match-token js2-NAME) (js2-current-token-string)))))
+              (if name
+                  (let ((node (make-js2-export-binding-node
+                               :pos beg
+                               :len (- (js2-current-token-end) beg)
+                               :local-name (make-js2-name-node
+                                            :name name
+                                            :pos (js2-current-token-beg)
+                                            :len (js2-current-token-len))
+                               :extern-name (make-js2-name-node
+                                             :name extern-name
+                                             :pos beg
+                                             :len extern-name-len))))
+                    (js2-node-add-children
+                     node
+                     (js2-export-binding-node-local-name node)
+                     (js2-export-binding-node-extern-name node))
+                    (if import-p
+                        (js2-set-face (js2-current-token-beg) (js2-current-token-end)
+                                      'font-lock-variable-name-face 'record))
+                    node)
+                (js2-unget-token)
+                nil))
+          (let* ((name-node (make-js2-name-node
+                             :name (js2-current-token-string)
+                             :pos (js2-current-token-beg)
+                             :len (js2-current-token-len)))
+                 (node (make-js2-export-binding-node
+                        :pos (js2-current-token-beg)
+                        :len (js2-current-token-len)
+                        :local-name name-node
+                        :extern-name name-node)))
+            (when is-reserved-name
+              (js2-report-error "msg.mod.as.after.reserved.word" extern-name))
+            (js2-node-add-children node name-node)
+            (if import-p
+                (js2-set-face (js2-current-token-beg) (js2-current-token-end)
+                              'font-lock-variable-name-face 'record))
+            node))
       nil)))
 
 (defun js2-parse-switch ()
@@ -8848,15 +8884,17 @@ invalid export statements."
       (when exports-list
         (dolist (export exports-list)
           (push export children)))
-      (when (js2-match-token js2-NAME)
-        (if (equal "from" (js2-current-token-string))
-            (progn
-              (js2-unget-token)
-              (setq from-clause (js2-parse-from-clause)))
-          (js2-unget-token))))
+      (when (js2-match-contextual-kwd "from")
+        (js2-unget-token)
+        (setq from-clause (js2-parse-from-clause))))
      ((js2-match-token js2-DEFAULT)
       (setq default (cond ((js2-match-token js2-CLASS)
                            (js2-parse-class-stmt))
+                          ((js2-match-token js2-NAME)
+                           (if (js2-match-async-function)
+                               (js2-parse-async-function-stmt)
+                             (js2-unget-token)
+                             (js2-parse-expr)))
                           ((js2-match-token js2-FUNCTION)
                            (js2-parse-function-stmt))
                           (t (js2-parse-expr)))))
@@ -8864,6 +8902,12 @@ invalid export statements."
       (setq declaration (js2-parse-variables (js2-current-token-type) (js2-current-token-beg))))
      ((js2-match-token js2-CLASS)
       (setq declaration (js2-parse-class-stmt)))
+     ((js2-match-token js2-NAME)
+      (setq declaration
+            (if (js2-match-async-function)
+                (js2-parse-async-function-stmt)
+              (js2-unget-token)
+              (js2-parse-expr))))
      ((js2-match-token js2-FUNCTION)
       (setq declaration (js2-parse-function-stmt)))
      (t
@@ -8923,7 +8967,7 @@ Last matched token must be js2-FOR."
              ((= tt js2-SEMI)
               (js2-unget-token)
               (setq init (make-js2-empty-expr-node)))
-             ((or (= tt js2-VAR) (= tt js2-LET))
+             ((or (= tt js2-VAR) (= tt js2-LET) (= tt js2-CONST))
               (setq init (js2-parse-variables tt (js2-current-token-beg))))
              (t
               (js2-unget-token)
@@ -9708,6 +9752,9 @@ If NODE is non-nil, it is the AST node associated with the symbol."
        ((and (= tt js2-ARROW)
              (>= js2-language-version 200))
         (js2-ts-seek ts-state)
+        (when async-p
+          (js2-record-face 'font-lock-keyword-face)
+          (js2-get-token))
         (setq js2-recorded-identifiers recorded-identifiers
               js2-parsed-errors parsed-errors)
         (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) nil async-p)))
@@ -9899,18 +9946,6 @@ to parse the operand (for prefix operators)."
     (js2-node-add-children pn expr)
     pn))
 
-(defun js2-make-await ()
-  "Make an await node."
-  (let* ((pos (js2-current-token-beg))
-         (expr (js2-parse-unary-expr))
-         (end (js2-node-end expr))
-         pn)
-    (setq pn (make-js2-await-node :pos pos
-                                  :len (- end pos)
-                                  :operand expr))
-    (js2-node-add-children pn expr)
-    pn))
-
 (defconst js2-incrementable-node-types
   (list js2-NAME js2-GETPROP js2-GETELEM js2-GET_REF js2-CALL)
   "Node types that can be the operand of a ++ or -- operator.")
@@ -9952,8 +9987,7 @@ to parse the operand (for prefix operators)."
      ((= tt js2-DELPROP)
       (js2-get-token)
       (js2-make-unary js2-DELPROP 'js2-parse-unary-expr))
-     ((js2-match-await)
-      (js2-make-unary js2-AWAIT 'js2-parse-unary-expr))
+     ((js2-parse-await-maybe tt))
      ((= tt js2-ERROR)
       (js2-get-token)
       (make-js2-error-node))  ; try to continue
@@ -10661,6 +10695,7 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed."
     (js2-set-face (js2-node-pos name) (js2-node-end name)
                   'font-lock-function-name-face 'record)
     (let ((node (js2-parse-class pos 'CLASS_STATEMENT name)))
+      (js2-record-imenu-functions node name)
       (js2-define-symbol js2-FUNCTION
                          (js2-name-node-name name)
                          node)
@@ -10764,7 +10799,7 @@ expression)."
        ;; Found a key/value property (of any sort)
        ((member tt (list js2-NAME js2-STRING js2-NUMBER js2-LB))
         (setq after-comma nil
-              elem (js2-parse-named-prop tt pos previous-token))
+              elem (js2-parse-named-prop tt previous-token))
         (if (and (null elem)
                  (not js2-recover-from-parse-errors))
             (setq continue nil)))
@@ -10823,7 +10858,7 @@ expression)."
     (js2-must-match js2-RC "msg.no.brace.prop")
     (nreverse elems)))
 
-(defun js2-parse-named-prop (tt pos previous-token)
+(defun js2-parse-named-prop (tt previous-token)
   "Parse a name, string, or getter/setter object property.
 When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted."
   (let ((key (js2-parse-prop-name tt))
@@ -10831,8 +10866,10 @@ When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted."
         (property-type (when previous-token
                              (if (= (js2-token-type previous-token) js2-MUL)
                                  "*"
-                               (js2-token-string previous-token)))))
+                               (js2-token-string previous-token))))
+        pos)
     (when (member prop '("get" "set" "async"))
+      (setq pos (js2-token-beg previous-token))
       (js2-set-face (js2-token-beg previous-token)
                     (js2-token-end previous-token)
                     'font-lock-keyword-face 'record))  ; get/set/async
@@ -10964,6 +11001,7 @@ TYPE-STRING is a string `get', `set', `*', or nil, indicating a found keyword."
     (js2-node-set-prop fn 'METHOD_TYPE type)  ; for codegen
     (when (string= type-string "*")
       (setf (js2-function-node-generator-type fn) 'STAR))
+    (unless pos (setq pos (js2-node-pos prop)))
     (setq end (js2-node-end fn)
           result (make-js2-method-node :pos pos
                                        :len (- end pos)
@@ -11008,6 +11046,7 @@ And, if CHECK-ACTIVATION-P is non-nil, use the value of TOKEN."
   "Print the path to the JSON value under point, and save it in the kill ring.
 If HARDCODED-ARRAY-INDEX provided, array index in JSON path is replaced with it."
   (interactive "P")
+  (js2-reparse)
   (let (previous-node current-node
         key-name
         rlt)
@@ -11550,7 +11589,9 @@ Selecting an error will jump it to the corresponding source-buffer error.
 
   (run-hooks 'js2-init-hook)
 
-  (js2-reparse))
+  (let ((js2-idle-timer-delay 0))
+    ;; Schedule parsing for after when the mode hooks run.
+    (js2-mode-reset-timer)))
 
 ;; We may eventually want js2-jsx-mode to derive from js-jsx-mode, but that'd be
 ;; a bit more complicated and it doesn't net us much yet.
@@ -11901,10 +11942,7 @@ PARSE-STATUS is as documented in `parse-partial-sexp'."
             (insert "\n")
             (indent-to col)
             (insert "*/"))))
-     ((and single
-           (save-excursion
-              (and (zerop (forward-line 1))
-                   (looking-at "\\s-*//"))))
+     (single
       (indent-to col)
       (insert "// ")))
     ;; Don't need to extend the comment after all.
@@ -12551,7 +12589,10 @@ it marks the next defun after the ones already marked."
 (defun js2-jump-to-definition (&optional arg)
   "Jump to the definition of an object's property, variable or function."
   (interactive "P")
-  (ring-insert find-tag-marker-ring (point-marker))
+  (if (eval-when-compile (fboundp 'xref-push-marker-stack))
+      (xref-push-marker-stack)
+    (ring-insert find-tag-marker-ring (point-marker)))
+  (js2-reparse)
   (let* ((node (js2-node-at-point))
          (parent (js2-node-parent node))
          (names (if (js2-prop-get-node-p parent)
index 82a00b4a28599b44a76f3d9f17c645b59936f228..f336005e4a75fb576a591bbed727c61a58867fc2 100644 (file)
@@ -131,13 +131,13 @@ switch statement body are indented one additional level."
 followed by an opening brace.")
 
 (defconst js2-indent-operator-re
-  (concat "[-+*/%<>&^|?:.]\\([^-+*/]\\|$\\)\\|!?=\\|"
-          (regexp-opt '("in" "instanceof") 'words))
+  (concat "[-+*/%<>&^|?:.]\\([^-+*/.]\\|$\\)\\|!?=\\|"
+          (regexp-opt '("in" "instanceof") 'symbols))
   "Regular expression matching operators that affect indentation
 of continued expressions.")
 
 (defconst js2-declaration-keyword-re
-  (regexp-opt '("var" "let" "const") 'words)
+  (regexp-opt '("var" "let" "const") 'symbols)
   "Regular expression matching variable declaration keywords.")
 
 (defun js2-re-search-forward-inner (regexp &optional bound count)
@@ -225,31 +225,30 @@ and comments have been removed."
                   (eq (char-after) ??))))
        (not (and
              (eq (char-after) ?*)
-             (looking-at (concat "\\* *" js2-mode-identifier-re " *("))
+             ;; Generator method (possibly using computed property).
+             (looking-at (concat "\\* *\\(?:\\[\\|"
+                                 js2-mode-identifier-re
+                                 " *(\\)"))
              (save-excursion
-               (goto-char (1- (match-end 0)))
-               (let (forward-sexp-function) (forward-sexp))
-               (js2-forward-sws)
-               (eq (char-after) ?{))))))
+               (js2-backward-sws)
+               ;; We might misindent some expressions that would
+               ;; return NaN anyway.  Shouldn't be a problem.
+               (memq (char-before) '(?, ?} ?{)))))))
 
 (defun js2-continued-expression-p ()
   "Return non-nil if the current line continues an expression."
   (save-excursion
     (back-to-indentation)
-    (or (js2-looking-at-operator-p)
-        (when (catch 'found
-                (while (and (re-search-backward "\n" nil t)
-                            (let ((state (syntax-ppss)))
-                              (when (nth 4 state)
-                                (goto-char (nth 8 state))) ;; skip comments
-                              (skip-chars-backward " \t")
-                              (if (bolp)
-                                  t
-                                (throw 'found t))))))
-          (backward-char)
-          (when (js2-looking-at-operator-p)
-            (backward-char)
-            (not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]")))))))
+    (if (js2-looking-at-operator-p)
+        (or (not (memq (char-after) '(?- ?+)))
+            (progn
+              (forward-comment (- (point)))
+              (not (memq (char-before) '(?, ?\[ ?\()))))
+      (forward-comment (- (point)))
+      (or (bobp) (backward-char))
+      (when (js2-looking-at-operator-p)
+        (backward-char)
+        (not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]"))))))
 
 (defun js2-end-of-do-while-loop-p ()
   "Return non-nil if word after point is `while' of a do-while
index 40957f600cf85fa3636fb23573562c0a36b9d274..09a60a9d3f9e89cc817696fecae9415316871c45 100644 (file)
     (should (equal (js2-get-jslint-globals)
                    '("quux" "tee" "$")))))
 
+(ert-deftest js2-finds-jslint-globals-with-space ()
+  (with-temp-buffer
+    (insert "/* global foo, bar:false, baz:true")
+    (js2-mode)
+    (should (equal (js2-get-jslint-globals)
+                   '("foo" "bar" "baz")))))
+
 ;;;TODO
 ;; ensure that any symbols bound with the import syntax are added to the extern list
 ;; ensure that any symbols bound with the export syntax exist in the file scope
index bacb614859f1c8c80775de99041ea12f37d3c30d..2fe60378c8832ba0fb42edcdab0ef8439fc92b8a 100644 (file)
@@ -32,6 +32,7 @@
            s
          (replace-regexp-in-string "^ *" "" s)))
       (js2-jsx-mode)
+      (js2-reparse) ; solely for js2-jsx-self-closing, for some reason
       (indent-region (point-min) (point-max))
       (should (string= s (buffer-substring-no-properties
                           (point-min) (point)))))))
   "class A {
   |  * x() {
   |    return 1
-  |      * 2;
+  |      * a(2);
+  |  }
+  |}")
+
+(js2-deftest-indent indent-generator-computed-method
+  "class A {
+  |  *[Symbol.iterator]() {
+  |    yield 'Foo';
+  |    yield 'Bar';
   |  }
   |}")
 
   |}"
   :bind ((js2-indent-switch-body t)))
 
+(js2-deftest-indent continued-expression-vs-unary-minus
+  "var arr = [
+  |  -1, 2,
+  |  -3, 4 +
+  |    -5
+  |];")
+
 (js2-deftest-indent jsx-one-line
   "var foo = <div></div>;")
 
index ea3584a5b10ebb8b003fb2a9f05325f5bd31d134..9667e566067986b9484abd1dda5480ec919f5f71 100644 (file)
              ,@body)
          (fundamental-mode)))))
 
+(defun js2-mode--and-parse ()
+  (js2-mode)
+  (js2-reparse))
+
 (defun js2-test-string-to-ast (s)
   (insert s)
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (null js2-mode-buffer-dirty-p))
   js2-mode-ast)
 
 (cl-defun js2-test-parse-string (code-string &key syntax-error errors-count
-                                             reference)
+                                             reference warnings-count)
   (ert-with-test-buffer (:name 'origin)
     (let ((ast (js2-test-string-to-ast code-string)))
       (if syntax-error
           (skip-chars-backward " \t\n")
           (should (string= (or reference code-string)
                            (buffer-substring-no-properties
-                            (point-min) (point)))))))))
+                            (point-min) (point)))))
+        (when warnings-count
+          (should (= warnings-count
+                     (length (js2-ast-root-warnings ast)))))))))
 
 (cl-defmacro js2-deftest-parse (name code-string &key bind syntax-error errors-count
-                                     reference)
+                                     reference warnings-count)
   "Parse CODE-STRING.  If SYNTAX-ERROR is nil, print syntax tree
 with `js2-print-tree' and assert the result to be equal to
 REFERENCE, if present, or the original string.  If SYNTAX-ERROR
@@ -73,6 +80,7 @@ the test."
        (js2-test-parse-string ,code-string
                               :syntax-error ,syntax-error
                               :errors-count ,errors-count
+                              :warnings-count ,warnings-count
                               :reference ,reference))))
 
 ;;; Basics
@@ -126,6 +134,9 @@ the test."
 (js2-deftest-parse let-expression-statement
   "let (x = 42) x;")
 
+(js2-deftest-parse void
+  "void 0;")
+
 ;;; Callers of `js2-valid-prop-name-token'
 
 (js2-deftest-parse parse-property-access-when-not-keyword
@@ -158,6 +169,12 @@ the test."
 (js2-deftest-parse parse-for-of
   "for (var a of []) {\n}")
 
+(js2-deftest-parse of-can-be-name
+  "void of;")
+
+(js2-deftest-parse of-can-be-object-name
+  "of.z;")
+
 (js2-deftest-parse of-can-be-var-name
   "var of = 3;")
 
@@ -167,22 +184,27 @@ the test."
 ;;; Destructuring binding
 
 (js2-deftest-parse destruct-in-declaration
-  "var {a, b} = {a: 1, b: 2};")
+  "var {a, b} = {a: 1, b: 2};"
+  :warnings-count 0)
 
 (js2-deftest-parse destruct-in-arguments
-  "function f({a: aa, b: bb}) {\n}")
+  "function f({a: aa, b: bb}) {\n}"
+  :warnings-count 0)
 
 (js2-deftest-parse destruct-in-array-comp-loop
   "[a + b for ([a, b] in [[0, 1], [1, 2]])];")
 
 (js2-deftest-parse destruct-in-catch-clause
-  "try {\n} catch ({a, b}) {\n  a + b;\n}")
+  "try {\n} catch ({a, b}) {\n  a + b;\n}"
+  :warnings-count 0)
 
 (js2-deftest-parse destruct-with-initializer-in-object
-  "var {a, b = 2, c} = {};")
+  "var {a, b = 2, c} = {};\nb;"
+  :warnings-count 0)
 
 (js2-deftest-parse destruct-with-initializer-in-array
-  "var [a, b = 2, c] = [];")
+  "var [a, b = 2, c] = [];\nb;"
+  :warnings-count 0)
 
 (js2-deftest-parse destruct-non-name-target-is-error
   "var {1=1} = {};" :syntax-error "1" :errors-count 1)
@@ -197,12 +219,12 @@ the test."
   "\"use strict\";\nvar {a=1,a=2} = {};" :syntax-error "a" :errors-count 1)
 
 (js2-deftest destruct-name-conflict-is-warning-in-array "\"use strict\";\nvar [a=1,a=2] = [];"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (equal '("msg.var.redecl" "a")
                  (caar js2-parsed-warnings))))
 
 (js2-deftest initializer-outside-destruct-is-error "({a=1});"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (equal "msg.init.no.destruct"
                  (car (caar js2-parsed-errors)))))
 
@@ -218,7 +240,7 @@ the test."
   "var x = {f(y) {  return y;\n}};")
 
 (js2-deftest object-literal-method-own-name-in-scope "({f(){f();}});"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (equal '("msg.undeclared.variable" "f")
                  (caar js2-parsed-warnings))))
 
@@ -246,11 +268,11 @@ the test."
 ;;; Function definition
 
 (js2-deftest function-redeclaring-var "var gen = 3; function gen() {};"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (= (length (js2-ast-root-warnings js2-mode-ast)) 1)))
 
 (js2-deftest function-expression-var-same-name "var gen = function gen() {};"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (null (js2-ast-root-warnings js2-mode-ast))))
 
 ;;; Function parameters
@@ -412,7 +434,7 @@ the test."
 
 (js2-deftest no-label-node-inside-expr "x = y:"
   (let (js2-parse-interruptable-p)
-    (js2-mode))
+    (js2-mode--and-parse))
   (let ((assignment (js2-expr-stmt-node-expr (car (js2-scope-kids js2-mode-ast)))))
     (should (js2-name-node-p (js2-assign-node-right assignment)))))
 
@@ -504,12 +526,24 @@ the test."
 
 ;;; 'async' and 'await' are contextual keywords
 
+(js2-deftest-parse async-can-be-name
+  "void async;")
+
+(js2-deftest-parse async-can-be-object-name
+  "async.z;")
+
 (js2-deftest-parse async-can-be-var-name
   "var async = 3;")
 
 (js2-deftest-parse async-can-be-function-name
   "function async() {\n}")
 
+(js2-deftest-parse await-can-be-name
+  "void await;")
+
+(js2-deftest-parse await-can-be-object-name
+  "await.z;")
+
 (js2-deftest-parse await-can-be-var-name
   "var await = 3;")
 
@@ -801,15 +835,22 @@ the test."
     (should (js2-export-node-default export-node))))
 
 (js2-deftest export-function-no-semicolon "export default function foo() {}"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (equal nil js2-parsed-warnings)))
 (js2-deftest export-default-function-no-semicolon "export function foo() {}"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (equal nil js2-parsed-warnings)))
 (js2-deftest export-anything-else-does-require-a-semicolon "export var obj = {}"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (not (equal nil js2-parsed-warnings))))
 
+(js2-deftest export-default-async-function-no-semicolon "export default async function foo() {}"
+  (js2-mode--and-parse)
+  (should (equal nil js2-parsed-warnings)))
+(js2-deftest export-async-function-no-semicolon "export async function foo() {}"
+  (js2-mode--and-parse)
+  (should (equal nil js2-parsed-warnings)))
+
 (js2-deftest-parse parse-export-rexport "export * from 'other/lib';")
 (js2-deftest-parse parse-export-export-named-list "export {foo, bar as bang};")
 (js2-deftest-parse parse-re-export-named-list "export {foo, bar as bang} from 'other/lib';")
@@ -819,6 +860,18 @@ the test."
 (js2-deftest-parse parse-export-generator-declaration "export default function* one() {\n}")
 (js2-deftest-parse parse-export-assignment-expression "export default a = b;")
 
+(js2-deftest-parse parse-export-function-declaration-no-semi
+  "export function f() {\n}")
+
+(js2-deftest-parse parse-export-class-declaration-no-semi
+  "export class C {\n}")
+
+(js2-deftest-parse parse-export-async-function-allow-await
+  "export async function f() {\n  await f();\n}")
+
+(js2-deftest-parse parse-export-default-async-function-allow-await
+  "export default async function f() {\n  await f();\n}")
+
 ;;; Strings
 
 (js2-deftest-parse string-literal
@@ -884,7 +937,7 @@ the test."
 ;;; Scopes
 
 (js2-deftest ast-symbol-table-includes-fn-node "function foo() {}"
-  (js2-mode)
+  (js2-mode--and-parse)
   (let ((entry (js2-scope-get-symbol js2-mode-ast 'foo)))
     (should (= (js2-symbol-decl-type entry) js2-FUNCTION))
     (should (equal (js2-symbol-name entry) "foo"))
@@ -894,7 +947,7 @@ the test."
   function bar() {}
   var x;
 }"
-  (js2-mode)
+  (js2-mode--and-parse)
   (let* ((scope (js2-node-at-point (point-min)))
          (fn-entry (js2-scope-get-symbol scope 'bar))
          (var-entry (js2-scope-get-symbol scope 'x)))
@@ -912,36 +965,36 @@ the test."
     (should (funcall predicate (js2-get-defining-scope scope variable)))))
 
 (js2-deftest for-node-is-declaration-scope "for (let i = 0; i; ++i) {};"
-  (js2-mode)
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "i" 0 #'js2-for-node-p))
 
 (js2-deftest const-scope-sloppy-script "{const a;} a;"
-  (js2-mode)
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 0 #'js2-script-node-p)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 1 #'js2-script-node-p))
 
 (js2-deftest const-scope-strict-script "'use strict'; { const a; } a;"
-  (js2-mode)
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 0 #'js2-block-node-p)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 1 #'null))
 
 (js2-deftest const-scope-sloppy-function "function f() { { const a; } a; }"
-  (js2-mode)
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 0 #'js2-function-node-p)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 1 #'js2-function-node-p))
 
 (js2-deftest const-scope-strict-function "function f() { 'use strict'; { const a; } a; }"
-  (js2-mode)
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 0 #'js2-block-node-p)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 1 #'null))
 
 (js2-deftest array-comp-is-result-scope "[x * 2 for (x in y)];"
-  (js2-mode)
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "x" 0 #'js2-comp-loop-node-p))
 
 (js2-deftest array-comp-has-parent-scope
              "var a,b=[for (i of [[1,2]]) for (j of i) j * a];"
-  (js2-mode)
+  (js2-mode--and-parse)
   (search-forward "for")
   (forward-char -3)
   (let ((node (js2-node-at-point)))
@@ -1011,23 +1064,23 @@ the test."
 ;;; Error handling
 
 (js2-deftest for-node-with-error-len "for "
-  (js2-mode)
+  (js2-mode--and-parse)
   (let ((node (js2-node-at-point (point-min))))
     (should (= (js2-node-len (js2-node-parent node)) 4))))
 
 (js2-deftest function-without-parens-error "function b {}"
   ;; Should finish the parse.
-  (js2-mode))
+  (js2-mode--and-parse))
 
 ;;; Comments
 
 (js2-deftest comment-node-length "//"
-  (js2-mode)
+  (js2-mode--and-parse)
   (let ((node (js2-node-at-point (point-min))))
     (should (= (js2-node-len node) 2))))
 
 (js2-deftest comment-node-length-newline "//\n"
-  (js2-mode)
+  (js2-mode--and-parse)
   (let ((node (js2-node-at-point (point-min))))
     (should (= (js2-node-len node) 3))))
 
@@ -1066,7 +1119,7 @@ the test."
          (insert ,buffer-contents))
        (unwind-protect
            (progn
-             (js2-mode)
+             (js2-mode--and-parse)
              (should (equal ,summary (js2--variables-summary
                                       (js2--classify-variables)))))
          (fundamental-mode)))))