X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/98433fb95422ffcdc8e9ac41ed4052029490f01c..92f5479d9bc27b5289d352946a76ef585525a22e:/js2-mode.el diff --git a/js2-mode.el b/js2-mode.el index 35660719f..527c639d6 100644 --- a/js2-mode.el +++ b/js2-mode.el @@ -199,7 +199,7 @@ Set `js2-include-rhino-externs' to t to include them.") (mapcar 'symbol-name '(__dirname __filename Buffer clearInterval clearTimeout require console exports global module process setInterval setTimeout - querystring)) + querystring setImmediate clearImmediate)) "Node.js externs. Set `js2-include-node-externs' to t to include them.") @@ -277,6 +277,11 @@ end of the line, otherwise, at the beginning of the next line." :type 'boolean :group 'js2-mode) +(defcustom js2-mode-assume-strict nil + "Non-nil to start files in strict mode automatically." + :type 'boolean + :group 'js2-mode) + (defcustom js2-mode-show-strict-warnings t "Non-nil to emit Ecma strict-mode warnings. Some of the warnings can be individually disabled by other flags, @@ -617,7 +622,9 @@ which doesn't seem particularly useful, but Rhino permits it." (defvar js2-NO_SUBS_TEMPLATE 167) ; template literal without substitutions (defvar js2-TAGGED_TEMPLATE 168) ; tagged template literal -(defconst js2-num-tokens (1+ js2-TAGGED_TEMPLATE)) +(defvar js2-AWAIT 169) ; await (pseudo keyword) + +(defconst js2-num-tokens (1+ js2-AWAIT)) (defconst js2-debug-print-trees nil) @@ -675,6 +682,7 @@ List of chars built up while scanning various tokens.") (string "") number number-base + number-legacy-octal-p regexp-flags comment-type follows-eol-p) @@ -1073,6 +1081,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." @@ -1362,6 +1371,9 @@ the correct number of ARGS must be provided." (js2-msg "msg.yield.parenthesized" "yield expression must be parenthesized.") +(js2-msg "msg.bad.await" + "await must be in async functions.") + ;; NativeGlobal (js2-msg "msg.cant.call.indirect" "Function '%s' must be called directly, and not by way of a " @@ -1449,7 +1461,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.") @@ -2545,7 +2557,10 @@ so many of its properties will be nil. (js2-print-from-clause from)) (exports-list (js2-print-named-imports exports-list))) - (insert ";\n"))) + (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 (:include js2-loop-node) @@ -3256,6 +3271,7 @@ a `js2-label-node' or the innermost enclosing loop.") params rest-p body generator-type + async lp rp))) "AST node for a function declaration. The `params' field is a Lisp list of nodes. Each node is either a simple @@ -3272,6 +3288,7 @@ The `params' field is a Lisp list of nodes. Each node is either a simple ignore-dynamic ; ignore value of the dynamic-scope flag (interpreter only) needs-activation ; t if we need an activation object for this frame generator-type ; STAR, LEGACY, COMPREHENSION or nil + async ; t if the function is defined as `async function` member-expr) ; nonstandard Ecma extension from Rhino (put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node) @@ -3293,8 +3310,10 @@ The `params' field is a Lisp list of nodes. Each node is either a simple (rest-p (js2-function-node-rest-p n)) (body (js2-function-node-body n)) (expr (not (eq (js2-function-node-form n) 'FUNCTION_STATEMENT)))) - (unless (or method arrow) - (insert pad "function") + (unless method + (insert pad) + (when (js2-function-node-async n) (insert "async ")) + (unless arrow (insert "function")) (when (eq (js2-function-node-generator-type n) 'STAR) (insert "*"))) (when name @@ -3465,6 +3484,8 @@ The type field inherited from `js2-node' holds the operator." (cons js2-TYPEOF "typeof") (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 "||") @@ -3545,7 +3566,7 @@ The type field holds the actual assignment operator.") len operand))) "AST node type for unary operator nodes. The type field can be NOT, BITNOT, POS, NEG, INC, DEC, -TYPEOF, DELPROP or TRIPLEDOT. For INC or DEC, a 'postfix node +TYPEOF, DELPROP, TRIPLEDOT or AWAIT. For INC or DEC, a 'postfix node property is added if the operator follows the operand." operand) ; a `js2-node' expression @@ -3565,7 +3586,9 @@ property is added if the operator follows the operand." (unless postfix (insert op)) (if (or (= tt js2-TYPEOF) - (= tt js2-DELPROP)) + (= tt js2-DELPROP) + (= tt js2-AWAIT) + (= tt js2-VOID)) (insert " ")) (js2-print-ast (js2-unary-node-operand n) 0) (when postfix @@ -3701,11 +3724,14 @@ Returns 0 if NODE is nil or its identifier field is nil." (num-value (js2-token-number (js2-current-token))) (num-base (js2-token-number-base - (js2-current-token)))))) + (js2-current-token))) + (legacy-octal-p (js2-token-number-legacy-octal-p + (js2-current-token)))))) "AST node for a number literal." value ; the original string, e.g. "6.02e23" num-value ; the parsed number value - num-base) ; the number's base + num-base ; the number's base + legacy-octal-p) ; whether the number is a legacy octal (0123 instead of 0o123) (put 'cl-struct-js2-number-node 'js2-visitor 'js2-visit-none) (put 'cl-struct-js2-number-node 'js2-printer 'js2-print-number-node) @@ -3825,9 +3851,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) @@ -3839,7 +3888,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) @@ -3871,29 +3920,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) @@ -3947,13 +3973,12 @@ both fields have the same value.") (cl-defstruct (js2-method-node (:include js2-infix-node) (:constructor nil) - (:constructor make-js2-method-node (&key type ; GET, SET, or FUNCTION - (pos js2-ts-cursor) + (:constructor make-js2-method-node (&key (pos js2-ts-cursor) len left right))) "AST node for a method in an object literal or a class body. The `left' field is the `js2-name-node' naming the method. The `right' field is always an anonymous `js2-function-node' with a node -property `METHOD_TYPE' set to js2-GET, js2-SET, or js2-FUNCTION. ") +property `METHOD_TYPE' set to 'GET or 'SET. ") (put 'cl-struct-js2-method-node 'js2-visitor 'js2-visit-infix-node) (put 'cl-struct-js2-method-node 'js2-printer 'js2-print-method) @@ -3961,10 +3986,14 @@ property `METHOD_TYPE' set to js2-GET, js2-SET, or js2-FUNCTION. ") (defun js2-print-method (n i) (let* ((pad (js2-make-pad i)) (left (js2-method-node-left n)) - (right (js2-method-node-right n))) + (right (js2-method-node-right n)) + (type (js2-node-get-prop right 'METHOD_TYPE))) (insert pad) - (if (/= (js2-node-type n) js2-FUNCTION) - (insert (if (= (js2-node-type n) js2-GET) "get " "set "))) + (when type + (insert (cdr (assoc type '((GET . "get ") + (SET . "set ") + (ASYNC . "async ") + (FUNCTION . "")))))) (when (and (js2-function-node-p right) (eq 'STAR (js2-function-node-generator-type right))) (insert "*")) @@ -5130,6 +5159,8 @@ You should use `js2-print-tree' instead of this function." (or (js2-node-has-side-effects expr) (when (js2-string-node-p expr) (member (js2-string-node-value expr) '("use strict" "use asm")))))) + ((= tt js2-AWAIT) + (js2-node-has-side-effects (js2-unary-node-operand node))) ((= tt js2-COMMA) (js2-node-has-side-effects (js2-infix-node-right node))) ((or (= tt js2-AND) @@ -5879,7 +5910,7 @@ its relevant fields and puts it into `js2-ti-tokens'." (let (identifier-start is-unicode-escape-start c contains-escape escape-val str result base - look-for-slash continue tt + look-for-slash continue tt legacy-octal (token (js2-new-token 0))) (setq tt @@ -6001,6 +6032,7 @@ its relevant fields and puts it into `js2-ti-tokens'." ((and (or (eq c ?o) (eq c ?O)) (>= js2-language-version 200)) (setq base 8) + (setq legacy-octal nil) (setq c (js2-get-char))) ((js2-digit-p c) (setq base 'maybe-8)) @@ -6038,7 +6070,8 @@ its relevant fields and puts it into `js2-ti-tokens'." (js2-add-to-string c) (setq c (js2-get-char))) (when (eq base 'maybe-8) - (setq base 8)))) + (setq base 8 + legacy-octal t)))) (when (and (eq base 10) (memq c '(?. ?e ?E))) (when (eq c ?.) (cl-loop do @@ -6060,7 +6093,8 @@ its relevant fields and puts it into `js2-ti-tokens'." (js2-unget-char) (let ((str (js2-set-string-from-buffer token))) (setf (js2-token-number token) (js2-string-to-number str base) - (js2-token-number-base token) base)) + (js2-token-number-base token) base + (js2-token-number-legacy-octal-p token) (and (= base 8) legacy-octal))) (throw 'return js2-NUMBER)) ;; is it a string? (when (or (memq c '(?\" ?\')) @@ -6878,15 +6912,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" @@ -6917,6 +6954,7 @@ of a simple name. Called before EXPR has a parent node." "export" "fileoverview" "final" + "func" "function" "hidden" "ignore" @@ -6925,6 +6963,7 @@ of a simple name. Called before EXPR has a parent node." "inner" "interface" "license" + "method" "noalias" "noshadow" "notypecheck" @@ -7225,11 +7264,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)))) @@ -7421,22 +7461,23 @@ We do a depth-first traversal of NODE. For any functions we find, we append the property name to QNAME, then call `js2-record-imenu-entry'." (let (right) (dolist (e (js2-object-node-elems node)) ; e is a `js2-object-prop-node' - (let ((left (js2-infix-node-left e)) - ;; Element positions are relative to the parent position. - (pos (+ pos (js2-node-pos e)))) - (cond - ;; foo: function() {...} - ((js2-function-node-p (setq right (js2-infix-node-right e))) - (when (js2-prop-node-name left) - ;; As a policy decision, we record the position of the property, - ;; not the position of the `function' keyword, since the property - ;; is effectively the name of the function. - (js2-record-imenu-entry right (append qname (list left)) pos))) - ;; foo: {object-literal} -- add foo to qname, offset position, and recurse - ((js2-object-node-p right) - (js2-record-object-literal right - (append qname (list (js2-infix-node-left e))) - (+ pos (js2-node-pos right))))))))) + (when (js2-infix-node-p e) + (let ((left (js2-infix-node-left e)) + ;; Element positions are relative to the parent position. + (pos (+ pos (js2-node-pos e)))) + (cond + ;; foo: function() {...} + ((js2-function-node-p (setq right (js2-infix-node-right e))) + (when (js2-prop-node-name left) + ;; As a policy decision, we record the position of the property, + ;; not the position of the `function' keyword, since the property + ;; is effectively the name of the function. + (js2-record-imenu-entry right (append qname (list left)) pos))) + ;; foo: {object-literal} -- add foo to qname, offset position, and recurse + ((js2-object-node-p right) + (js2-record-object-literal right + (append qname (list (js2-infix-node-left e))) + (+ pos (js2-node-pos right)))))))))) (defun js2-node-top-level-decl-p (node) "Return t if NODE's name is defined in the top-level scope. @@ -7478,7 +7519,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)) @@ -7702,14 +7743,67 @@ Returns nil and consumes nothing if MATCH is not the next token." (defun js2-match-contextual-kwd (name) "Consume and return t if next token is `js2-NAME', and its string is NAME. Returns nil and keeps current token otherwise." - (if (or (/= (js2-get-token) js2-NAME) - (not (string= (js2-current-token-string) name))) - (progn - (js2-unget-token) - nil) + (if (js2-contextual-kwd-p (progn (js2-get-token) + (js2-current-token)) + name) + (progn (js2-record-face 'font-lock-keyword-face) t) + (js2-unget-token) + nil)) + +(defun js2-contextual-kwd-p (token name) + "Return t if TOKEN is `js2-NAME', and its string is NAME." + (and (= (js2-token-type token) js2-NAME) + (string= (js2-token-string token) name))) + +(defun js2-match-async-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)) +(defun js2-match-async-arrow-function () + (and (js2-contextual-kwd-p (js2-current-token) "async") + (/= (js2-peek-token) js2-FUNCTION))) + +(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))) @@ -7760,9 +7854,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))) @@ -7902,7 +7993,7 @@ Scanner should be initialized." js2-nesting-of-function 0 js2-labeled-stmt nil js2-recorded-identifiers nil ; for js2-highlight - js2-in-use-strict-directive nil) + js2-in-use-strict-directive js2-mode-assume-strict) (while (/= (setq tt (js2-get-token)) js2-EOF) (if (= tt js2-FUNCTION) (progn @@ -8013,18 +8104,34 @@ declared; probably to check them for errors." (list node))) ((js2-object-node-p node) (dolist (elem (js2-object-node-elems node)) - ;; js2-infix-node-p catches both object prop node and initialized - ;; binding element (which is directly an infix node). - (when (js2-infix-node-p elem) - (push (js2-define-destruct-symbols - (js2-infix-node-left elem) - decl-type face ignore-not-in-block) - name-nodes))) + (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 + subexpr decl-type face ignore-not-in-block) + name-nodes)))) (apply #'append (nreverse name-nodes))) ((js2-array-node-p node) (dolist (elem (js2-array-node-elems node)) (when elem - (if (js2-infix-node-p elem) (setq elem (js2-infix-node-left elem))) + (setq elem (cond ((js2-infix-node-p elem) ;; default (=) + (js2-infix-node-left elem)) + ((js2-unary-node-p elem) ;; rest (...) + (js2-unary-node-operand elem)) + (t elem))) (push (js2-define-destruct-symbols elem decl-type face ignore-not-in-block) name-nodes))) @@ -8081,8 +8188,7 @@ represented by FN-NODE at POS." new-param-name-nodes (js2-define-destruct-symbols param js2-LP 'js2-function-param)) (js2-check-strict-function-params param-name-nodes new-param-name-nodes) - (setq param-name-nodes (append param-name-nodes new-param-name-nodes)) - (push param params)) + (setq param-name-nodes (append param-name-nodes new-param-name-nodes))) ;; variable name (t (when (and (>= js2-language-version 200) @@ -8096,22 +8202,23 @@ represented by FN-NODE at POS." (setq param (js2-create-name-node)) (js2-define-symbol js2-LP (js2-current-token-string) param) (js2-check-strict-function-params param-name-nodes (list param)) - (setq param-name-nodes (append param-name-nodes (list param))) - ;; default parameter value - (when (and (>= js2-language-version 200) - (js2-match-token js2-ASSIGN)) - (cl-assert (not paren-free-arrow)) - (let* ((pos (js2-node-pos param)) - (tt (js2-current-token-type)) - (op-pos (- (js2-current-token-beg) pos)) - (left param) - (right (js2-parse-assign-expr)) - (len (- (js2-node-end right) pos))) - (setq param (make-js2-assign-node - :type tt :pos pos :len len :op-pos op-pos - :left left :right right)) - (js2-node-add-children param left right))) - (push param params))) + (setq param-name-nodes (append param-name-nodes (list param))))) + ;; default parameter value + (when (and (not rest-param-at) + (>= js2-language-version 200) + (js2-match-token js2-ASSIGN)) + (cl-assert (not paren-free-arrow)) + (let* ((pos (js2-node-pos param)) + (tt (js2-current-token-type)) + (op-pos (- (js2-current-token-beg) pos)) + (left param) + (right (js2-parse-assign-expr)) + (len (- (js2-node-end right) pos))) + (setq param (make-js2-assign-node + :type tt :pos pos :len len :op-pos op-pos + :left left :right right)) + (js2-node-add-children param left right))) + (push param params) (when (and rest-param-at (> (length params) (1+ rest-param-at))) (js2-report-error "msg.param.after.rest" nil (js2-node-pos param) (js2-node-len param))) @@ -8144,7 +8251,7 @@ Last token scanned is the close-curly for the function body." (js2-name-node-name name) pos end) (js2-add-strict-warning "msg.anon.no.return.value" nil pos end))))) -(defun js2-parse-function-stmt () +(defun js2-parse-function-stmt (&optional async-p) (let ((pos (js2-current-token-beg)) (star-p (js2-match-token js2-MUL))) (js2-must-match-name "msg.unnamed.function.stmt") @@ -8152,28 +8259,31 @@ Last token scanned is the close-curly for the function body." pn member-expr) (cond ((js2-match-token js2-LP) - (js2-parse-function 'FUNCTION_STATEMENT pos star-p name)) + (js2-parse-function 'FUNCTION_STATEMENT pos star-p async-p name)) (js2-allow-member-expr-as-function-name (setq member-expr (js2-parse-member-expr-tail nil name)) (js2-parse-highlight-member-expr-fn-name member-expr) (js2-must-match js2-LP "msg.no.paren.parms") - (setf pn (js2-parse-function 'FUNCTION_STATEMENT pos star-p) + (setf pn (js2-parse-function 'FUNCTION_STATEMENT pos star-p async-p) (js2-function-node-member-expr pn) member-expr) pn) (t (js2-report-error "msg.no.paren.parms") (make-js2-error-node)))))) -(defun js2-parse-function-expr () +(defun js2-parse-async-function-stmt () + (js2-parse-function-stmt t)) + +(defun js2-parse-function-expr (&optional async-p) (let ((pos (js2-current-token-beg)) (star-p (js2-match-token js2-MUL)) name) (when (js2-match-token js2-NAME) (setq name (js2-create-name-node t))) (js2-must-match js2-LP "msg.no.paren.parms") - (js2-parse-function 'FUNCTION_EXPRESSION pos star-p name))) + (js2-parse-function 'FUNCTION_EXPRESSION pos star-p async-p name))) -(defun js2-parse-function-internal (function-type pos star-p &optional name) +(defun js2-parse-function-internal (function-type pos star-p &optional async-p name) (let (fn-node lp) (if (= (js2-current-token-type) js2-LP) ; eventually matched LP? (setq lp (js2-current-token-beg))) @@ -8181,7 +8291,8 @@ Last token scanned is the close-curly for the function body." :name name :form function-type :lp (if lp (- lp pos)) - :generator-type (and star-p 'STAR))) + :generator-type (and star-p 'STAR) + :async async-p)) (when name (js2-set-face (js2-node-pos name) (js2-node-end name) 'font-lock-function-name-face 'record) @@ -8236,7 +8347,7 @@ Last token scanned is the close-curly for the function body." (setf (js2-scope-parent-scope fn-node) js2-current-scope) fn-node)) -(defun js2-parse-function (function-type pos star-p &optional name) +(defun js2-parse-function (function-type pos star-p &optional async-p name) "Function parser. FUNCTION-TYPE is a symbol, POS is the beginning of the first token (function keyword, unless it's an arrow function), NAME is js2-name-node." @@ -8252,7 +8363,7 @@ arrow function), NAME is js2-name-node." (setq ts-state (make-js2-ts-state)) (setq continue (catch 'reparse (setq fn-node (js2-parse-function-internal - function-type pos star-p name)) + function-type pos star-p async-p name)) ;; Don't continue. nil)) (when continue @@ -8362,9 +8473,12 @@ node are given relative start positions and correct lengths." (defun js2-statement-helper () (let* ((tt (js2-get-token)) (first-tt tt) + (async-stmt (js2-match-async-function)) (parser (if (= tt js2-ERROR) #'js2-parse-semi - (aref js2-parsers tt))) + (if async-stmt + #'js2-parse-async-function-stmt + (aref js2-parsers tt)))) pn) ;; If the statement is set, then it's been told its label by now. (and js2-labeled-stmt @@ -8373,7 +8487,8 @@ node are given relative start positions and correct lengths." (setq pn (funcall parser)) ;; Don't do auto semi insertion for certain statement types. (unless (or (memq first-tt js2-no-semi-insertion) - (js2-labeled-stmt-node-p pn)) + (js2-labeled-stmt-node-p pn) + async-stmt) (js2-auto-insert-semicolon pn)) pn)) @@ -8502,7 +8617,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) @@ -8535,50 +8650,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 @@ -8586,7 +8698,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))) @@ -8595,7 +8707,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 @@ -8607,45 +8719,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 () @@ -8773,27 +8889,45 @@ 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 (js2-parse-expr))) + (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))))) ((or (js2-match-token js2-VAR) (js2-match-token js2-CONST) (js2-match-token js2-LET)) (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 (setq declaration (js2-parse-expr)))) (when from-clause (push from-clause children)) (when declaration (push declaration children) - (when (not (js2-function-node-p declaration)) + (when (not (or (js2-function-node-p declaration) + (js2-class-node-p declaration))) (js2-auto-insert-semicolon declaration))) (when default (push default children) - (when (not (js2-function-node-p default)) + (when (not (or (js2-function-node-p default) + (js2-class-node-p default))) (js2-auto-insert-semicolon default))) (let ((node (make-js2-export-node :pos beg @@ -8838,7 +8972,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) @@ -9476,16 +9610,10 @@ If NODE is non-nil, it is the AST node associated with the symbol." (pos (if node (js2-node-abs-pos node))) (len (if node (js2-node-len node)))) (cond - ((and symbol ; already defined - (or (if js2-in-use-strict-directive - ;; two const-bound vars in this block have same name - (and (= sdt js2-CONST) - (eq defining-scope js2-current-scope)) - (or (= sdt js2-CONST) ; old version is const - (= decl-type js2-CONST))) ; new version is const - ;; two let-bound vars in this block have same name - (and (= sdt js2-LET) - (eq defining-scope js2-current-scope)))) + ((and symbol ; already defined in this block + (or (= sdt js2-LET) + (= sdt js2-CONST)) + (eq defining-scope js2-current-scope)) (js2-report-error (cond ((= sdt js2-CONST) "msg.const.redecl") @@ -9495,9 +9623,7 @@ If NODE is non-nil, it is the AST node associated with the symbol." (t "msg.parm.redecl")) name pos len)) ((or (= decl-type js2-LET) - ;; strict mode const is scoped to the current LexicalEnvironment - (and js2-in-use-strict-directive - (= decl-type js2-CONST))) + (= decl-type js2-CONST)) (if (and (= decl-type js2-LET) (not ignore-not-in-block) (or (= (js2-node-type js2-current-scope) js2-IF) @@ -9505,10 +9631,7 @@ If NODE is non-nil, it is the AST node associated with the symbol." (js2-report-error "msg.let.decl.not.in.block") (js2-define-new-symbol decl-type name node))) ((or (= decl-type js2-VAR) - (= decl-type js2-FUNCTION) - ;; sloppy mode const is scoped to the current VariableEnvironment - (and (not js2-in-use-strict-directive) - (= decl-type js2-CONST))) + (= decl-type js2-FUNCTION)) (if symbol (if (and js2-strict-var-redeclaration-warning (= sdt js2-VAR)) (js2-add-strict-warning "msg.var.redecl" name) @@ -9576,9 +9699,20 @@ If NODE is non-nil, it is the AST node associated with the symbol." (let ((tt (js2-get-token)) (pos (js2-current-token-beg)) pn left right op-pos - ts-state recorded-identifiers parsed-errors) + ts-state recorded-identifiers parsed-errors + async-p) (if (= tt js2-YIELD) (js2-parse-return-or-yield tt t) + ;; TODO(mooz): Bit confusing. + ;; If we meet `async` token and it's not part of `async + ;; function`, then this `async` is for a succeeding async arrow + ;; function. + ;; Since arrow function parsing doesn't rely on neither + ;; `js2-parse-function-stmt' nor `js2-parse-function-expr' that + ;; interpret `async` token, we trash `async` and just remember + ;; we met `async` keyword to `async-p'. + (when (js2-match-async-arrow-function) + (setq async-p t)) ;; Save the tokenizer state in case we find an arrow function ;; and have to rewind. (setq ts-state (make-js2-ts-state) @@ -9612,9 +9746,12 @@ 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))) + (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) nil async-p))) (t (js2-unget-token))) pn))) @@ -9844,6 +9981,7 @@ to parse the operand (for prefix operators)." ((= tt js2-DELPROP) (js2-get-token) (js2-make-unary js2-DELPROP 'js2-parse-unary-expr)) + ((js2-parse-await-maybe tt)) ((= tt js2-ERROR) (js2-get-token) (make-js2-error-node)) ; try to continue @@ -10233,6 +10371,8 @@ array-literals, array comprehensions and regular expressions." (js2-parse-class-expr)) ((= tt js2-FUNCTION) (js2-parse-function-expr)) + ((js2-match-async-function) + (js2-parse-function-expr t)) ((= tt js2-LB) (js2-parse-array-comp-or-literal)) ((= tt js2-LC) @@ -10249,7 +10389,8 @@ array-literals, array comprehensions and regular expressions." ((= tt js2-NUMBER) (setq node (make-js2-number-node)) (when (and js2-in-use-strict-directive - (= (js2-number-node-num-base node) 8)) + (= (js2-number-node-num-base node) 8) + (js2-number-node-legacy-octal-p node)) (js2-report-error "msg.no.octal.strict")) node) ((or (= tt js2-STRING) (= tt js2-NO_SUBS_TEMPLATE)) @@ -10348,19 +10489,13 @@ array-literals, array comprehensions and regular expressions." (defun js2-parse-array-literal (pos) (let ((after-lb-or-comma t) - after-comma tt elems pn + after-comma tt elems pn was-rest (continue t)) (unless js2-is-in-destructuring (js2-push-scope (make-js2-scope))) ; for the legacy array comp (while continue (setq tt (js2-get-token)) (cond - ;; comma - ((= tt js2-COMMA) - (setq after-comma (js2-current-token-end)) - (if (not after-lb-or-comma) - (setq after-lb-or-comma t) - (push nil elems))) ;; end of array ((or (= tt js2-RB) (= tt js2-EOF)) ; prevent infinite loop @@ -10374,27 +10509,18 @@ array-literals, array comprehensions and regular expressions." :len (- js2-ts-cursor pos) :elems (nreverse elems))) (apply #'js2-node-add-children pn (js2-array-node-elems pn))) - ;; destructuring binding - (js2-is-in-destructuring - (push (cond - ((and (= tt js2-NAME) - (= js2-ASSIGN (js2-peek-token))) - ;; a=defaultValue - (js2-parse-initialized-binding (js2-parse-name js2-NAME))) - ((or (= tt js2-LC) - (= tt js2-LB) - (= tt js2-NAME)) - ;; [a, b, c] | {a, b, c} | {a:x, b:y, c:z} | a - (js2-parse-destruct-primary-expr)) - ;; invalid pattern - (t - (js2-report-error "msg.bad.var") - (make-js2-error-node))) - elems) - (setq after-lb-or-comma nil - after-comma nil)) + ;; anything after rest element (...foo) + (was-rest + (js2-report-error "msg.param.after.rest")) + ;; comma + ((= tt js2-COMMA) + (setq after-comma (js2-current-token-end)) + (if (not after-lb-or-comma) + (setq after-lb-or-comma t) + (push nil elems))) ;; array comp ((and (>= js2-language-version 170) + (not js2-is-in-destructuring) (= tt js2-FOR) ; check for array comprehension (not after-lb-or-comma) ; "for" can't follow a comma elems ; must have at least 1 element @@ -10408,9 +10534,12 @@ array-literals, array comprehensions and regular expressions." (js2-report-error "msg.no.bracket.arg")) (if (and (= tt js2-TRIPLEDOT) (>= js2-language-version 200)) - ;; spread operator - (push (js2-make-unary tt 'js2-parse-assign-expr) - elems) + ;; rest/spread operator + (progn + (push (js2-make-unary tt 'js2-parse-assign-expr) + elems) + (if js2-is-in-destructuring + (setq was-rest t))) (js2-unget-token) (push (js2-parse-assign-expr) elems)) (setq after-lb-or-comma nil @@ -10560,6 +10689,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) @@ -10607,16 +10737,19 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed." `js2-method-node') as a string, or nil if it can't be represented as a string (e.g., the key is computed by an expression)." - (let ((key (js2-infix-node-left property-node))) - (when (js2-computed-prop-name-node-p key) - (setq key (js2-computed-prop-name-node-expr key))) - (cond - ((js2-name-node-p key) - (js2-name-node-name key)) - ((js2-string-node-p key) - (js2-string-node-value key)) - ((js2-number-node-p key) - (js2-number-node-value key))))) + (cond + ((js2-unary-node-p property-node) nil) ;; {...foo} + (t + (let ((key (js2-infix-node-left property-node))) + (when (js2-computed-prop-name-node-p key) + (setq key (js2-computed-prop-name-node-expr key))) + (cond + ((js2-name-node-p key) + (js2-name-node-name key)) + ((js2-string-node-p key) + (js2-string-node-value key)) + ((js2-number-node-p key) + (js2-number-node-value key))))))) (defun js2-parse-object-literal-elems (&optional class-p) (let ((pos (js2-current-token-beg)) @@ -10641,21 +10774,26 @@ expression)." (= js2-MUL tt)) (setq previous-token (js2-current-token) tt (js2-get-prop-name-token))) - ;; Handle 'get' or 'set' keywords + ;; Handle getter, setter and async methods (let ((prop (js2-current-token-string))) (when (and (>= js2-language-version 200) (= js2-NAME tt) - (or (string= prop "get") - (string= prop "set")) + (member prop '("get" "set" "async")) (member (js2-peek-token) (list js2-NAME js2-STRING js2-NUMBER js2-LB))) (setq previous-token (js2-current-token) tt (js2-get-prop-name-token)))) (cond - ;; Found a property (of any sort) + ;; Rest/spread (...expr) + ((and (>= js2-language-version 200) + (not class-p) (not static) (not previous-token) + (= js2-TRIPLEDOT tt)) + (setq after-comma nil + elem (js2-make-unary js2-TRIPLEDOT 'js2-parse-assign-expr))) + ;; 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))) @@ -10699,8 +10837,11 @@ expression)." ;; But make an exception for getter / setter pairs. (not (and (js2-method-node-p elem) (js2-method-node-p previous-elem) - (/= (js2-method-node-type elem) - (js2-method-node-type previous-elem)))))) + (let ((type (js2-node-get-prop (js2-method-node-right elem) 'METHOD_TYPE)) + (previous-type (js2-node-get-prop (js2-method-node-right previous-elem) 'METHOD_TYPE))) + (and (member type '(GET SET)) + (member previous-type '(GET SET)) + (not (eq type previous-type)))))))) elems)) (js2-report-error "msg.dup.obj.lit.prop.strict" elem-key-string @@ -10711,7 +10852,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)) @@ -10719,12 +10860,13 @@ 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))))) - (when (or (string= prop "get") - (string= prop "set")) + (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 + 'font-lock-keyword-face 'record)) ; get/set/async (cond ;; method definition: {f() {...}} ((and (= (js2-peek-token) js2-LP) @@ -10829,7 +10971,8 @@ string or expression." "Parse method property in an object literal or a class body. JavaScript syntax is: - { foo(...) {...}, get foo() {...}, set foo(x) {...}, *foo(...) {...} } + { foo(...) {...}, get foo() {...}, set foo(x) {...}, *foo(...) {...}, + async foo(...) {...} } and expression closure style is also supported @@ -10838,23 +10981,21 @@ and expression closure style is also supported POS is the start position of the `get' or `set' keyword. PROP is the `js2-name-node' representing the property name. TYPE-STRING is a string `get', `set', `*', or nil, indicating a found keyword." - (let ((type (cond - ((string= "get" type-string) js2-GET) - ((string= "set" type-string) js2-SET) - (t js2-FUNCTION))) - result end - (fn (js2-parse-function-expr))) - ;; it has to be an anonymous function, as we already parsed the name - (if (/= (js2-node-type fn) js2-FUNCTION) - (js2-report-error "msg.bad.prop") - (if (cl-plusp (length (js2-function-name fn))) - (js2-report-error "msg.bad.prop"))) + (let* ((type (or (cdr (assoc type-string '(("get" . GET) + ("set" . SET) + ("async" . ASYNC)))) + 'FUNCTION)) + result end + (pos (js2-current-token-beg)) + (_ (js2-must-match js2-LP "msg.no.paren.parms")) + (fn (js2-parse-function 'FUNCTION_EXPRESSION pos + (string= type-string "*") + (eq type 'ASYNC) + nil))) (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 :type type - :pos pos + result (make-js2-method-node :pos pos :len (- end pos) :left prop :right fn)) @@ -10897,6 +11038,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) @@ -11439,7 +11581,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. @@ -11790,10 +11934,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. @@ -12440,7 +12581,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)