;; Support for JSX is available via the derived mode `js2-jsx-mode'. If you
;; also want JSX support, use that mode instead:
-;; (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-jsx-mode))
+;; (add-to-list 'auto-mode-alist '("\\.jsx?\\'" . js2-jsx-mode))
;; (add-to-list 'interpreter-mode-alist '("node" . js2-jsx-mode))
;; To customize how it works:
(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.")
(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)
(string "")
number
number-base
+ number-legacy-octal-p
regexp-flags
comment-type
follows-eol-p)
(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 "
(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)
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
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)
(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
(cons js2-TYPEOF "typeof")
(cons js2-INSTANCEOF "instanceof")
(cons js2-DELPROP "delete")
+ (cons js2-AWAIT "await")
(cons js2-COMMA ",")
(cons js2-COLON ":")
(cons js2-OR "||")
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
(unless postfix
(insert op))
(if (or (= tt js2-TYPEOF)
- (= tt js2-DELPROP))
+ (= tt js2-DELPROP)
+ (= tt js2-AWAIT))
(insert " "))
(js2-print-ast (js2-unary-node-operand n) 0)
(when postfix
(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)
(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)
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)
(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)
(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)
(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 "*"))
(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)
(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
((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))
(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
(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 '(?\" ?\'))
(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 ()
+ (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-await (tt)
+ (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))
(defun js2-get-prop-name-token ()
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)
(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)))
(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")
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)))
: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)
(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."
(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
(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
(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))
(setq from-clause (js2-parse-from-clause)))
(js2-unget-token))))
((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
((= 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)
(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)
(js2-ts-seek ts-state)
(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)))
(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.")
((= tt js2-DELPROP)
(js2-get-token)
(js2-make-unary js2-DELPROP 'js2-parse-unary-expr))
+ ((js2-match-await tt)
+ (js2-make-unary js2-AWAIT 'js2-parse-unary-expr))
((= tt js2-ERROR)
(js2-get-token)
(make-js2-error-node)) ; try to continue
(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)
((= 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))
(= 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)
;; 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
(if (= (js2-token-type previous-token) js2-MUL)
"*"
(js2-token-string previous-token)))))
- (when (or (string= prop "get")
- (string= prop "set"))
+ (when (member prop '("get" "set" "async"))
(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)
"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
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)))
+ (let* ((type (or (cdr (assoc type-string '(("get" . GET)
+ ("set" . SET)
+ ("async" . ASYNC))))
+ 'FUNCTION))
+ result end
+ (fn (js2-parse-function-expr (eq type 'ASYNC))))
;; 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")
(when (string= type-string "*")
(setf (js2-function-node-generator-type fn) 'STAR))
(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))
(js2-check-activation-name s (or token js2-NAME)))
name))
+;;; Use AST to extract semantic information
+
+(defun js2-get-element-index-from-array-node (elem array-node &optional hardcoded-array-index)
+ "Get index of ELEM from ARRAY-NODE or 0 and return it as string."
+ (let ((idx 0) elems (rlt hardcoded-array-index))
+ (setq elems (js2-array-node-elems array-node))
+ (if (and elem (not hardcoded-array-index))
+ (setq rlt (catch 'nth-elt
+ (dolist (x elems)
+ ;; We know the ELEM does belong to ARRAY-NODE,
+ (if (eq elem x) (throw 'nth-elt idx))
+ (setq idx (1+ idx)))
+ 0)))
+ (format "[%s]" rlt)))
+
+(defun js2-print-json-path (&optional hardcoded-array-index)
+ "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")
+ (let (previous-node current-node
+ key-name
+ rlt)
+
+ ;; The `js2-node-at-point' starts scanning from AST root node.
+ ;; So there is no way to optimize it.
+ (setq current-node (js2-node-at-point))
+
+ (while (not (js2-ast-root-p current-node))
+ (cond
+ ;; JSON property node
+ ((js2-object-prop-node-p current-node)
+ (setq key-name (js2-prop-node-name (js2-object-prop-node-left current-node)))
+ (if rlt (setq rlt (concat "." key-name rlt))
+ (setq rlt (concat "." key-name))))
+
+ ;; Array node
+ ((or (js2-array-node-p current-node))
+ (setq rlt (concat (js2-get-element-index-from-array-node previous-node
+ current-node
+ hardcoded-array-index)
+ rlt)))
+
+ ;; Other nodes are ignored
+ (t))
+
+ ;; current node is archived
+ (setq previous-node current-node)
+ ;; Get parent node and continue the loop
+ (setq current-node (js2-node-parent current-node)))
+
+ (cond
+ (rlt
+ ;; Clean the final result
+ (setq rlt (replace-regexp-in-string "^\\." "" rlt))
+ (kill-new rlt)
+ (message "%s => kill-ring" rlt))
+ (t
+ (message "No JSON path found!")))
+
+ rlt))
+
;;; Indentation support (bouncing)
;; In recent-enough Emacs, we reuse the indentation code from
(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.