[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 04f3d4b 110/110: Merge commit '0cda39255827f283e7578cd469a
From: |
Dmitry Gutov |
Subject: |
[elpa] master 04f3d4b 110/110: Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode |
Date: |
Thu, 23 Jun 2016 01:13:04 +0000 (UTC) |
branch: master
commit 04f3d4bb44d7ef30e705b53f3d15e3279ac0d230
Merge: 7a2deff 0cda392
Author: Dmitry Gutov <address@hidden>
Commit: Dmitry Gutov <address@hidden>
Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode
---
packages/js2-mode/Makefile | 9 +-
packages/js2-mode/NEWS.md | 18 +
packages/js2-mode/README.md | 4 +-
packages/js2-mode/js2-mode.el | 954 ++++++++++++++++++++++------------
packages/js2-mode/js2-old-indent.el | 259 ++++++++-
packages/js2-mode/tests/externs.el | 9 +-
packages/js2-mode/tests/indent.el | 151 +++++-
packages/js2-mode/tests/json-path.el | 64 +++
packages/js2-mode/tests/parser.el | 265 ++++++++--
9 files changed, 1320 insertions(+), 413 deletions(-)
diff --git a/packages/js2-mode/Makefile b/packages/js2-mode/Makefile
index 08b1e48..7777a67 100644
--- a/packages/js2-mode/Makefile
+++ b/packages/js2-mode/Makefile
@@ -10,17 +10,14 @@ SRCS = js2-mode.el js2-imenu-extras.el
OBJS = $(SRCS:.el=.elc)
%.elc: %.el
- ${EMACS} $(BATCHFLAGS) -f batch-byte-compile $^
+ ${EMACS} $(BATCHFLAGS) -L . -f batch-byte-compile $^
all: $(OBJS)
clean:
-rm -f $(OBJS)
-# custom build (require loads)
-js2-imenu-extras.elc: js2-mode.elc
- ${EMACS} $(BATCHFLAGS) -l ./js2-mode.elc -f batch-byte-compile $*.el
-
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
diff --git a/packages/js2-mode/NEWS.md b/packages/js2-mode/NEWS.md
index 2984e91..87e9d72 100644
--- a/packages/js2-mode/NEWS.md
+++ b/packages/js2-mode/NEWS.md
@@ -1,5 +1,23 @@
# History of user-visible changes
+## 2016-06-23
+
+* New variable `js2-mode-assume-strict`, for use with ES6 modules.
+* Support for JSDoc @callback, @func and @method tags.
+* Object properties are highlighted using a different face:
+ `js2-object-property`, which has no color by default.
+* Experimental support for object rest/spread ECMAScript proposal.
+* `js2-getter-setter-node` is renamed to `js2-method-node`, together with
+ its related functions. It already handles generator methods, and we
+ added support for async methods (see below), so the old name would get
+ more confusing.
+* Support for default parameters in destructuring. It should work for both
+ objects and arrays, in both literals and function arguments.
+* New mode: `js2-jsx-mode`, deriving from `js2-mode`. Supports indentation of
+ JSXElement expressions wrapped within parentheses or as function arguments.
+ Indentation is customizable via `sgml-attribute-offset`.
+* Experimental support for async/await ECMAScript proposal.
+
## 20150909
* `js2-mode` now derives from `js-mode`. That means the former
diff --git a/packages/js2-mode/README.md b/packages/js2-mode/README.md
index b0ee444..cbce9c3 100644
--- a/packages/js2-mode/README.md
+++ b/packages/js2-mode/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)
+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)).
@@ -12,7 +12,7 @@ The stable versions are hosted at [GNU
ELPA](http://elpa.gnu.org/)
(<kbd>M-x list-packages</kbd>).
You can also install the latest development version from
-[Melpa](http://melpa.milkbox.net/#installing).
+[MELPA](https://melpa.org/#/getting-started).
Emacs 22 and 23
===============
diff --git a/packages/js2-mode/js2-mode.el b/packages/js2-mode/js2-mode.el
index 2d6f336..a2e58fa 100644
--- a/packages/js2-mode/js2-mode.el
+++ b/packages/js2-mode/js2-mode.el
@@ -1,13 +1,13 @@
;;; js2-mode.el --- Improved JavaScript editing mode
-;; Copyright (C) 2009, 2011-2015 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2016 Free Software Foundation, Inc.
;; Author: Steve Yegge <address@hidden>
;; mooz <address@hidden>
;; Dmitry Gutov <address@hidden>
;; URL: https://github.com/mooz/js2-mode/
;; http://code.google.com/p/js2-mode/
-;; Version: 20150909
+;; Version: 20160623
;; Keywords: languages, javascript
;; Package-Requires: ((emacs "24.1") (cl-lib "0.5"))
@@ -60,6 +60,12 @@
;; (add-to-list 'interpreter-mode-alist '("node" . js2-mode))
+;; 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 '("\\.jsx?\\'" . js2-jsx-mode))
+;; (add-to-list 'interpreter-mode-alist '("node" . js2-jsx-mode))
+
;; To customize how it works:
;; M-x customize-group RET js2-mode RET
@@ -95,6 +101,7 @@
(require 'js2-old-indent)
(defvaralias 'js2-basic-offset 'js-indent-level nil)
(defalias 'js2-proper-indentation 'js--proper-indentation)
+ (defalias 'js2-jsx-indent-line 'js-jsx-indent-line)
(defalias 'js2-indent-line 'js-indent-line)
(defalias 'js2-re-search-forward 'js--re-search-forward)))
@@ -192,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.")
@@ -270,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,
@@ -610,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)
@@ -668,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)
@@ -1018,6 +1033,11 @@ in large files.")
"Face used to highlight function name in calls."
:group 'js2-mode)
+(defface js2-object-property
+ '((t :inherit default))
+ "Face used to highlight named property in object literal."
+ :group 'js2-mode)
+
(defface js2-instance-member
'((t :foreground "DarkOrchid"))
"Face used to highlight instance variables in javascript.
@@ -1061,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."
@@ -1350,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 "
@@ -1437,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.")
@@ -1712,6 +1736,9 @@ the correct number of ARGS must be provided."
(js2-msg "msg.destruct.assign.no.init"
"Missing = in destructuring declaration")
+(js2-msg "msg.init.no.destruct"
+ "Binding initializer not in destructuring assignment")
+
(js2-msg "msg.no.octal.strict"
"Octal numbers prohibited in strict mode.")
@@ -2530,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)
@@ -3241,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
@@ -3257,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)
@@ -3270,7 +3302,7 @@ The `params' field is a Lisp list of nodes. Each node is
either a simple
(defun js2-print-function-node (n i)
(let* ((pad (js2-make-pad i))
- (getter (js2-node-get-prop n 'GETTER_SETTER))
+ (method (js2-node-get-prop n 'METHOD_TYPE))
(name (or (js2-function-node-name n)
(js2-function-node-member-expr n)))
(params (js2-function-node-params n))
@@ -3278,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 getter 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
@@ -3450,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 "||")
@@ -3530,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
@@ -3550,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
@@ -3686,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)
@@ -3810,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)
@@ -3824,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)
@@ -3856,28 +3920,29 @@ optional `js2-expr-node'"
(js2-print-ast elem (1+ i))))
(insert "\n" pad "}")))
-(cl-defstruct (js2-object-node
+(cl-defstruct (js2-computed-prop-name-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)
+ (:constructor make-js2-computed-prop-name-node
+ (&key
+ (type js2-LB)
+ expr
+ (pos (js2-current-token-beg))
+ (len (- js2-ts-cursor
+ (js2-current-token-beg))))))
+ "AST node for a `ComputedPropertyName'."
+ expr)
-(put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
-(put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node)
+(put 'cl-struct-js2-computed-prop-name-node 'js2-visitor
'js2-visit-computed-prop-name-node)
+(put 'cl-struct-js2-computed-prop-name-node 'js2-printer
'js2-print-computed-prop-name-node)
-(defun js2-visit-object-node (n v)
- (dolist (e (js2-object-node-elems n))
- (js2-visit-ast e v)))
+(defun js2-visit-computed-prop-name-node (n v)
+ (js2-visit-ast (js2-computed-prop-name-node-expr n) v))
-(defun js2-print-object-node (n i)
- (insert (js2-make-pad i) "{")
- (js2-print-list (js2-object-node-elems n))
- (insert "}"))
+(defun js2-print-computed-prop-name-node (n i)
+ (insert (js2-make-pad i) "[")
+ (js2-print-ast (js2-computed-prop-name-node-expr n) 0)
+ (insert "]"))
(cl-defstruct (js2-object-prop-node
(:include js2-infix-node)
@@ -3898,53 +3963,41 @@ both fields have the same value.")
(defun js2-print-object-prop-node (n i)
(let* ((left (js2-object-prop-node-left n))
- (right (js2-object-prop-node-right n))
- (computed (not (or (js2-string-node-p left)
- (js2-number-node-p left)
- (js2-name-node-p left)))))
- (insert (js2-make-pad i))
- (if computed
- (insert "["))
- (js2-print-ast left 0)
- (if computed
- (insert "]"))
+ (right (js2-object-prop-node-right n)))
+ (js2-print-ast left i)
(if (not (js2-node-get-prop n 'SHORTHAND))
(progn
(insert ": ")
(js2-print-ast right 0)))))
-(cl-defstruct (js2-getter-setter-node
+(cl-defstruct (js2-method-node
(:include js2-infix-node)
(:constructor nil)
- (:constructor make-js2-getter-setter-node (&key type ; GET,
SET, or FUNCTION
- (pos
js2-ts-cursor)
- len left
right)))
- "AST node for a getter/setter property in an object literal.
-The `left' field is the `js2-name-node' naming the getter/setter prop.
+ (: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 `GETTER_SETTER' set to js2-GET, js2-SET, or js2-FUNCTION. ")
+property `METHOD_TYPE' set to 'GET or 'SET. ")
-(put 'cl-struct-js2-getter-setter-node 'js2-visitor 'js2-visit-infix-node)
-(put 'cl-struct-js2-getter-setter-node 'js2-printer 'js2-print-getter-setter)
+(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-getter-setter (n i)
+(defun js2-print-method (n i)
(let* ((pad (js2-make-pad i))
- (left (js2-getter-setter-node-left n))
- (right (js2-getter-setter-node-right n))
- (computed (not (or (js2-string-node-p left)
- (js2-number-node-p left)
- (js2-name-node-p left)))))
+ (left (js2-method-node-left 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 "*"))
- (when computed
- (insert "["))
(js2-print-ast left 0)
- (when computed
- (insert "]"))
(js2-print-ast right 0)))
(cl-defstruct (js2-prop-get-node
@@ -5106,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)
@@ -5855,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
@@ -5977,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))
@@ -6014,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
@@ -6036,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 '(?\" ?\'))
@@ -6753,6 +6811,8 @@ Shown at or above `js2-highlight-level' 3.")
(prop
(if (string-match js2-ecma-object-props prop-name)
'font-lock-constant-face))))))
+ (when (and (not face) target (not call-p) prop-name)
+ (setq face 'js2-object-property))
(when face
(let ((pos (+ (js2-node-pos parent) ; absolute
(js2-node-pos prop)))) ; relative
@@ -6852,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"
@@ -6891,6 +6954,7 @@ of a simple name. Called before EXPR has a parent node."
"export"
"fileoverview"
"final"
+ "func"
"function"
"hidden"
"ignore"
@@ -6899,6 +6963,7 @@ of a simple name. Called before EXPR has a parent node."
"inner"
"interface"
"license"
+ "method"
"noalias"
"noshadow"
"notypecheck"
@@ -7199,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))))
@@ -7395,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.
@@ -7452,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))
@@ -7676,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)))
@@ -7734,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)))
@@ -7876,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
@@ -7987,16 +8104,34 @@ declared; probably to check them for errors."
(list node)))
((js2-object-node-p node)
(dolist (elem (js2-object-node-elems node))
- (when (js2-object-prop-node-p elem)
- (push (js2-define-destruct-symbols
- ;; In abbreviated destructuring {a, b}, right == left.
- (js2-object-prop-node-right 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
+ (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)))
@@ -8053,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)
@@ -8068,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)))
@@ -8116,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")
@@ -8124,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)))
@@ -8153,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)
@@ -8208,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."
@@ -8224,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
@@ -8334,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
@@ -8345,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))
@@ -8474,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)
@@ -8507,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
@@ -8558,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)))
@@ -8567,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
@@ -8579,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 ()
@@ -8745,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
@@ -8810,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)
@@ -9448,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")
@@ -9467,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)
@@ -9477,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)
@@ -9548,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)
@@ -9584,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)))
@@ -9616,7 +9781,7 @@ If NODE is non-nil, it is the AST node associated with
the symbol."
(js2-node-add-children pn test-expr if-true if-false))
pn))
-(defun js2-make-binary (type left parser)
+(defun js2-make-binary (type left parser &optional no-get)
"Helper for constructing a binary-operator AST node.
LEFT is the left-side-expression, already parsed, and the
binary operator should have just been matched.
@@ -9627,7 +9792,7 @@ FIXME: The latter option is unused?"
(op-pos (- (js2-current-token-beg) pos))
(right (if (js2-node-p parser)
parser
- (js2-get-token)
+ (unless no-get (js2-get-token))
(funcall parser)))
(pn (make-js2-infix-node :type type
:pos pos
@@ -9816,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
@@ -9968,9 +10134,9 @@ Returns an expression tree that includes PN, the parent
node."
(setq pn (js2-parse-tagged-template pn (make-js2-string-node :type
tt))))
(t
(js2-unget-token)
- (setq continue nil))))
- (if (>= js2-highlight-level 2)
- (js2-parse-highlight-member-expr-node pn))
+ (setq continue nil)))
+ (if (>= js2-highlight-level 2)
+ (js2-parse-highlight-member-expr-node pn)))
pn))
(defun js2-parse-tagged-template (tag-node tpl-node)
@@ -10205,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)
@@ -10221,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))
@@ -10320,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
@@ -10346,21 +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 (if (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
- (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
@@ -10374,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
@@ -10526,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)
@@ -10570,17 +10734,22 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)'
form is allowed."
(defun js2-property-key-string (property-node)
"Return the key of PROPERTY-NODE (a `js2-object-prop-node' or
-`js2-getter-setter-node') as a string, or nil if it can't be
+`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)))
- (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))
@@ -10605,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)))
@@ -10631,6 +10805,10 @@ expression)."
(if after-comma
(js2-parse-warn-trailing-comma "msg.extra.trailing.comma"
pos elems after-comma)))
+ ;; Skip semicolons in a class body
+ ((and class-p
+ (= tt js2-SEMI))
+ nil)
(t
(js2-report-error "msg.bad.prop")
(unless js2-recover-from-parse-errors
@@ -10654,7 +10832,16 @@ expression)."
(lambda (previous-elem)
(and (setq previous-elem-key-string
(js2-property-key-string previous-elem))
- (string= previous-elem-key-string elem-key-string)))
+ ;; Check if the property is a duplicate.
+ (string= previous-elem-key-string elem-key-string)
+ ;; But make an exception for getter / setter pairs.
+ (not (and (js2-method-node-p elem)
+ (js2-method-node-p 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
@@ -10665,44 +10852,34 @@ 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 (cond
- ;; Literal string keys: {'foo': 'bar'}
- ((= tt js2-STRING)
- (make-js2-string-node))
- ;; Handle computed keys: {[Symbol.iterator]: ...}, *[1+2]()
{...}},
- ;; {[foo + bar]() { ... }}, {[get ['x' + 1]() {...}}
- ((and (= tt js2-LB)
- (>= js2-language-version 200))
- (prog1 (js2-parse-expr)
- (js2-must-match js2-RB "msg.missing.computed.rb")))
- ;; Numeric keys: {12: 'foo'}, {10.7: 'bar'}
- ((= tt js2-NUMBER)
- (make-js2-number-node))
- ;; Unquoted names: {foo: 12}
- ((= tt js2-NAME)
- (js2-create-name-node))
- ;; Anything else is an error
- (t (js2-report-error "msg.bad.prop"))))
+ (let ((key (js2-parse-prop-name tt))
(prop (and previous-token (js2-token-string previous-token)))
(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)
(>= js2-language-version 200))
(when (js2-name-node-p key) ; highlight function name properties
(js2-record-face 'font-lock-function-name-face))
- (js2-parse-getter-setter-prop pos key property-type))
+ (js2-parse-method-prop pos key property-type))
+ ;; binding element with initializer
+ ((and (= (js2-peek-token) js2-ASSIGN)
+ (>= js2-language-version 200))
+ (if (not js2-is-in-destructuring)
+ (js2-report-error "msg.init.no.destruct"))
+ (js2-parse-initialized-binding key))
;; regular prop
(t
(let ((beg (js2-current-token-beg))
@@ -10717,10 +10894,38 @@ When `js2-is-in-destructuring' is t, forms like {a,
b, c} will be permitted."
(if (js2-function-node-p
(js2-object-prop-node-right expr))
'font-lock-function-name-face
- 'font-lock-variable-name-face)
+ 'js2-object-property)
'record)
expr)))))
+(defun js2-parse-initialized-binding (name)
+ "Parse a `SingleNameBinding' with initializer.
+
+`name' is the `BindingIdentifier'."
+ (when (js2-match-token js2-ASSIGN)
+ (js2-make-binary js2-ASSIGN name 'js2-parse-assign-expr t)))
+
+(defun js2-parse-prop-name (tt)
+ (cond
+ ;; Literal string keys: {'foo': 'bar'}
+ ((= tt js2-STRING)
+ (make-js2-string-node))
+ ;; Handle computed keys: {[Symbol.iterator]: ...}, *[1+2]() {...}},
+ ;; {[foo + bar]() { ... }}, {[get ['x' + 1]() {...}}
+ ((and (= tt js2-LB)
+ (>= js2-language-version 200))
+ (make-js2-computed-prop-name-node
+ :expr (prog1 (js2-parse-assign-expr)
+ (js2-must-match js2-RB "msg.missing.computed.rb"))))
+ ;; Numeric keys: {12: 'foo'}, {10.7: 'bar'}
+ ((= tt js2-NUMBER)
+ (make-js2-number-node))
+ ;; Unquoted names: {foo: 12}
+ ((= tt js2-NAME)
+ (js2-create-name-node))
+ ;; Anything else is an error
+ (t (js2-report-error "msg.bad.prop"))))
+
(defun js2-parse-plain-property (prop)
"Parse a non-getter/setter property in an object literal.
PROP is the node representing the property: a number, name,
@@ -10762,11 +10967,12 @@ string or expression."
(js2-node-add-children result prop expr)
result))))
-(defun js2-parse-getter-setter-prop (pos prop type-string)
- "Parse getter or setter property in an object literal.
+(defun js2-parse-method-prop (pos prop type-string)
+ "Parse method property in an object literal or a class body.
JavaScript syntax is:
- { get foo() {...}, set foo(x) {...} }
+ { foo(...) {...}, get foo() {...}, set foo(x) {...}, *foo(...) {...},
+ async foo(...) {...} }
and expression closure style is also supported
@@ -10775,26 +10981,24 @@ 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")))
- (js2-node-set-prop fn 'GETTER_SETTER type) ; for codegen
- (when (string= type-string "*")
- (setf (js2-function-node-generator-type fn) 'STAR))
+ (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
+ (unless pos (setq pos (js2-node-pos prop)))
(setq end (js2-node-end fn)
- result (make-js2-getter-setter-node :type type
- :pos pos
- :len (- end pos)
- :left prop
- :right fn))
+ result (make-js2-method-node :pos pos
+ :len (- end pos)
+ :left prop
+ :right fn))
(js2-node-add-children result prop fn)
result))
@@ -10815,6 +11019,68 @@ And, if CHECK-ACTIVATION-P is non-nil, use the value
of TOKEN."
(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")
+ (js2-reparse)
+ (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
@@ -11310,7 +11576,23 @@ 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.
+;;;###autoload
+(define-derived-mode js2-jsx-mode js2-mode "JSX-IDE"
+ "Major mode for editing JSX code.
+
+To customize the indentation for this mode, set the SGML offset
+variables (`sgml-basic-offset' et al) locally, like so:
+
+ (defun set-jsx-indentation ()
+ (setq-local sgml-basic-offset js2-basic-offset))
+ (add-hook 'js2-jsx-mode-hook #'set-jsx-indentation)"
+ (set (make-local-variable 'indent-line-function) #'js2-jsx-indent-line))
(defun js2-mode-exit ()
"Exit `js2-mode' and clean up."
@@ -11647,10 +11929,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.
@@ -12297,7 +12576,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)
diff --git a/packages/js2-mode/js2-old-indent.el
b/packages/js2-mode/js2-old-indent.el
index efc9053..f336005 100644
--- a/packages/js2-mode/js2-old-indent.el
+++ b/packages/js2-mode/js2-old-indent.el
@@ -54,6 +54,8 @@
;;; Code:
+(require 'sgml-mode)
+
(defvar js2-language-version)
(declare-function js2-mark-safe-local "js2-mode")
@@ -129,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)
@@ -215,30 +217,38 @@ and comments have been removed."
(defun js2-looking-at-operator-p ()
"Return non-nil if text after point is a non-comma operator."
+ (defvar js2-mode-identifier-re)
(and (looking-at js2-indent-operator-re)
- (or (not (looking-at ":"))
+ (or (not (eq (char-after) ?:))
(save-excursion
(and (js2-re-search-backward "[?:{]\\|\\_<case\\_>" nil t)
- (looking-at "?"))))))
+ (eq (char-after) ??))))
+ (not (and
+ (eq (char-after) ?*)
+ ;; Generator method (possibly using computed property).
+ (looking-at (concat "\\* *\\(?:\\[\\|"
+ js2-mode-identifier-re
+ " *(\\)"))
+ (save-excursion
+ (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
@@ -431,7 +441,7 @@ indentation is aligned to that column."
(goto-char bracket)
(cond
((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)")
- (when (save-excursion (skip-chars-backward " \t)")
+ (when (save-excursion (skip-chars-backward " \t\n)")
(looking-at ")"))
(backward-list))
(back-to-indentation)
@@ -484,6 +494,215 @@ indentation is aligned to that column."
(when (cl-plusp offset)
(forward-char offset)))))
+;;; JSX Indentation
+
+;; The following JSX indentation code is copied basically verbatim from js.el
at
+;; 958da7f, except that the prefixes on the functions/variables are changed.
+
+(defsubst js2--jsx-find-before-tag ()
+ "Find where JSX starts.
+
+Assume JSX appears in the following instances:
+- Inside parentheses, when returned or as the first argument
+ to a function, and after a newline
+- When assigned to variables or object properties, but only
+ on a single line
+- As the N+1th argument to a function
+
+This is an optimized version of (re-search-backward \"[(,]\n\"
+nil t), except set point to the end of the match. This logic
+executes up to the number of lines in the file, so it should be
+really fast to reduce that impact."
+ (let (pos)
+ (while (and (> (point) (point-min))
+ (not (progn
+ (end-of-line 0)
+ (when (or (eq (char-before) 40) ; (
+ (eq (char-before) 44)) ; ,
+ (setq pos (1- (point))))))))
+ pos))
+
+(defconst js2--jsx-end-tag-re
+ (concat "</" sgml-name-re ">\\|/>")
+ "Find the end of a JSX element.")
+
+(defconst js2--jsx-after-tag-re "[),]"
+ "Find where JSX ends.
+This complements the assumption of where JSX appears from
+`js--jsx-before-tag-re', which see.")
+
+(defun js2--jsx-indented-element-p ()
+ "Determine if/how the current line should be indented as JSX.
+
+Return `first' for the first JSXElement on its own line.
+Return `nth' for subsequent lines of the first JSXElement.
+Return `expression' for an embedded JS expression.
+Return `after' for anything after the last JSXElement.
+Return nil for non-JSX lines.
+
+Currently, JSX indentation supports the following styles:
+
+- Single-line elements (indented like normal JS):
+
+ var element = <div></div>;
+
+- Multi-line elements (enclosed in parentheses):
+
+ function () {
+ return (
+ <div>
+ <div></div>
+ </div>
+ );
+ }
+
+- Function arguments:
+
+ React.render(
+ <div></div>,
+ document.querySelector('.root')
+ );"
+ (let ((current-pos (point))
+ (current-line (line-number-at-pos))
+ last-pos
+ before-tag-pos before-tag-line
+ tag-start-pos tag-start-line
+ tag-end-pos tag-end-line
+ after-tag-line
+ parens paren type)
+ (save-excursion
+ (and
+ ;; Determine if we're inside a jsx element
+ (progn
+ (end-of-line)
+ (while (and (not tag-start-pos)
+ (setq last-pos (js2--jsx-find-before-tag)))
+ (while (forward-comment 1))
+ (when (= (char-after) 60) ; <
+ (setq before-tag-pos last-pos
+ tag-start-pos (point)))
+ (goto-char last-pos))
+ tag-start-pos)
+ (progn
+ (setq before-tag-line (line-number-at-pos before-tag-pos)
+ tag-start-line (line-number-at-pos tag-start-pos))
+ (and
+ ;; A "before" line which also starts an element begins with js, so
+ ;; indent it like js
+ (> current-line before-tag-line)
+ ;; Only indent the jsx lines like jsx
+ (>= current-line tag-start-line)))
+ (cond
+ ;; Analyze bounds if there are any
+ ((progn
+ (while (and (not tag-end-pos)
+ (setq last-pos (re-search-forward js2--jsx-end-tag-re
nil t)))
+ (while (forward-comment 1))
+ (when (looking-at js2--jsx-after-tag-re)
+ (setq tag-end-pos last-pos)))
+ tag-end-pos)
+ (setq tag-end-line (line-number-at-pos tag-end-pos)
+ after-tag-line (line-number-at-pos after-tag-line))
+ (or (and
+ ;; Ensure we're actually within the bounds of the jsx
+ (<= current-line tag-end-line)
+ ;; An "after" line which does not end an element begins with
+ ;; js, so indent it like js
+ (<= current-line after-tag-line))
+ (and
+ ;; Handle another case where there could be e.g. comments after
+ ;; the element
+ (> current-line tag-end-line)
+ (< current-line after-tag-line)
+ (setq type 'after))))
+ ;; They may not be any bounds (yet)
+ (t))
+ ;; Check if we're inside an embedded multi-line js expression
+ (cond
+ ((not type)
+ (goto-char current-pos)
+ (end-of-line)
+ (setq parens (nth 9 (syntax-ppss)))
+ (while (and parens (not type))
+ (setq paren (car parens))
+ (cond
+ ((and (>= paren tag-start-pos)
+ ;; Curly bracket indicates the start of an embedded
expression
+ (= (char-after paren) 123) ; {
+ ;; The first line of the expression is indented like sgml
+ (> current-line (line-number-at-pos paren))
+ ;; Check if within a closing curly bracket (if any)
+ ;; (exclusive, as the closing bracket is indented like sgml)
+ (cond
+ ((progn
+ (goto-char paren)
+ (ignore-errors (let (forward-sexp-function)
+ (forward-sexp))))
+ (< current-line (line-number-at-pos)))
+ (t)))
+ ;; Indicate this guy will be indented specially
+ (setq type 'expression))
+ (t (setq parens (cdr parens)))))
+ t)
+ (t))
+ (cond
+ (type)
+ ;; Indent the first jsx thing like js so we can indent future jsx
things
+ ;; like sgml relative to the first thing
+ ((= current-line tag-start-line) 'first)
+ ('nth))))))
+
+(defmacro js2--as-sgml (&rest body)
+ "Execute BODY as if in sgml-mode."
+ `(with-syntax-table sgml-mode-syntax-table
+ (let (forward-sexp-function
+ parse-sexp-lookup-properties)
+ ,@body)))
+
+(defun js2--expression-in-sgml-indent-line ()
+ "Indent the current line as JavaScript or SGML (whichever is farther)."
+ (let* (indent-col
+ (savep (point))
+ ;; Don't whine about errors/warnings when we're indenting.
+ ;; This has to be set before calling parse-partial-sexp below.
+ (inhibit-point-motion-hooks t)
+ (parse-status (save-excursion
+ (syntax-ppss (point-at-bol)))))
+ ;; Don't touch multiline strings.
+ (unless (nth 3 parse-status)
+ (setq indent-col (save-excursion
+ (back-to-indentation)
+ (if (>= (point) savep) (setq savep nil))
+ (js2--as-sgml (sgml-calculate-indent))))
+ (if (null indent-col)
+ 'noindent
+ ;; Use whichever indentation column is greater, such that the sgml
+ ;; column is effectively a minimum
+ (setq indent-col (max (js2-proper-indentation parse-status)
+ (+ indent-col js2-basic-offset)))
+ (if savep
+ (save-excursion (indent-line-to indent-col))
+ (indent-line-to indent-col))))))
+
+(defun js2-jsx-indent-line ()
+ "Indent the current line as JSX (with SGML offsets).
+i.e., customize JSX element indentation with `sgml-basic-offset'
+et al."
+ (interactive)
+ (let ((indentation-type (js2--jsx-indented-element-p)))
+ (cond
+ ((eq indentation-type 'expression)
+ (js2--expression-in-sgml-indent-line))
+ ((or (eq indentation-type 'first)
+ (eq indentation-type 'after))
+ ;; Don't treat this first thing as a continued expression (often a "<" or
+ ;; ">" causes this misinterpretation)
+ (cl-letf (((symbol-function #'js2-continued-expression-p) 'ignore))
+ (js2-indent-line)))
+ ((eq indentation-type 'nth)
+ (js2--as-sgml (sgml-indent-line)))
+ (t (js2-indent-line)))))
+
(provide 'js2-old-indent)
;;; js2-old-indent.el ends here
diff --git a/packages/js2-mode/tests/externs.el
b/packages/js2-mode/tests/externs.el
index 40957f6..75b93cd 100644
--- a/packages/js2-mode/tests/externs.el
+++ b/packages/js2-mode/tests/externs.el
@@ -1,6 +1,6 @@
;;; tests/externs.el --- Some tests for js2-mode.
-;; Copyright (C) 2009, 2011-2013 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2014, 2016 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
@@ -42,6 +42,13 @@
(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
diff --git a/packages/js2-mode/tests/indent.el
b/packages/js2-mode/tests/indent.el
index 27b6c5a..0fabe95 100644
--- a/packages/js2-mode/tests/indent.el
+++ b/packages/js2-mode/tests/indent.el
@@ -1,6 +1,6 @@
;;; tests/indent.el --- Some tests for js2-mode.
-;; Copyright (C) 2009, 2011-2013 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2016 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
@@ -31,14 +31,16 @@
(if keep-indent
s
(replace-regexp-in-string "^ *" "" s)))
- (js2-mode)
+ (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)))))))
(cl-defmacro js2-deftest-indent (name content &key bind keep-indent)
`(ert-deftest ,(intern (format "js2-%s" name)) ()
- (let ,(append '((js2-basic-offset 2)
+ (let ,(append '(indent-tabs-mode
+ (js2-basic-offset 2)
(js2-pretty-multiline-declarations t)
(inhibit-point-motion-hooks t))
bind)
@@ -140,3 +142,146 @@
| ,
| bar = 8;"
:bind ((js2-pretty-multiline-declarations 'dynamic)))
+
+(js2-deftest-indent indent-generator-method
+ "class A {
+ | * x() {
+ | return 1
+ | * a(2);
+ | }
+ |}")
+
+(js2-deftest-indent indent-generator-computed-method
+ "class A {
+ | *[Symbol.iterator]() {
+ | yield 'Foo';
+ | yield 'Bar';
+ | }
+ |}")
+
+(js2-deftest-indent case-inside-switch
+ "switch(true) {
+ |case 'true':
+ | return 1;
+ |}")
+
+(js2-deftest-indent case-inside-switch-with-extra-indent
+ "switch(true) {
+ | case 'true':
+ | return 1;
+ |}"
+ :bind ((js2-indent-switch-body t)))
+
+(js2-deftest-indent case-inside-switch-with-extra-indent-curly-after-newline
+ "switch(true)
+ |{
+ | case 'true':
+ | return 1;
+ |}"
+ :bind ((js2-indent-switch-body t)))
+
+(js2-deftest-indent continued-expression-vs-unary-minus
+ "var arr = [
+ | -1, 2,
+ | -3, 4 +
+ | -5
+ |];")
+
+(js2-deftest-indent spread-inside-array
+ "var z = [
+ | ...iterableObj,
+ | 4,
+ | 5
+ |]")
+
+(js2-deftest-indent jsx-one-line
+ "var foo = <div></div>;")
+
+(js2-deftest-indent jsx-children-parentheses
+ "return (
+ | <div>
+ | </div>
+ | <div>
+ | <div></div>
+ | <div>
+ | <div></div>
+ | </div>
+ | </div>
+ |);")
+
+(js2-deftest-indent jsx-children-unclosed
+ "return (
+ | <div>
+ | <div>")
+
+(js2-deftest-indent jsx-argument
+ "React.render(
+ | <div>
+ | <div></div>
+ | </div>,
+ | {
+ | a: 1
+ | },
+ | <div>
+ | <div></div>
+ | </div>
+ |);")
+
+(js2-deftest-indent jsx-leading-comment
+ "return (
+ | // Sneaky!
+ | <div></div>
+ |);")
+
+(js2-deftest-indent jsx-trailing-comment
+ "return (
+ | <div></div>
+ | // Sneaky!
+ |);")
+
+(js2-deftest-indent jsx-self-closing
+ ;; This ensures we know the bounds of a self-closing element
+ "React.render(
+ | <input
+ | />,
+ | {
+ | a: 1
+ | }
+ |);"
+ :bind ((sgml-attribute-offset 1))) ; Emacs 24.5 -> 25 compat
+
+(js2-deftest-indent jsx-embedded-js-content
+ "return (
+ | <div>
+ | {array.map(function () {
+ | return {
+ | a: 1
+ | };
+ | })}
+ | </div>
+ |);")
+
+(js2-deftest-indent jsx-embedded-js-unclosed
+ "return (
+ | <div>
+ | {array.map(function () {
+ | return {
+ | a: 1")
+
+(js2-deftest-indent jsx-embedded-js-attribute
+ "return (
+ | <div attribute={array.map(function () {
+ | return {
+ | a: 1
+ | };
+ |
+ | return {
+ | a: 1
+ | };
+ |
+ | return {
+ | a: 1
+ | };
+ | })}>
+ | </div>
+ |);")
diff --git a/packages/js2-mode/tests/json-path.el
b/packages/js2-mode/tests/json-path.el
new file mode 100644
index 0000000..70aecef
--- /dev/null
+++ b/packages/js2-mode/tests/json-path.el
@@ -0,0 +1,64 @@
+;;; tests/json-path.el --- Test of using js2-mode AST to print JSON path.
+
+;; Copyright (C) 2015 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'js2-mode)
+
+(ert-deftest js2-json-path-with-actual-array-index ()
+ (with-temp-buffer
+ (insert "var a = { hello: [1, 2, [1,3,3,4, { world: { hell: { yes: [1,2,
'test'] } } }]] };")
+ (js2-mode)
+ (goto-char 0)
+ (search-forward "test")
+ (should (string= (js2-print-json-path) "hello[2][4].world.hell.yes[2]"))))
+
+(ert-deftest js2-json-path-pure-arrays ()
+ (with-temp-buffer
+ (insert "var a = [5, 1, 4, [ 4, [1, 2, [1, 3.9, 4, [1, 2, 'test',3]]]], 9,
9];")
+ (js2-mode)
+ (goto-char 0)
+ (search-forward "test")
+ (should (string= (js2-print-json-path) "[3][1][2][3][2]"))))
+
+(ert-deftest js2-json-path-key-is-numeric ()
+ (with-temp-buffer
+ (insert "var b = {hello: {3 : {world: {2: 'test'}}}};")
+ (js2-mode)
+ (goto-char 0)
+ (search-forward "test")
+ (should (string= (js2-print-json-path) "hello.3.world.2"))))
+
+(ert-deftest js2-json-path-not-found ()
+ (with-temp-buffer
+ (insert "console.log('test');")
+ (js2-mode)
+ (goto-char 0)
+ (search-forward "test")
+ (should (eq (js2-print-json-path) nil))))
+
+(ert-deftest js2-json-path-with-array-index-hardcoded ()
+ (with-temp-buffer
+ (insert "var a = { hello: [1, 2, [1,3,3,4, { world: { hell: { yes: [1,2,
'test'] } } }]] };")
+ (js2-mode)
+ (goto-char 0)
+ (search-forward "test")
+ (should (string= (js2-print-json-path 1) "hello[1][1].world.hell.yes[1]"))
+ (should (string= (js2-print-json-path 0)
"hello[0][0].world.hell.yes[0]"))))
diff --git a/packages/js2-mode/tests/parser.el
b/packages/js2-mode/tests/parser.el
index dc8c001..b4aa683 100644
--- a/packages/js2-mode/tests/parser.el
+++ b/packages/js2-mode/tests/parser.el
@@ -1,6 +1,6 @@
;;; tests/parser.el --- Some tests for js2-mode.
-;; Copyright (C) 2009, 2011-2013 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2016 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
@@ -35,14 +35,18 @@
,@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
@@ -57,10 +61,13 @@
(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,16 +184,49 @@ 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} = {};\nb;"
+ :warnings-count 0)
+
+(js2-deftest-parse destruct-with-initializer-in-array
+ "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)
+
+(js2-deftest-parse destruct-with-initializer-in-function-params
+ "function f({a, b = 1, c}, [d, e = 1, f]) {\n}")
+
+(js2-deftest-parse destruct-with-default-in-function-params
+ "function f({x = 1, y = 2} = {}, [x, y] = [1, 2]) {\n}")
+
+(js2-deftest-parse destruct-name-conflict-is-error-in-object
+ "\"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--and-parse)
+ (should (equal '("msg.var.redecl" "a")
+ (caar js2-parsed-warnings))))
+
+(js2-deftest initializer-outside-destruct-is-error "({a=1});"
+ (js2-mode--and-parse)
+ (should (equal "msg.init.no.destruct"
+ (car (caar js2-parsed-errors)))))
;;; Object literals
@@ -189,6 +239,11 @@ the test."
(js2-deftest-parse object-literal-method
"var x = {f(y) { return y;\n}};")
+(js2-deftest object-literal-method-own-name-in-scope "({f(){f();}});"
+ (js2-mode--and-parse)
+ (should (equal '("msg.undeclared.variable" "f")
+ (caar js2-parsed-warnings))))
+
(js2-deftest-parse object-literal-getter-method
"var x = {get f() { return 42;\n}};")
@@ -205,7 +260,7 @@ the test."
"var x = {get [foo + bar]() { return 42;\n}};")
(js2-deftest-parse object-literal-generator
- "var x = {*foo() { yield 42;\n}};")
+ "var x = {*foo() { yield* 42;\n}};")
(js2-deftest-parse object-literal-computed-generator-key
"var x = {*[foo + bar]() { yield 42;\n}};")
@@ -213,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
@@ -290,14 +345,29 @@ the test."
"'use strict';\nvar number = 0644;"
:syntax-error "0644" :errors-count 1)
+(js2-deftest-parse function-strict-octal-allow-0o
+ "'use strict';\n0o644;" :reference "'use strict';\n420;")
+
(js2-deftest-parse function-strict-duplicate-keys
"'use strict';\nvar object = {a: 1, a: 2, 'a': 3, ['a']: 4, 1: 5, '1': 6, [1
+ 1]: 7};"
:syntax-error "a" :errors-count 4) ; "a" has 3 dupes, "1" has 1 dupe.
-;; errors... or lackthereof.
+(js2-deftest-parse function-strict-duplicate-getter
+ "'use strict';\nvar a = {get x() {}, get x() {}};"
+ :syntax-error "x" :errors-count 1)
+
+(js2-deftest-parse function-strict-duplicate-setter
+ "'use strict';\nvar a = {set x() {}, set x() {}};"
+ :syntax-error "x" :errors-count 1)
+
+;;; Lack of errors in strict mode
+
(js2-deftest-parse function-strict-const-scope
"'use strict';\nconst a;\nif (1) {\n const a;\n}")
+(js2-deftest-parse function-strict-no-getter-setter-duplicate
+ "'use strict';\nvar a = {get x() {}, set x() {}};")
+
;;; Spread operator
(js2-deftest-parse spread-in-array-literal
@@ -306,6 +376,26 @@ the test."
(js2-deftest-parse spread-in-function-call
"f(3, ...[t(2), t(3)], 42, ...[t(4)]);")
+(js2-deftest-parse rest-in-array-destructure
+ "let [x, y, z, ...w] = [1, ...a, ...b, c];")
+
+(js2-deftest-parse comma-after-rest-in-array
+ "let [...x,] = [1, 2, 3];"
+ :syntax-error "," :errors-count 1)
+
+(js2-deftest-parse elem-after-rest-in-array
+ "let [...x, y] = [1, 2, 3];"
+ :syntax-error "," :errors-count 2)
+
+(js2-deftest-parse array-destructure-expr-default
+ "let [[x] = [3]] = y;")
+
+(js2-deftest-parse spread-in-object-literal
+ "f({x, y, ...z});")
+
+(js2-deftest-parse rest-in-object-literal
+ "const {x, y, ...z} = f();")
+
;;; Arrow functions
(js2-deftest-parse arrow-function-with-empty-args-and-no-curlies
@@ -344,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)))))
@@ -387,6 +477,79 @@ the test."
(js2-deftest-parse parse-generator-comp-with-yield-inside-function-is-ok
"(for (x of []) function*() { yield x;\n});")
+;;; Async
+
+(js2-deftest-parse async-function-statement
+ "async function foo() {\n}")
+
+(js2-deftest-parse async-function-statement-inside-block
+ "if (true) {\n async function foo() {\n }\n}")
+
+(js2-deftest-parse async-function-expression-statements-are-verboten
+ "async function() {}" :syntax-error "(")
+
+(js2-deftest-parse async-named-function-expression
+ "a = async function b() {};")
+
+(js2-deftest-parse async-arrow-function-expression
+ "a = async (b) => { b;\n};")
+
+(js2-deftest-parse async-method-in-object-literal
+ "({async f() {}});")
+
+(js2-deftest-parse async-method-in-class-body
+ "class C {\n async foo() {}\n}")
+
+(js2-deftest-parse static-async-method-in-class-body
+ "class C {\n static async foo() {}\n}")
+
+(js2-deftest-parse async-method-allow-await
+ "({async f() { await x;\n}});")
+
+;;; Await
+
+(js2-deftest-parse await-is-ok "async function foo() {\n await bar();\n}")
+
+(js2-deftest-parse await-inside-assignment-is-ok
+ "async function foo() {\n var result = await bar();\n}")
+
+(js2-deftest-parse await-inside-array-is-ok
+ "async function foo() {\n var results = [await bar(),
await baz()];\n}")
+
+(js2-deftest-parse await-inside-non-async-function-is-not-ok
+ "function foo() {\n await bar();\n}"
+ :syntax-error "await")
+
+(js2-deftest-parse await-inside-non-async-arrow-function-is-not-ok
+ "a = () => { await bar();\n}"
+ :syntax-error "await")
+
+;;; '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;")
+
+(js2-deftest-parse await-can-be-function-name
+ "function await() {\n}")
+
;;; Numbers
(js2-deftest-parse decimal-starting-with-zero "081;" :reference "81;")
@@ -639,7 +802,7 @@ the test."
(should export-node)
(should (js2-var-decl-node-p (js2-export-node-declaration export-node)))))
-(js2-deftest export-class-declaration "export class Foo {};"
+(js2-deftest export-class-declaration "export class Foo {}"
(js2-init-scanner)
(js2-push-scope (make-js2-scope :pos 0))
(should (js2-match-token js2-EXPORT))
@@ -647,7 +810,7 @@ the test."
(should export-node)
(should (js2-class-node-p (js2-export-node-declaration export-node)))))
-(js2-deftest export-function-declaration "export default function doStuff()
{};"
+(js2-deftest export-function-declaration "export default function doStuff() {}"
(js2-init-scanner)
(js2-push-scope (make-js2-scope :pos 0))
(should (js2-match-token js2-EXPORT))
@@ -655,7 +818,7 @@ the test."
(should export-node)
(should (js2-export-node-default export-node))))
-(js2-deftest export-generator-declaration "export default function* one() {};"
+(js2-deftest export-generator-declaration "export default function* one() {}"
(js2-init-scanner)
(js2-push-scope (make-js2-scope :pos 0))
(should (js2-match-token js2-EXPORT))
@@ -672,24 +835,43 @@ 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';")
(js2-deftest-parse parse-export-const-declaration "export const PI = Math.PI;")
(js2-deftest-parse parse-export-let-declaration "export let foo = [1];")
-(js2-deftest-parse parse-export-function-declaration "export default function
doStuff() {};")
-(js2-deftest-parse parse-export-generator-declaration "export default
function* one() {};")
+(js2-deftest-parse parse-export-function-declaration "export default function
doStuff() {\n}")
+(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
@@ -749,10 +931,13 @@ the test."
(js2-deftest-parse parse-class-keywordlike-method
"class C {\n delete() {}\n if() {}\n}")
+(js2-deftest-parse parse-harmony-class-allow-semicolon-element
+ "class Foo {;}" :reference "class Foo {\n}")
+
;;; 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"))
@@ -762,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)))
@@ -780,36 +965,26 @@ 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-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-deftest const-scope-inside-script "{ const a; } a;"
+ (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-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-deftest const-scope-inside-function "function f() { { const a; } a; }"
+ (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)))
@@ -879,23 +1054,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))))
@@ -934,7 +1109,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)))))
- [elpa] master e44d9e1 088/110: Merge pull request #325 from phst/all-tests, (continued)
- [elpa] master e44d9e1 088/110: Merge pull request #325 from phst/all-tests, Dmitry Gutov, 2016/06/22
- [elpa] master 812df51 091/110: Merge pull request #326 from phst/compiler-warning-25, Dmitry Gutov, 2016/06/22
- [elpa] master de5b31c 085/110: Fix the tests broken in the previous commit, Dmitry Gutov, 2016/06/22
- [elpa] master f130121 095/110: Merge pull request #328 from XeCycle/iname-color, Dmitry Gutov, 2016/06/22
- [elpa] master 9586f45 096/110: Add :warnings-count keyword argument, Dmitry Gutov, 2016/06/22
- [elpa] master e5e4fa4 105/110: Add js2-mode-assume-strict custom variable, Dmitry Gutov, 2016/06/22
- [elpa] master bb0acf5 106/110: Merge pull request #342 from shicks/strict, Dmitry Gutov, 2016/06/22
- [elpa] master 173d1c8 101/110: Merge pull request #332 from bolivier/master, Dmitry Gutov, 2016/06/22
- [elpa] master c162f35 092/110: Colorize contextual keywords as & from in import & export, Dmitry Gutov, 2016/06/22
- [elpa] master 66fe8de 107/110: js2-define-symbol: Treat const same as let, Dmitry Gutov, 2016/06/22
- [elpa] master 04f3d4b 110/110: Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode,
Dmitry Gutov <=