about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/js2-mode-20180724.801/js2-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/js2-mode-20180724.801/js2-mode.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/js2-mode-20180724.801/js2-mode.el12856
1 files changed, 0 insertions, 12856 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/js2-mode-20180724.801/js2-mode.el b/configs/shared/emacs/.emacs.d/elpa/js2-mode-20180724.801/js2-mode.el
deleted file mode 100644
index 6aca8266c45f..000000000000
--- a/configs/shared/emacs/.emacs.d/elpa/js2-mode-20180724.801/js2-mode.el
+++ /dev/null
@@ -1,12856 +0,0 @@
-;;; js2-mode.el --- Improved JavaScript editing mode -*- lexical-binding: t -*-
-
-;; Copyright (C) 2009, 2011-2018  Free Software Foundation, Inc.
-
-;; Author: Steve Yegge <steve.yegge@gmail.com>
-;;         mooz <stillpedant@gmail.com>
-;;         Dmitry Gutov <dgutov@yandex.ru>
-;; URL:  https://github.com/mooz/js2-mode/
-;;       http://code.google.com/p/js2-mode/
-;; Version: 20180301
-;; Keywords: languages, javascript
-;; Package-Requires: ((emacs "24.1") (cl-lib "0.5"))
-
-;; 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/>.
-
-;;; Commentary:
-
-;; This JavaScript editing mode supports:
-
-;;  - strict recognition of the Ecma-262 language standard
-;;  - support for most Rhino and SpiderMonkey extensions from 1.5 and up
-;;  - parsing support for ECMAScript for XML (E4X, ECMA-357)
-;;  - accurate syntax highlighting using a recursive-descent parser
-;;  - on-the-fly reporting of syntax errors and strict-mode warnings
-;;  - undeclared-variable warnings using a configurable externs framework
-;;  - "bouncing" line indentation to choose among alternate indentation points
-;;  - smart line-wrapping within comments and strings
-;;  - code folding:
-;;    - show some or all function bodies as {...}
-;;    - show some or all block comments as /*...*/
-;;  - context-sensitive menu bar and popup menus
-;;  - code browsing using the `imenu' package
-;;  - many customization options
-
-;; Installation:
-;;
-;; To install it as your major mode for JavaScript editing:
-
-;;   (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
-
-;; Alternatively, to install it as a minor mode just for JavaScript linting,
-;; you must add it to the appropriate major-mode hook.  Normally this would be:
-
-;;   (add-hook 'js-mode-hook 'js2-minor-mode)
-
-;; You may also want to hook it in for shell scripts running via node.js:
-
-;;   (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
-
-;; Notes:
-
-;; This mode includes a port of Mozilla Rhino's scanner, parser and
-;; symbol table.  Ideally it should stay in sync with Rhino, keeping
-;; `js2-mode' current as the EcmaScript language standard evolves.
-
-;; Unlike cc-engine based language modes, js2-mode's line-indentation is not
-;; customizable.  It is a surprising amount of work to support customizable
-;; indentation.  The current compromise is that the tab key lets you cycle among
-;; various likely indentation points, similar to the behavior of python-mode.
-
-;; This mode does not yet work with "multi-mode" modes such as `mmm-mode'
-;; and `mumamo', although it could be made to do so with some effort.
-;; This means that `js2-mode' is currently only useful for editing JavaScript
-;; files, and not for editing JavaScript within <script> tags or templates.
-
-;; The project page on GitHub is used for development and issue tracking.
-;; The original homepage at Google Code has outdated information and is mostly
-;; unmaintained.
-
-;;; Code:
-
-(require 'cl-lib)
-(require 'imenu)
-(require 'js)
-(require 'etags)
-
-(eval-and-compile
-  (if (version< emacs-version "25.0")
-      (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)))
-
-;;; Externs (variables presumed to be defined by the host system)
-
-(defvar js2-ecma-262-externs
-  (mapcar 'symbol-name
-          '(Array Boolean Date Error EvalError Function Infinity JSON
-          Math NaN Number Object RangeError ReferenceError RegExp
-          String SyntaxError TypeError URIError
-          decodeURI decodeURIComponent encodeURI
-          encodeURIComponent escape eval isFinite isNaN
-          parseFloat parseInt undefined unescape))
-"Ecma-262 externs.  Never highlighted as undeclared variables.")
-
-(defvar js2-browser-externs
-  (mapcar 'symbol-name
-          '(;; DOM level 1
-            Attr CDATASection CharacterData Comment DOMException
-            DOMImplementation Document DocumentFragment
-            DocumentType Element Entity EntityReference
-            ExceptionCode NamedNodeMap Node NodeList Notation
-            ProcessingInstruction Text
-
-            ;; DOM level 2
-            HTMLAnchorElement HTMLAppletElement HTMLAreaElement
-            HTMLBRElement HTMLBaseElement HTMLBaseFontElement
-            HTMLBodyElement HTMLButtonElement HTMLCollection
-            HTMLDListElement HTMLDirectoryElement HTMLDivElement
-            HTMLDocument HTMLElement HTMLFieldSetElement
-            HTMLFontElement HTMLFormElement HTMLFrameElement
-            HTMLFrameSetElement HTMLHRElement HTMLHeadElement
-            HTMLHeadingElement HTMLHtmlElement HTMLIFrameElement
-            HTMLImageElement HTMLInputElement HTMLIsIndexElement
-            HTMLLIElement HTMLLabelElement HTMLLegendElement
-            HTMLLinkElement HTMLMapElement HTMLMenuElement
-            HTMLMetaElement HTMLModElement HTMLOListElement
-            HTMLObjectElement HTMLOptGroupElement
-            HTMLOptionElement HTMLOptionsCollection
-            HTMLParagraphElement HTMLParamElement HTMLPreElement
-            HTMLQuoteElement HTMLScriptElement HTMLSelectElement
-            HTMLStyleElement HTMLTableCaptionElement
-            HTMLTableCellElement HTMLTableColElement
-            HTMLTableElement HTMLTableRowElement
-            HTMLTableSectionElement HTMLTextAreaElement
-            HTMLTitleElement HTMLUListElement
-
-            ;; DOM level 3
-            DOMConfiguration DOMError DOMException
-            DOMImplementationList DOMImplementationSource
-            DOMLocator DOMStringList NameList TypeInfo
-            UserDataHandler
-
-            ;; Window
-            window alert confirm document java navigator prompt screen
-            self top requestAnimationFrame cancelAnimationFrame
-
-            ;; W3C CSS
-            CSSCharsetRule CSSFontFace CSSFontFaceRule
-            CSSImportRule CSSMediaRule CSSPageRule
-            CSSPrimitiveValue CSSProperties CSSRule CSSRuleList
-            CSSStyleDeclaration CSSStyleRule CSSStyleSheet
-            CSSValue CSSValueList Counter DOMImplementationCSS
-            DocumentCSS DocumentStyle ElementCSSInlineStyle
-            LinkStyle MediaList RGBColor Rect StyleSheet
-            StyleSheetList ViewCSS
-
-            ;; W3C Event
-            EventListener EventTarget Event DocumentEvent UIEvent
-            MouseEvent MutationEvent KeyboardEvent
-
-            ;; W3C Range
-            DocumentRange Range RangeException
-
-            ;; W3C XML
-            XPathResult XMLHttpRequest
-
-            ;; console object.  Provided by at least Chrome and Firefox.
-            console))
-  "Browser externs.
-You can cause these to be included or excluded with the custom
-variable `js2-include-browser-externs'.")
-
-(defvar js2-rhino-externs
-  (mapcar 'symbol-name
-          '(Packages importClass importPackage com org java
-            ;; Global object (shell) externs.
-            defineClass deserialize doctest gc help load
-            loadClass print quit readFile readUrl runCommand seal
-            serialize spawn sync toint32 version))
-  "Mozilla Rhino externs.
-Set `js2-include-rhino-externs' to t to include them.")
-
-(defvar js2-node-externs
-  (mapcar 'symbol-name
-          '(__dirname __filename Buffer clearInterval clearTimeout require
-            console exports global module process setInterval setTimeout
-            querystring setImmediate clearImmediate))
-  "Node.js externs.
-Set `js2-include-node-externs' to t to include them.")
-
-(defvar js2-typed-array-externs
-  (mapcar 'symbol-name
-          '(ArrayBuffer Uint8ClampedArray DataView
-            Int8Array Uint8Array Int16Array Uint16Array Int32Array Uint32Array
-            Float32Array Float64Array))
-  "Khronos typed array externs. Available in most modern browsers and
-in node.js >= 0.6. If `js2-include-node-externs' or `js2-include-browser-externs'
-are enabled, these will also be included.")
-
-(defvar js2-harmony-externs
-  (mapcar 'symbol-name
-          '(Map Promise Proxy Reflect Set Symbol WeakMap WeakSet))
-  "ES6 externs.  If `js2-include-browser-externs' is enabled and
-`js2-language-version' is sufficiently high, these will be included.")
-
-;;; Variables
-
-(defcustom js2-ignored-warnings nil
-  "A list of warning message types that will not be reported.
-
-Possible values are the keys of `js2-message-table'."
-  :group 'js2-mode
-  :type '(repeat string))
-
-(defcustom js2-highlight-level 2
-  "Amount of syntax highlighting to perform.
-0 or a negative value means none.
-1 adds basic syntax highlighting.
-2 adds highlighting of some Ecma built-in properties.
-3 adds highlighting of many Ecma built-in functions."
-  :group 'js2-mode
-  :type '(choice (const :tag "None" 0)
-                 (const :tag "Basic" 1)
-                 (const :tag "Include Properties" 2)
-                 (const :tag "Include Functions" 3)))
-
-(defvar js2-mode-dev-mode-p nil
-  "Non-nil if running in development mode.  Normally nil.")
-
-(defgroup js2-mode nil
-  "An improved JavaScript mode."
-  :group 'languages)
-
-(defcustom js2-idle-timer-delay 0.2
-  "Delay in secs before re-parsing after user makes changes.
-Multiplied by `js2-dynamic-idle-timer-adjust', which see."
-  :type 'number
-  :group 'js2-mode)
-(make-variable-buffer-local 'js2-idle-timer-delay)
-
-(defcustom js2-dynamic-idle-timer-adjust 0
-  "Positive to adjust `js2-idle-timer-delay' based on file size.
-The idea is that for short files, parsing is faster so we can be
-more responsive to user edits without interfering with editing.
-The buffer length in characters (typically bytes) is divided by
-this value and used to multiply `js2-idle-timer-delay' for the
-buffer.  For example, a 21k file and 10k adjust yields 21k/10k
-== 2, so js2-idle-timer-delay is multiplied by 2.
-If `js2-dynamic-idle-timer-adjust' is 0 or negative,
-`js2-idle-timer-delay' is not dependent on the file size."
-  :type 'number
-  :group 'js2-mode)
-
-(defcustom js2-concat-multiline-strings t
-  "When non-nil, `js2-line-break' in mid-string will make it a
-string concatenation. When `eol', the '+' will be inserted at the
-end of the line, otherwise, at the beginning of the next line."
-  :type '(choice (const t) (const eol) (const nil))
-  :group 'js2-mode)
-
-(defcustom js2-mode-show-parse-errors t
-  "True to highlight parse errors."
-  :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,
-even if this flag is non-nil."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-strict-trailing-comma-warning nil
-  "Non-nil to warn about trailing commas in array literals.
-Ecma-262-5.1 allows them, but older versions of IE raise an error."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-strict-missing-semi-warning t
-  "Non-nil to warn about semicolon auto-insertion after statement.
-Technically this is legal per Ecma-262, but some style guides disallow
-depending on it."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-missing-semi-one-line-override nil
-  "Non-nil to permit missing semicolons in one-line functions.
-In one-liner functions such as `function identity(x) {return x}'
-people often omit the semicolon for a cleaner look.  If you are
-such a person, you can suppress the missing-semicolon warning
-by setting this variable to t."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-strict-inconsistent-return-warning t
-  "Non-nil to warn about mixing returns with value-returns.
-It's perfectly legal to have a `return' and a `return foo' in the
-same function, but it's often an indicator of a bug, and it also
-interferes with type inference (in systems that support it.)"
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-strict-cond-assign-warning t
-  "Non-nil to warn about expressions like if (a = b).
-This often should have been '==' instead of '='.  If the warning
-is enabled, you can suppress it on a per-expression basis by
-parenthesizing the expression, e.g. if ((a = b)) ..."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-strict-var-redeclaration-warning t
-  "Non-nil to warn about redeclaring variables in a script or function."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-strict-var-hides-function-arg-warning t
-  "Non-nil to warn about a var decl hiding a function argument."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-skip-preprocessor-directives nil
-  "Non-nil to treat lines beginning with # as comments.
-Useful for viewing Mozilla JavaScript source code."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-language-version 200
-  "Configures what JavaScript language version to recognize.
-Currently versions 150, 160, 170, 180 and 200 are supported,
-corresponding to JavaScript 1.5, 1.6, 1.7, 1.8 and 2.0 (Harmony),
-respectively.  In a nutshell, 1.6 adds E4X support, 1.7 adds let,
-yield, and Array comprehensions, and 1.8 adds function closures."
-  :type 'integer
-  :group 'js2-mode)
-
-(defcustom js2-instanceof-has-side-effects nil
-  "If non-nil, treats the instanceof operator as having side effects.
-This is useful for xulrunner apps."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-getprop-has-side-effects nil
-  "If non-nil, treats the getprop operator as having side effects.
-This is useful for testing libraries with nontrivial getters and for
-compilers that use empty getprops to declare interface properties."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-move-point-on-right-click t
-  "Non-nil to move insertion point when you right-click.
-This makes right-click context menu behavior a bit more intuitive,
-since menu operations generally apply to the point.  The exception
-is if there is a region selection, in which case the point does -not-
-move, so cut/copy/paste can work properly.
-
-Note that IntelliJ moves the point, and Eclipse leaves it alone,
-so this behavior is customizable."
-  :group 'js2-mode
-  :type 'boolean)
-
-(defcustom js2-allow-rhino-new-expr-initializer t
-  "Non-nil to support a Rhino's experimental syntactic construct.
-
-Rhino supports the ability to follow a `new' expression with an object
-literal, which is used to set additional properties on the new object
-after calling its constructor.  Syntax:
-
-  new <expr> [ ( arglist ) ] [initializer]
-
-Hence, this expression:
-
-  new Object {a: 1, b: 2}
-
-results in an Object with properties a=1 and b=2.  This syntax is
-apparently not configurable in Rhino - it's currently always enabled,
-as of Rhino version 1.7R2."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-allow-member-expr-as-function-name nil
-  "Non-nil to support experimental Rhino syntax for function names.
-
-Rhino supports an experimental syntax configured via the Rhino Context
-setting `allowMemberExprAsFunctionName'.  The experimental syntax is:
-
-  function <member-expr> ( [ arg-list ] ) { <body> }
-
-Where member-expr is a non-parenthesized 'member expression', which
-is anything at the grammar level of a new-expression or lower, meaning
-any expression that does not involve infix or unary operators.
-
-When <member-expr> is not a simple identifier, then it is syntactic
-sugar for assigning the anonymous function to the <member-expr>.  Hence,
-this code:
-
-  function a.b().c[2] (x, y) { ... }
-
-is rewritten as:
-
-  a.b().c[2] = function(x, y) {...}
-
-which doesn't seem particularly useful, but Rhino permits it."
-  :type 'boolean
-  :group 'js2-mode)
-
-;; scanner variables
-
-(defmacro js2-deflocal (name value &optional comment)
-  "Define a buffer-local variable NAME with VALUE and COMMENT."
-  (declare (debug defvar) (doc-string 3))
-  `(progn
-     (defvar ,name ,value ,comment)
-     (make-variable-buffer-local ',name)))
-
-(defvar js2-EOF_CHAR -1
-  "Represents end of stream.  Distinct from js2-EOF token type.")
-
-;; I originally used symbols to represent tokens, but Rhino uses
-;; ints and then sets various flag bits in them, so ints it is.
-;; The upshot is that we need a `js2-' prefix in front of each name.
-(defvar js2-ERROR -1)
-(defvar js2-EOF 0)
-(defvar js2-EOL 1)
-(defvar js2-ENTERWITH 2)       ; begin interpreter bytecodes
-(defvar js2-LEAVEWITH 3)
-(defvar js2-RETURN 4)
-(defvar js2-GOTO 5)
-(defvar js2-IFEQ 6)
-(defvar js2-IFNE 7)
-(defvar js2-SETNAME 8)
-(defvar js2-BITOR 9)
-(defvar js2-BITXOR 10)
-(defvar js2-BITAND 11)
-(defvar js2-EQ 12)
-(defvar js2-NE 13)
-(defvar js2-LT 14)
-(defvar js2-LE 15)
-(defvar js2-GT 16)
-(defvar js2-GE 17)
-(defvar js2-LSH 18)
-(defvar js2-RSH 19)
-(defvar js2-URSH 20)
-(defvar js2-ADD 21)            ; infix plus
-(defvar js2-SUB 22)            ; infix minus
-(defvar js2-MUL 23)
-(defvar js2-DIV 24)
-(defvar js2-MOD 25)
-(defvar js2-NOT 26)
-(defvar js2-BITNOT 27)
-(defvar js2-POS 28)            ; unary plus
-(defvar js2-NEG 29)            ; unary minus
-(defvar js2-NEW 30)
-(defvar js2-DELPROP 31)
-(defvar js2-TYPEOF 32)
-(defvar js2-GETPROP 33)
-(defvar js2-GETPROPNOWARN 34)
-(defvar js2-SETPROP 35)
-(defvar js2-GETELEM 36)
-(defvar js2-SETELEM 37)
-(defvar js2-CALL 38)
-(defvar js2-NAME 39)           ; an identifier
-(defvar js2-NUMBER 40)
-(defvar js2-STRING 41)
-(defvar js2-NULL 42)
-(defvar js2-THIS 43)
-(defvar js2-FALSE 44)
-(defvar js2-TRUE 45)
-(defvar js2-SHEQ 46)           ; shallow equality (===)
-(defvar js2-SHNE 47)           ; shallow inequality (!==)
-(defvar js2-REGEXP 48)
-(defvar js2-BINDNAME 49)
-(defvar js2-THROW 50)
-(defvar js2-RETHROW 51)        ; rethrow caught exception: catch (e if ) uses it
-(defvar js2-IN 52)
-(defvar js2-INSTANCEOF 53)
-(defvar js2-LOCAL_LOAD 54)
-(defvar js2-GETVAR 55)
-(defvar js2-SETVAR 56)
-(defvar js2-CATCH_SCOPE 57)
-(defvar js2-ENUM_INIT_KEYS 58) ; FIXME: what are these?
-(defvar js2-ENUM_INIT_VALUES 59)
-(defvar js2-ENUM_INIT_ARRAY 60)
-(defvar js2-ENUM_NEXT 61)
-(defvar js2-ENUM_ID 62)
-(defvar js2-THISFN 63)
-(defvar js2-RETURN_RESULT 64)  ; to return previously stored return result
-(defvar js2-ARRAYLIT 65)       ; array literal
-(defvar js2-OBJECTLIT 66)      ; object literal
-(defvar js2-GET_REF 67)        ; *reference
-(defvar js2-SET_REF 68)        ; *reference = something
-(defvar js2-DEL_REF 69)        ; delete reference
-(defvar js2-REF_CALL 70)       ; f(args) = something or f(args)++
-(defvar js2-REF_SPECIAL 71)    ; reference for special properties like __proto
-(defvar js2-YIELD 72)          ; JS 1.7 yield pseudo keyword
-
-;; XML support
-(defvar js2-DEFAULTNAMESPACE 73)
-(defvar js2-ESCXMLATTR 74)
-(defvar js2-ESCXMLTEXT 75)
-(defvar js2-REF_MEMBER 76)     ; Reference for x.@y, x..y etc.
-(defvar js2-REF_NS_MEMBER 77)  ; Reference for x.ns::y, x..ns::y etc.
-(defvar js2-REF_NAME 78)       ; Reference for @y, @[y] etc.
-(defvar js2-REF_NS_NAME 79)    ; Reference for ns::y, @ns::y@[y] etc.
-
-(defvar js2-first-bytecode js2-ENTERWITH)
-(defvar js2-last-bytecode js2-REF_NS_NAME)
-
-(defvar js2-TRY 80)
-(defvar js2-SEMI 81)           ; semicolon
-(defvar js2-LB 82)             ; left and right brackets
-(defvar js2-RB 83)
-(defvar js2-LC 84)             ; left and right curly-braces
-(defvar js2-RC 85)
-(defvar js2-LP 86)             ; left and right parens
-(defvar js2-RP 87)
-(defvar js2-COMMA 88)          ; comma operator
-
-(defvar js2-ASSIGN 89)         ; simple assignment (=)
-(defvar js2-ASSIGN_BITOR 90)   ; |=
-(defvar js2-ASSIGN_BITXOR 91)  ; ^=
-(defvar js2-ASSIGN_BITAND 92)  ; &=
-(defvar js2-ASSIGN_LSH 93)     ; <<=
-(defvar js2-ASSIGN_RSH 94)     ; >>=
-(defvar js2-ASSIGN_URSH 95)    ; >>>=
-(defvar js2-ASSIGN_ADD 96)     ; +=
-(defvar js2-ASSIGN_SUB 97)     ; -=
-(defvar js2-ASSIGN_MUL 98)     ; *=
-(defvar js2-ASSIGN_DIV 99)     ; /=
-(defvar js2-ASSIGN_MOD 100)    ; %=
-(defvar js2-ASSIGN_EXPON 101)
-
-(defvar js2-first-assign js2-ASSIGN)
-(defvar js2-last-assign js2-ASSIGN_EXPON)
-
-(defvar js2-COLON 102)
-(defvar js2-OR 103)            ; logical or (||)
-(defvar js2-AND 104)           ; logical and (&&)
-(defvar js2-INC 105)           ; increment/decrement (++ --)
-(defvar js2-DEC 106)
-(defvar js2-DOT 107)           ; member operator (.)
-(defvar js2-FUNCTION 108)      ; function keyword
-(defvar js2-EXPORT 109)        ; export keyword
-(defvar js2-IMPORT 110)        ; import keyword
-(defvar js2-IF 111)            ; if keyword
-(defvar js2-ELSE 112)          ; else keyword
-(defvar js2-SWITCH 113)        ; switch keyword
-(defvar js2-CASE 114)          ; case keyword
-(defvar js2-DEFAULT 115)       ; default keyword
-(defvar js2-WHILE 116)         ; while keyword
-(defvar js2-DO 117)            ; do keyword
-(defvar js2-FOR 118)           ; for keyword
-(defvar js2-BREAK 119)         ; break keyword
-(defvar js2-CONTINUE 120)      ; continue keyword
-(defvar js2-VAR 121)           ; var keyword
-(defvar js2-WITH 122)          ; with keyword
-(defvar js2-CATCH 123)         ; catch keyword
-(defvar js2-FINALLY 124)       ; finally keyword
-(defvar js2-VOID 125)          ; void keyword
-(defvar js2-RESERVED 126)      ; reserved keywords
-
-(defvar js2-EMPTY 127)
-
-;; Types used for the parse tree - never returned by scanner.
-
-(defvar js2-BLOCK 128)         ; statement block
-(defvar js2-LABEL 129)         ; label
-(defvar js2-TARGET 130)
-(defvar js2-LOOP 131)
-(defvar js2-EXPR_VOID 132)     ; expression statement in functions
-(defvar js2-EXPR_RESULT 133)   ; expression statement in scripts
-(defvar js2-JSR 134)
-(defvar js2-SCRIPT 135)        ; top-level node for entire script
-(defvar js2-TYPEOFNAME 136)    ; for typeof(simple-name)
-(defvar js2-USE_STACK 137)
-(defvar js2-SETPROP_OP 138)    ; x.y op= something
-(defvar js2-SETELEM_OP 139)    ; x[y] op= something
-(defvar js2-LOCAL_BLOCK 140)
-(defvar js2-SET_REF_OP 141)    ; *reference op= something
-
-;; For XML support:
-(defvar js2-DOTDOT 142)        ; member operator (..)
-(defvar js2-COLONCOLON 143)    ; namespace::name
-(defvar js2-XML 144)           ; XML type
-(defvar js2-DOTQUERY 145)      ; .() -- e.g., x.emps.emp.(name == "terry")
-(defvar js2-XMLATTR 146)       ; @
-(defvar js2-XMLEND 147)
-
-;; Optimizer-only tokens
-(defvar js2-TO_OBJECT 148)
-(defvar js2-TO_DOUBLE 149)
-
-(defvar js2-GET 150)           ; JS 1.5 get pseudo keyword
-(defvar js2-SET 151)           ; JS 1.5 set pseudo keyword
-(defvar js2-LET 152)           ; JS 1.7 let pseudo keyword
-(defvar js2-CONST 153)
-(defvar js2-SETCONST 154)
-(defvar js2-SETCONSTVAR 155)
-(defvar js2-ARRAYCOMP 156)
-(defvar js2-LETEXPR 157)
-(defvar js2-WITHEXPR 158)
-(defvar js2-DEBUGGER 159)
-
-(defvar js2-COMMENT 160)
-(defvar js2-TRIPLEDOT 161)     ; for rest parameter
-(defvar js2-ARROW 162)         ; function arrow (=>)
-(defvar js2-CLASS 163)
-(defvar js2-EXTENDS 164)
-(defvar js2-SUPER 165)
-(defvar js2-TEMPLATE_HEAD 166)    ; part of template literal before substitution
-(defvar js2-NO_SUBS_TEMPLATE 167) ; template literal without substitutions
-(defvar js2-TAGGED_TEMPLATE 168)  ; tagged template literal
-
-(defvar js2-AWAIT 169)  ; await (pseudo keyword)
-
-(defvar js2-HOOK 170)          ; conditional (?:)
-(defvar js2-EXPON 171)
-
-(defconst js2-num-tokens (1+ js2-EXPON))
-
-(defconst js2-debug-print-trees nil)
-
-;; Rhino accepts any string or stream as input.  Emacs character
-;; processing works best in buffers, so we'll assume the input is a
-;; buffer.  JavaScript strings can be copied into temp buffers before
-;; scanning them.
-
-;; Buffer-local variables yield much cleaner code than using `defstruct'.
-;; They're the Emacs equivalent of instance variables, more or less.
-
-(js2-deflocal js2-ts-dirty-line nil
-  "Token stream buffer-local variable.
-Indicates stuff other than whitespace since start of line.")
-
-(js2-deflocal js2-ts-hit-eof nil
-  "Token stream buffer-local variable.")
-
-;; FIXME: Unused.
-(js2-deflocal js2-ts-line-start 0
-  "Token stream buffer-local variable.")
-
-(js2-deflocal js2-ts-lineno 1
-  "Token stream buffer-local variable.")
-
-;; FIXME: Unused.
-(js2-deflocal js2-ts-line-end-char -1
-  "Token stream buffer-local variable.")
-
-(js2-deflocal js2-ts-cursor 1  ; emacs buffers are 1-indexed
-  "Token stream buffer-local variable.
-Current scan position.")
-
-;; FIXME: Unused.
-(js2-deflocal js2-ts-is-xml-attribute nil
-  "Token stream buffer-local variable.")
-
-(js2-deflocal js2-ts-xml-is-tag-content nil
-  "Token stream buffer-local variable.")
-
-(js2-deflocal js2-ts-xml-open-tags-count 0
-  "Token stream buffer-local variable.")
-
-(js2-deflocal js2-ts-string-buffer nil
-  "Token stream buffer-local variable.
-List of chars built up while scanning various tokens.")
-
-(cl-defstruct (js2-token
-               (:constructor make-js2-token (beg)))
-  "Value returned from the token stream."
-  (type js2-EOF)
-  (beg 1)
-  (end -1)
-  (string "")
-  number
-  number-base
-  number-legacy-octal-p
-  regexp-flags
-  comment-type
-  follows-eol-p)
-
-;; Have to call `js2-init-scanner' to initialize the values.
-(js2-deflocal js2-ti-tokens nil)
-(js2-deflocal js2-ti-tokens-cursor nil)
-(js2-deflocal js2-ti-lookahead nil)
-
-(cl-defstruct (js2-ts-state
-               (:constructor make-js2-ts-state (&key (lineno js2-ts-lineno)
-                                                     (cursor js2-ts-cursor)
-                                                     (tokens (copy-sequence js2-ti-tokens))
-                                                     (tokens-cursor js2-ti-tokens-cursor)
-                                                     (lookahead js2-ti-lookahead))))
-  lineno
-  cursor
-  tokens
-  tokens-cursor
-  lookahead)
-
-;;; Parser variables
-
-(js2-deflocal js2-parsed-errors nil
-  "List of errors produced during scanning/parsing.")
-
-(js2-deflocal js2-parsed-warnings nil
-  "List of warnings produced during scanning/parsing.")
-
-(js2-deflocal js2-recover-from-parse-errors t
-  "Non-nil to continue parsing after a syntax error.
-
-In recovery mode, the AST will be built in full, and any error
-nodes will be flagged with appropriate error information.  If
-this flag is nil, a syntax error will result in an error being
-signaled.
-
-The variable is automatically buffer-local, because different
-modes that use the parser will need different settings.")
-
-(js2-deflocal js2-parse-hook nil
-  "List of callbacks for receiving parsing progress.")
-
-(defvar js2-parse-finished-hook nil
-  "List of callbacks to notify when parsing finishes.
-Not called if parsing was interrupted.")
-
-(js2-deflocal js2-is-eval-code nil
-  "True if we're evaluating code in a string.
-If non-nil, the tokenizer will record the token text, and the AST nodes
-will record their source text.  Off by default for IDE modes, since the
-text is available in the buffer.")
-
-(defvar js2-parse-ide-mode t
-  "Non-nil if the parser is being used for `js2-mode'.
-If non-nil, the parser will set text properties for fontification
-and the syntax table.  The value should be nil when using the
-parser as a frontend to an interpreter or byte compiler.")
-
-;;; Parser instance variables (buffer-local vars for js2-parse)
-
-(defconst js2-ti-after-eol (lsh 1 16)
-  "Flag:  first token of the source line.")
-
-;; Inline Rhino's CompilerEnvirons vars as buffer-locals.
-
-(js2-deflocal js2-compiler-generate-debug-info t)
-(js2-deflocal js2-compiler-use-dynamic-scope nil)
-(js2-deflocal js2-compiler-reserved-keywords-as-identifier nil)
-(js2-deflocal js2-compiler-xml-available t)
-(js2-deflocal js2-compiler-optimization-level 0)
-(js2-deflocal js2-compiler-generating-source t)
-(js2-deflocal js2-compiler-strict-mode nil)
-(js2-deflocal js2-compiler-report-warning-as-error nil)
-(js2-deflocal js2-compiler-generate-observer-count nil)
-(js2-deflocal js2-compiler-activation-names nil)
-
-;; SKIP:  sourceURI
-
-;; There's a compileFunction method in Context.java - may need it.
-(js2-deflocal js2-called-by-compile-function nil
-  "True if `js2-parse' was called by `js2-compile-function'.
-Will only be used when we finish implementing the interpreter.")
-
-;; SKIP:  ts  (we just call `js2-init-scanner' and use its vars)
-
-;; SKIP:  node factory - we're going to just call functions directly,
-;; and eventually go to a unified AST format.
-
-(js2-deflocal js2-nesting-of-function 0)
-
-(js2-deflocal js2-recorded-identifiers nil
-  "Tracks identifiers found during parsing.")
-
-(js2-deflocal js2-is-in-destructuring nil
-  "True while parsing destructuring expression.")
-
-(js2-deflocal js2-in-use-strict-directive nil
-  "True while inside a script or function under strict mode.")
-
-(defcustom js2-global-externs nil
-  "A list of any extern names you'd like to consider always declared.
-This list is global and is used by all `js2-mode' files.
-You can create buffer-local externs list using `js2-additional-externs'."
-  :type 'list
-  :group 'js2-mode)
-
-(defcustom js2-include-browser-externs t
-  "Non-nil to include browser externs in the master externs list.
-If you work on JavaScript files that are not intended for browsers,
-such as Mozilla Rhino server-side JavaScript, set this to nil.
-See `js2-additional-externs' for more information about externs."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-include-rhino-externs nil
-  "Non-nil to include Mozilla Rhino externs in the master externs list.
-See `js2-additional-externs' for more information about externs."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-include-node-externs nil
-  "Non-nil to include Node.js externs in the master externs list.
-See `js2-additional-externs' for more information about externs."
-  :type 'boolean
-  :group 'js2-mode)
-
-(js2-deflocal js2-additional-externs nil
-  "A buffer-local list of additional external declarations.
-It is used to decide whether variables are considered undeclared
-for purposes of highlighting.  See `js2-highlight-undeclared-vars'.
-
-Each entry is a Lisp string.  The string should be the fully qualified
-name of an external entity.  All externs should be added to this list,
-so that as js2-mode's processing improves it can take advantage of them.
-
-You may want to declare your externs in three ways.
-First, you can add externs that are valid for all your JavaScript files.
-You should probably do this by adding them to `js2-global-externs', which
-is a global list used for all js2-mode files.
-
-Next, you can add a function to `js2-init-hook' that adds additional
-externs appropriate for the specific file, perhaps based on its path.
-These should go in `js2-additional-externs', which is buffer-local.
-
-Third, you can use JSLint's global declaration, as long as
-`js2-include-jslint-globals' is non-nil, which see.
-
-Finally, you can add a function to `js2-post-parse-callbacks',
-which is called after parsing completes, and `js2-mode-ast' is bound to
-the root of the parse tree.  At this stage you can set up an AST
-node visitor using `js2-visit-ast' and examine the parse tree
-for specific import patterns that may imply the existence of
-other externs, possibly tied to your build system.  These should also
-be added to `js2-additional-externs'.
-
-Your post-parse callback may of course also use the simpler and
-faster (but perhaps less robust) approach of simply scanning the
-buffer text for your imports, using regular expressions.")
-
-(put 'js2-additional-externs 'safe-local-variable
-     (lambda (val) (cl-every #'stringp val)))
-
-;; SKIP:  decompiler
-;; SKIP:  encoded-source
-
-;;; The following variables are per-function and should be saved/restored
-;;; during function parsing...
-
-(js2-deflocal js2-current-script-or-fn nil)
-(js2-deflocal js2-current-scope nil)
-(js2-deflocal js2-nesting-of-with 0)
-(js2-deflocal js2-label-set nil
-  "An alist mapping label names to nodes.")
-
-(js2-deflocal js2-loop-set nil)
-(js2-deflocal js2-loop-and-switch-set nil)
-(js2-deflocal js2-has-return-value nil)
-(js2-deflocal js2-end-flags 0)
-
-;;; ...end of per function variables
-
-;; These flags enumerate the possible ways a statement/function can
-;; terminate. These flags are used by endCheck() and by the Parser to
-;; detect inconsistent return usage.
-;;
-;; END_UNREACHED is reserved for code paths that are assumed to always be
-;; able to execute (example: throw, continue)
-;;
-;; END_DROPS_OFF indicates if the statement can transfer control to the
-;; next one. Statement such as return dont. A compound statement may have
-;; some branch that drops off control to the next statement.
-;;
-;; END_RETURNS indicates that the statement can return (without arguments)
-;; END_RETURNS_VALUE indicates that the statement can return a value.
-;;
-;; A compound statement such as
-;; if (condition) {
-;;   return value;
-;; }
-;; Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
-
-(defconst js2-end-unreached     #x0)
-(defconst js2-end-drops-off     #x1)
-(defconst js2-end-returns       #x2)
-(defconst js2-end-returns-value #x4)
-
-;; Rhino awkwardly passes a statementLabel parameter to the
-;; statementHelper() function, the main statement parser, which
-;; is then used by quite a few of the sub-parsers.  We just make
-;; it a buffer-local variable and make sure it's cleaned up properly.
-(js2-deflocal js2-labeled-stmt nil)  ; type `js2-labeled-stmt-node'
-
-;; Similarly, Rhino passes an inForInit boolean through about half
-;; the expression parsers.  We use a dynamically-scoped variable,
-;; which makes it easier to funcall the parsers individually without
-;; worrying about whether they take the parameter or not.
-(js2-deflocal js2-in-for-init nil)
-(js2-deflocal js2-temp-name-counter 0)
-(js2-deflocal js2-parse-stmt-count 0)
-
-(defsubst js2-get-next-temp-name ()
-  (format "$%d" (cl-incf js2-temp-name-counter)))
-
-(defvar js2-parse-interruptable-p t
-  "Set this to nil to force parse to continue until finished.
-This will mostly be useful for interpreters.")
-
-(defvar js2-statements-per-pause 50
-  "Pause after this many statements to check for user input.
-If user input is pending, stop the parse and discard the tree.
-This makes for a smoother user experience for large files.
-You may have to wait a second or two before the highlighting
-and error-reporting appear, but you can always type ahead if
-you wish.  This appears to be more or less how Eclipse, IntelliJ
-and other editors work.")
-
-(js2-deflocal js2-record-comments t
-  "Instructs the scanner to record comments in `js2-scanned-comments'.")
-
-(js2-deflocal js2-scanned-comments nil
-  "List of all comments from the current parse.")
-
-(defcustom js2-mode-indent-inhibit-undo nil
-  "Non-nil to disable collection of Undo information when indenting lines.
-Some users have requested this behavior.  It's nil by default because
-other Emacs modes don't work this way."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-mode-indent-ignore-first-tab nil
-  "If non-nil, ignore first TAB keypress if we look indented properly.
-It's fairly common for users to navigate to an already-indented line
-and press TAB for reassurance that it's been indented.  For this class
-of users, we want the first TAB press on a line to be ignored if the
-line is already indented to one of the precomputed alternatives.
-
-This behavior is only partly implemented.  If you TAB-indent a line,
-navigate to another line, and then navigate back, it fails to clear
-the last-indented variable, so it thinks you've already hit TAB once,
-and performs the indent.  A full solution would involve getting on the
-point-motion hooks for the entire buffer.  If we come across another
-use cases that requires watching point motion, I'll consider doing it.
-
-If you set this variable to nil, then the TAB key will always change
-the indentation of the current line, if more than one alternative
-indentation spot exists."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defvar js2-indent-hook nil
-  "A hook for user-defined indentation rules.
-
-Functions on this hook should expect two arguments:    (LIST INDEX)
-The LIST argument is the list of computed indentation points for
-the current line.  INDEX is the list index of the indentation point
-that `js2-bounce-indent' plans to use.  If INDEX is nil, then the
-indent function is not going to change the current line indentation.
-
-If a hook function on this list returns a non-nil value, then
-`js2-bounce-indent' assumes the hook function has performed its own
-indentation, and will do nothing.  If all hook functions on the list
-return nil, then `js2-bounce-indent' will use its computed indentation
-and reindent the line.
-
-When hook functions on this hook list are called, the variable
-`js2-mode-ast' may or may not be set, depending on whether the
-parse tree is available.  If the variable is nil, you can pass a
-callback to `js2-mode-wait-for-parse', and your callback will be
-called after the new parse tree is built.  This can take some time
-in large files.")
-
-(defface js2-warning
-  `((((class color) (background light))
-     (:underline  "orange"))
-    (((class color) (background dark))
-     (:underline "orange"))
-    (t (:underline t)))
-  "Face for JavaScript warnings."
-  :group 'js2-mode)
-
-(defface js2-error
-  `((((class color) (background light))
-     (:foreground "red"))
-    (((class color) (background dark))
-     (:foreground "red"))
-    (t (:foreground "red")))
-  "Face for JavaScript errors."
-  :group 'js2-mode)
-
-(defface js2-jsdoc-tag
-  '((t :foreground "SlateGray"))
-  "Face used to highlight @whatever tags in jsdoc comments."
-  :group 'js2-mode)
-
-(defface js2-jsdoc-type
-  '((t :foreground "SteelBlue"))
-  "Face used to highlight {FooBar} types in jsdoc comments."
-  :group 'js2-mode)
-
-(defface js2-jsdoc-value
-  '((t :foreground "PeachPuff3"))
-  "Face used to highlight tag values in jsdoc comments."
-  :group 'js2-mode)
-
-(defface js2-function-param
-  '((t :foreground "SeaGreen"))
-  "Face used to highlight function parameters in javascript."
-  :group 'js2-mode)
-
-(defface js2-function-call
-  '((t :inherit default))
-  "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-object-property-access
-  '((t :inherit js2-object-property))
-  "Face used to highlight property access with dot on an object."
-  :group 'js2-mode)
-
-(defface js2-instance-member
-  '((t :foreground "DarkOrchid"))
-  "Face used to highlight instance variables in javascript.
-Not currently used."
-  :group 'js2-mode)
-
-(defface js2-private-member
-  '((t :foreground "PeachPuff3"))
-  "Face used to highlight calls to private methods in javascript.
-Not currently used."
-  :group 'js2-mode)
-
-(defface js2-private-function-call
-  '((t :foreground "goldenrod"))
-  "Face used to highlight calls to private functions in javascript.
-Not currently used."
-  :group 'js2-mode)
-
-(defface js2-jsdoc-html-tag-name
-  '((((class color) (min-colors 88) (background light))
-     (:foreground "rosybrown"))
-    (((class color) (min-colors 8) (background dark))
-     (:foreground "yellow"))
-    (((class color) (min-colors 8) (background light))
-     (:foreground "magenta")))
-    "Face used to highlight jsdoc html tag names"
-  :group 'js2-mode)
-
-(defface js2-jsdoc-html-tag-delimiter
-  '((((class color) (min-colors 88) (background light))
-     (:foreground "dark khaki"))
-    (((class color) (min-colors 8) (background dark))
-     (:foreground "green"))
-    (((class color) (min-colors 8) (background light))
-     (:foreground "green")))
-  "Face used to highlight brackets in jsdoc html tags."
-  :group 'js2-mode)
-
-(defface js2-external-variable
-  '((t :foreground "orange"))
-  "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."
-  :type 'hook
-  :group 'js2-mode
-  :version "20130608")
-
-(defcustom js2-post-parse-callbacks nil
-  "List of callback functions invoked after parsing finishes.
-Currently, the main use for this function is to add synthetic
-declarations to `js2-recorded-identifiers', which see."
-  :type 'hook
-  :group 'js2-mode)
-
-(defcustom js2-build-imenu-callbacks nil
-  "List of functions called during Imenu index generation.
-It's a good place to add additional entries to it, using
-`js2-record-imenu-entry'."
-  :type 'hook
-  :group 'js2-mode)
-
-(defcustom js2-highlight-external-variables t
-  "Non-nil to highlight undeclared variable identifiers.
-An undeclared variable is any variable not declared with var or let
-in the current scope or any lexically enclosing scope.  If you use
-such a variable, then you are either expecting it to originate from
-another file, or you've got a potential bug."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-warn-about-unused-function-arguments nil
-  "Non-nil to treat function arguments like declared-but-unused variables."
-  :type 'booleanp
-  :group 'js2-mode)
-
-(defcustom js2-include-jslint-globals t
-  "Non-nil to include the identifiers from JSLint global
-declaration (see http://www.jslint.com/help.html#global) in the
-buffer-local externs list.  See `js2-additional-externs' for more
-information."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defcustom js2-include-jslint-declaration-externs t
-  "Non-nil to include the identifiers JSLint assumes to be there
-under certain declarations in the buffer-local externs list.  See
-`js2-additional-externs' for more information."
-  :type 'boolean
-  :group 'js2-mode)
-
-(defvar js2-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [remap indent-new-comment-line] #'js2-line-break)
-    (define-key map (kbd "C-c C-e") #'js2-mode-hide-element)
-    (define-key map (kbd "C-c C-s") #'js2-mode-show-element)
-    (define-key map (kbd "C-c C-a") #'js2-mode-show-all)
-    (define-key map (kbd "C-c C-f") #'js2-mode-toggle-hide-functions)
-    (define-key map (kbd "C-c C-t") #'js2-mode-toggle-hide-comments)
-    (define-key map (kbd "C-c C-o") #'js2-mode-toggle-element)
-    (define-key map (kbd "C-c C-w") #'js2-mode-toggle-warnings-and-errors)
-    (define-key map [down-mouse-3] #'js2-down-mouse-3)
-    (define-key map [remap js-find-symbol] #'js2-jump-to-definition)
-
-    (define-key map [menu-bar javascript]
-      (cons "JavaScript" (make-sparse-keymap "JavaScript")))
-
-    (define-key map [menu-bar javascript customize-js2-mode]
-      '(menu-item "Customize js2-mode" js2-mode-customize
-                  :help "Customize the behavior of this mode"))
-
-    (define-key map [menu-bar javascript js2-force-refresh]
-      '(menu-item "Force buffer refresh" js2-mode-reset
-                  :help "Re-parse the buffer from scratch"))
-
-    (define-key map [menu-bar javascript separator-2]
-      '("--"))
-
-    (define-key map [menu-bar javascript next-error]
-      '(menu-item "Next warning or error" next-error
-                  :enabled (and js2-mode-ast
-                                (or (js2-ast-root-errors js2-mode-ast)
-                                    (js2-ast-root-warnings js2-mode-ast)))
-                  :help "Move to next warning or error"))
-
-    (define-key map [menu-bar javascript display-errors]
-      '(menu-item "Show errors and warnings" js2-mode-display-warnings-and-errors
-                  :visible (not js2-mode-show-parse-errors)
-                  :help "Turn on display of warnings and errors"))
-
-    (define-key map [menu-bar javascript hide-errors]
-      '(menu-item "Hide errors and warnings" js2-mode-hide-warnings-and-errors
-                  :visible js2-mode-show-parse-errors
-                  :help "Turn off display of warnings and errors"))
-
-    (define-key map [menu-bar javascript separator-1]
-      '("--"))
-
-    (define-key map [menu-bar javascript js2-toggle-function]
-      '(menu-item "Show/collapse element" js2-mode-toggle-element
-                  :help "Hide or show function body or comment"))
-
-    (define-key map [menu-bar javascript show-comments]
-      '(menu-item "Show block comments" js2-mode-toggle-hide-comments
-                  :visible js2-mode-comments-hidden
-                  :help "Expand all hidden block comments"))
-
-    (define-key map [menu-bar javascript hide-comments]
-      '(menu-item "Hide block comments" js2-mode-toggle-hide-comments
-                  :visible (not js2-mode-comments-hidden)
-                  :help "Show block comments as /*...*/"))
-
-    (define-key map [menu-bar javascript show-all-functions]
-      '(menu-item "Show function bodies" js2-mode-toggle-hide-functions
-                  :visible js2-mode-functions-hidden
-                  :help "Expand all hidden function bodies"))
-
-    (define-key map [menu-bar javascript hide-all-functions]
-      '(menu-item "Hide function bodies" js2-mode-toggle-hide-functions
-                  :visible (not js2-mode-functions-hidden)
-                  :help "Show {...} for all top-level function bodies"))
-
-    map)
-  "Keymap used in `js2-mode' buffers.")
-
-(defcustom js2-bounce-indent-p nil
-  "Non-nil to bind `js2-indent-bounce' and `js2-indent-bounce-backward'.
-They will augment the default indent-line behavior with cycling
-among several computed alternatives.  See the function
-`js2-bounce-indent' for details.  The above commands will be
-bound to TAB and backtab."
-  :type 'boolean
-  :group 'js2-mode
-  :set (lambda (sym value)
-         (set-default sym value)
-         (let ((map js2-mode-map))
-           (if (not value)
-               (progn
-                 (define-key map "\t" nil)
-                 (define-key map (kbd "<backtab>") nil))
-             (define-key map "\t" #'js2-indent-bounce)
-             (define-key map (kbd "<backtab>") #'js2-indent-bounce-backward)))))
-
-(defconst js2-mode-identifier-re "[[:alpha:]_$][[:alnum:]_$]*")
-
-(defvar js2-mode-//-comment-re "^\\(\\s-*\\)//.+"
-  "Matches a //-comment line.  Must be first non-whitespace on line.
-First match-group is the leading whitespace.")
-
-(defvar js2-mode-hook nil)
-
-(js2-deflocal js2-mode-ast nil "Private variable.")
-(js2-deflocal js2-mode-parse-timer nil "Private variable.")
-(js2-deflocal js2-mode-buffer-dirty-p nil "Private variable.")
-(js2-deflocal js2-mode-parsing nil "Private variable.")
-(js2-deflocal js2-mode-node-overlay nil)
-
-(defvar js2-mode-show-overlay js2-mode-dev-mode-p
-  "Debug:  Non-nil to highlight AST nodes on mouse-down.")
-
-(js2-deflocal js2-mode-fontifications nil "Private variable")
-(js2-deflocal js2-mode-deferred-properties nil "Private variable")
-(js2-deflocal js2-imenu-recorder nil "Private variable")
-(js2-deflocal js2-imenu-function-map nil "Private variable")
-
-(defvar js2-mode-verbose-parse-p js2-mode-dev-mode-p
-  "Non-nil to emit status messages during parsing.")
-
-(defvar js2-mode-functions-hidden nil "Private variable.")
-(defvar js2-mode-comments-hidden nil "Private variable.")
-
-(defvar js2-mode-syntax-table
-  (let ((table (make-syntax-table)))
-    (c-populate-syntax-table table)
-    (modify-syntax-entry ?` "\"" table)
-    table)
-  "Syntax table used in `js2-mode' buffers.")
-
-(defvar js2-mode-abbrev-table nil
-  "Abbrev table in use in `js2-mode' buffers.")
-(define-abbrev-table 'js2-mode-abbrev-table ())
-
-(defvar js2-mode-pending-parse-callbacks nil
-  "List of functions waiting to be notified that parse is finished.")
-
-(defvar js2-mode-last-indented-line -1)
-
-;;; Localizable error and warning messages
-
-;; Messages are copied from Rhino's Messages.properties.
-;; Many of the Java-specific messages have been elided.
-;; Add any js2-specific ones at the end, so we can keep
-;; this file synced with changes to Rhino's.
-
-(defvar js2-message-table
-  (make-hash-table :test 'equal :size 250)
-  "Contains localized messages for `js2-mode'.")
-
-;; TODO(stevey):  construct this table at compile-time.
-(defmacro js2-msg (key &rest strings)
-  `(puthash ,key (concat ,@strings)
-            js2-message-table))
-
-(defun js2-get-msg (msg-key)
-  "Look up a localized message.
-MSG-KEY is a list of (MSG ARGS).  If the message takes parameters,
-the correct number of ARGS must be provided."
-  (let* ((key (if (listp msg-key) (car msg-key) msg-key))
-         (args (if (listp msg-key) (cdr msg-key)))
-         (msg (gethash key js2-message-table)))
-    (if msg
-        (apply #'format msg args)
-      key)))  ; default to showing the key
-
-(js2-msg "msg.dup.parms"
-         "Duplicate parameter name '%s'.")
-
-(js2-msg "msg.too.big.jump"
-         "Program too complex: jump offset too big.")
-
-(js2-msg "msg.too.big.index"
-         "Program too complex: internal index exceeds 64K limit.")
-
-(js2-msg "msg.while.compiling.fn"
-         "Encountered code generation error while compiling function '%s': %s")
-
-(js2-msg "msg.while.compiling.script"
-         "Encountered code generation error while compiling script: %s")
-
-;; Context
-(js2-msg "msg.ctor.not.found"
-         "Constructor for '%s' not found.")
-
-(js2-msg "msg.not.ctor"
-         "'%s' is not a constructor.")
-
-;; FunctionObject
-(js2-msg "msg.varargs.ctor"
-         "Method or constructor '%s' must be static "
-         "with the signature (Context cx, Object[] args, "
-         "Function ctorObj, boolean inNewExpr) "
-         "to define a variable arguments constructor.")
-
-(js2-msg "msg.varargs.fun"
-         "Method '%s' must be static with the signature "
-         "(Context cx, Scriptable thisObj, Object[] args, Function funObj) "
-         "to define a variable arguments function.")
-
-(js2-msg "msg.incompat.call"
-         "Method '%s' called on incompatible object.")
-
-(js2-msg "msg.bad.parms"
-         "Unsupported parameter type '%s' in method '%s'.")
-
-(js2-msg "msg.bad.method.return"
-         "Unsupported return type '%s' in method '%s'.")
-
-(js2-msg "msg.bad.ctor.return"
-         "Construction of objects of type '%s' is not supported.")
-
-(js2-msg "msg.no.overload"
-         "Method '%s' occurs multiple times in class '%s'.")
-
-(js2-msg "msg.method.not.found"
-         "Method '%s' not found in '%s'.")
-
-;; IRFactory
-
-(js2-msg "msg.bad.for.in.lhs"
-         "Invalid left-hand side of for..in loop.")
-
-(js2-msg "msg.mult.index"
-         "Only one variable allowed in for..in loop.")
-
-(js2-msg "msg.bad.for.in.destruct"
-         "Left hand side of for..in loop must be an array of "
-         "length 2 to accept key/value pair.")
-
-(js2-msg "msg.cant.convert"
-         "Can't convert to type '%s'.")
-
-(js2-msg "msg.bad.assign.left"
-         "Invalid assignment left-hand side.")
-
-(js2-msg "msg.bad.decr"
-         "Invalid decrement operand.")
-
-(js2-msg "msg.bad.incr"
-         "Invalid increment operand.")
-
-(js2-msg "msg.bad.yield"
-         "yield must be in a function.")
-
-(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 "
-          "function of another name.")
-
-(js2-msg "msg.eval.nonstring"
-          "Calling eval() with anything other than a primitive "
-          "string value will simply return the value. "
-          "Is this what you intended?")
-
-(js2-msg "msg.eval.nonstring.strict"
-         "Calling eval() with anything other than a primitive "
-         "string value is not allowed in strict mode.")
-
-(js2-msg "msg.bad.destruct.op"
-         "Invalid destructuring assignment operator")
-
-;; NativeCall
-(js2-msg "msg.only.from.new"
-         "'%s' may only be invoked from a `new' expression.")
-
-(js2-msg "msg.deprec.ctor"
-         "The '%s' constructor is deprecated.")
-
-;; NativeFunction
-(js2-msg "msg.no.function.ref.found"
-         "no source found to decompile function reference %s")
-
-(js2-msg "msg.arg.isnt.array"
-         "second argument to Function.prototype.apply must be an array")
-
-;; NativeGlobal
-(js2-msg "msg.bad.esc.mask"
-         "invalid string escape mask")
-
-;; NativeRegExp
-(js2-msg "msg.bad.quant"
-  "Invalid quantifier %s")
-
-(js2-msg "msg.overlarge.backref"
-  "Overly large back reference %s")
-
-(js2-msg "msg.overlarge.min"
-  "Overly large minimum %s")
-
-(js2-msg "msg.overlarge.max"
-  "Overly large maximum %s")
-
-(js2-msg "msg.zero.quant"
-  "Zero quantifier %s")
-
-(js2-msg "msg.max.lt.min"
-  "Maximum %s less than minimum")
-
-(js2-msg "msg.unterm.quant"
-  "Unterminated quantifier %s")
-
-(js2-msg "msg.unterm.paren"
-  "Unterminated parenthetical %s")
-
-(js2-msg "msg.unterm.class"
-  "Unterminated character class %s")
-
-(js2-msg "msg.bad.range"
-  "Invalid range in character class.")
-
-(js2-msg "msg.trail.backslash"
-  "Trailing \\ in regular expression.")
-
-(js2-msg "msg.re.unmatched.right.paren"
-  "unmatched ) in regular expression.")
-
-(js2-msg "msg.no.regexp"
-  "Regular expressions are not available.")
-
-(js2-msg "msg.bad.backref"
-  "back-reference exceeds number of capturing parentheses.")
-
-(js2-msg "msg.bad.regexp.compile"
-         "Only one argument may be specified if the first "
-         "argument to RegExp.prototype.compile is a RegExp object.")
-
-;; Parser
-(js2-msg "msg.got.syntax.errors"
-         "Compilation produced %s syntax errors.")
-
-(js2-msg "msg.var.redecl"
-         "Redeclaration of var %s.")
-
-(js2-msg "msg.const.redecl"
-         "TypeError: redeclaration of const %s.")
-
-(js2-msg "msg.let.redecl"
-         "TypeError: redeclaration of variable %s.")
-
-(js2-msg "msg.parm.redecl"
-         "TypeError: redeclaration of formal parameter %s.")
-
-(js2-msg "msg.fn.redecl"
-         "TypeError: redeclaration of function %s.")
-
-(js2-msg "msg.let.decl.not.in.block"
-         "SyntaxError: let declaration not directly within block")
-
-(js2-msg "msg.mod.import.decl.at.top.level"
-         "SyntaxError: import declarations may only appear at the top level")
-
-(js2-msg "msg.mod.as.after.reserved.word"
-         "SyntaxError: missing keyword 'as' after reserved word %s")
-
-(js2-msg "msg.mod.rc.after.import.spec.list"
-         "SyntaxError: missing '}' after module specifier list")
-
-(js2-msg "msg.mod.from.after.import.spec.set"
-         "SyntaxError: missing keyword 'from' after import specifier set")
-
-(js2-msg "msg.mod.declaration.after.import"
-         "SyntaxError: missing declaration after 'import' keyword")
-
-(js2-msg "msg.mod.spec.after.from"
-         "SyntaxError: missing module specifier after 'from' keyword")
-
-(js2-msg "msg.mod.export.decl.at.top.level"
-         "SyntaxError: export declarations may only appear at top level")
-
-(js2-msg "msg.mod.rc.after.export.spec.list"
-         "SyntaxError: missing '}' after export specifier list")
-
-;; NodeTransformer
-(js2-msg "msg.dup.label"
-         "duplicated label")
-
-(js2-msg "msg.undef.label"
-         "undefined label")
-
-(js2-msg "msg.bad.break"
-         "unlabelled break must be inside loop or switch")
-
-(js2-msg "msg.continue.outside"
-         "continue must be inside loop")
-
-(js2-msg "msg.continue.nonloop"
-         "continue can only use labels of iteration statements")
-
-(js2-msg "msg.bad.throw.eol"
-         "Line terminator is not allowed between the throw "
-         "keyword and throw expression.")
-
-(js2-msg "msg.unnamed.function.stmt" ; added by js2-mode
-         "function statement requires a name")
-
-(js2-msg "msg.no.paren.parms"
-         "missing ( before function parameters.")
-
-(js2-msg "msg.no.parm"
-         "missing formal parameter")
-
-(js2-msg "msg.no.paren.after.parms"
-         "missing ) after formal parameters")
-
-(js2-msg "msg.no.default.after.default.param" ; added by js2-mode
-         "parameter without default follows parameter with default")
-
-(js2-msg "msg.param.after.rest" ; added by js2-mode
-         "parameter after rest parameter")
-
-(js2-msg "msg.bad.arrow.args" ; added by js2-mode
-         "invalid arrow-function arguments (parentheses around the arrow-function may help)")
-
-(js2-msg "msg.no.brace.body"
-         "missing '{' before function body")
-
-(js2-msg "msg.no.brace.after.body"
-         "missing } after function body")
-
-(js2-msg "msg.no.paren.cond"
-         "missing ( before condition")
-
-(js2-msg "msg.no.paren.after.cond"
-         "missing ) after condition")
-
-(js2-msg "msg.no.semi.stmt"
-         "missing ; before statement")
-
-(js2-msg "msg.missing.semi"
-         "missing ; after statement")
-
-(js2-msg "msg.no.name.after.dot"
-         "missing name after . operator")
-
-(js2-msg "msg.no.name.after.coloncolon"
-         "missing name after :: operator")
-
-(js2-msg "msg.no.name.after.dotdot"
-         "missing name after .. operator")
-
-(js2-msg "msg.no.name.after.xmlAttr"
-         "missing name after .@")
-
-(js2-msg "msg.no.bracket.index"
-         "missing ] in index expression")
-
-(js2-msg "msg.no.paren.switch"
-         "missing ( before switch expression")
-
-(js2-msg "msg.no.paren.after.switch"
-         "missing ) after switch expression")
-
-(js2-msg "msg.no.brace.switch"
-         "missing '{' before switch body")
-
-(js2-msg "msg.bad.switch"
-         "invalid switch statement")
-
-(js2-msg "msg.no.colon.case"
-         "missing : after case expression")
-
-(js2-msg "msg.double.switch.default"
-         "double default label in the switch statement")
-
-(js2-msg "msg.no.while.do"
-         "missing while after do-loop body")
-
-(js2-msg "msg.no.paren.for"
-         "missing ( after for")
-
-(js2-msg "msg.no.semi.for"
-         "missing ; after for-loop initializer")
-
-(js2-msg "msg.no.semi.for.cond"
-         "missing ; after for-loop condition")
-
-(js2-msg "msg.in.after.for.name"
-         "missing in or of after for")
-
-(js2-msg "msg.no.paren.for.ctrl"
-         "missing ) after for-loop control")
-
-(js2-msg "msg.no.paren.with"
-         "missing ( before with-statement object")
-
-(js2-msg "msg.no.paren.after.with"
-         "missing ) after with-statement object")
-
-(js2-msg "msg.no.with.strict"
-         "with statements not allowed in strict mode")
-
-(js2-msg "msg.no.paren.after.let"
-         "missing ( after let")
-
-(js2-msg "msg.no.paren.let"
-         "missing ) after variable list")
-
-(js2-msg "msg.no.curly.let"
-         "missing } after let statement")
-
-(js2-msg "msg.bad.return"
-         "invalid return")
-
-(js2-msg "msg.no.brace.block"
-         "missing } in compound statement")
-
-(js2-msg "msg.bad.label"
-         "invalid label")
-
-(js2-msg "msg.bad.var"
-         "missing variable name")
-
-(js2-msg "msg.bad.var.init"
-         "invalid variable initialization")
-
-(js2-msg "msg.no.colon.cond"
-         "missing : in conditional expression")
-
-(js2-msg "msg.no.paren.arg"
-         "missing ) after argument list")
-
-(js2-msg "msg.no.bracket.arg"
-         "missing ] after element list")
-
-(js2-msg "msg.bad.prop"
-         "invalid property id")
-
-(js2-msg "msg.no.colon.prop"
-         "missing : after property id")
-
-(js2-msg "msg.no.brace.prop"
-         "missing } after property list")
-
-(js2-msg "msg.no.paren"
-         "missing ) in parenthetical")
-
-(js2-msg "msg.reserved.id"
-         "'%s' is a reserved identifier")
-
-(js2-msg "msg.no.paren.catch"
-         "missing ( before catch-block condition")
-
-(js2-msg "msg.bad.catchcond"
-         "invalid catch block condition")
-
-(js2-msg "msg.catch.unreachable"
-         "any catch clauses following an unqualified catch are unreachable")
-
-(js2-msg "msg.no.brace.try"
-         "missing '{' before try block")
-
-(js2-msg "msg.no.brace.catchblock"
-         "missing '{' before catch-block body")
-
-(js2-msg "msg.try.no.catchfinally"
-         "'try' without 'catch' or 'finally'")
-
-(js2-msg "msg.no.return.value"
-         "function %s does not always return a value")
-
-(js2-msg "msg.anon.no.return.value"
-         "anonymous function does not always return a value")
-
-(js2-msg "msg.return.inconsistent"
-         "return statement is inconsistent with previous usage")
-
-(js2-msg "msg.generator.returns"
-         "TypeError: legacy generator function '%s' returns a value")
-
-(js2-msg "msg.anon.generator.returns"
-         "TypeError: anonymous legacy generator function returns a value")
-
-(js2-msg "msg.syntax"
-         "syntax error")
-
-(js2-msg "msg.unexpected.eof"
-         "Unexpected end of file")
-
-(js2-msg "msg.XML.bad.form"
-         "illegally formed XML syntax")
-
-(js2-msg "msg.XML.not.available"
-         "XML runtime not available")
-
-(js2-msg "msg.too.deep.parser.recursion"
-         "Too deep recursion while parsing")
-
-(js2-msg "msg.no.side.effects"
-         "Code has no side effects")
-
-(js2-msg "msg.extra.trailing.comma"
-         "Trailing comma is not supported in some browsers")
-
-(js2-msg "msg.array.trailing.comma"
-         "Trailing comma yields different behavior across browsers")
-
-(js2-msg "msg.equal.as.assign"
-         (concat "Test for equality (==) mistyped as assignment (=)?"
-                 " (parenthesize to suppress warning)"))
-
-(js2-msg "msg.var.hides.arg"
-         "Variable %s hides argument")
-
-(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.")
-
-(js2-msg "msg.dup.obj.lit.prop.strict"
-         "Property '%s' already defined in this object literal.")
-
-(js2-msg "msg.dup.param.strict"
-         "Parameter '%s' already declared in this function.")
-
-(js2-msg "msg.bad.id.strict"
-         "'%s' is not a valid identifier for this use in strict mode.")
-
-;; ScriptRuntime
-(js2-msg "msg.no.properties"
-         "%s has no properties.")
-
-(js2-msg "msg.invalid.iterator"
-         "Invalid iterator value")
-
-(js2-msg "msg.iterator.primitive"
-         "__iterator__ returned a primitive value")
-
-(js2-msg "msg.assn.create.strict"
-         "Assignment to undeclared variable %s")
-
-(js2-msg "msg.undeclared.variable"  ; added by js2-mode
-         "Undeclared variable or function '%s'")
-
-(js2-msg "msg.unused.variable"  ; added by js2-mode
-         "Unused variable or function '%s'")
-
-(js2-msg "msg.uninitialized.variable"  ; added by js2-mode
-         "Variable '%s' referenced but never initialized")
-
-(js2-msg "msg.ref.undefined.prop"
-         "Reference to undefined property '%s'")
-
-(js2-msg "msg.prop.not.found"
-         "Property %s not found.")
-
-(js2-msg "msg.invalid.type"
-         "Invalid JavaScript value of type %s")
-
-(js2-msg "msg.primitive.expected"
-         "Primitive type expected (had %s instead)")
-
-(js2-msg "msg.namespace.expected"
-         "Namespace object expected to left of :: (found %s instead)")
-
-(js2-msg "msg.null.to.object"
-         "Cannot convert null to an object.")
-
-(js2-msg "msg.undef.to.object"
-         "Cannot convert undefined to an object.")
-
-(js2-msg "msg.cyclic.value"
-         "Cyclic %s value not allowed.")
-
-(js2-msg "msg.is.not.defined"
-         "'%s' is not defined.")
-
-(js2-msg "msg.undef.prop.read"
-         "Cannot read property '%s' from %s")
-
-(js2-msg "msg.undef.prop.write"
-         "Cannot set property '%s' of %s to '%s'")
-
-(js2-msg "msg.undef.prop.delete"
-         "Cannot delete property '%s' of %s")
-
-(js2-msg "msg.undef.method.call"
-         "Cannot call method '%s' of %s")
-
-(js2-msg "msg.undef.with"
-         "Cannot apply 'with' to %s")
-
-(js2-msg "msg.isnt.function"
-         "%s is not a function, it is %s.")
-
-(js2-msg "msg.isnt.function.in"
-         "Cannot call property %s in object %s. "
-         "It is not a function, it is '%s'.")
-
-(js2-msg "msg.function.not.found"
-         "Cannot find function %s.")
-
-(js2-msg "msg.function.not.found.in"
-         "Cannot find function %s in object %s.")
-
-(js2-msg "msg.isnt.xml.object"
-         "%s is not an xml object.")
-
-(js2-msg "msg.no.ref.to.get"
-         "%s is not a reference to read reference value.")
-
-(js2-msg "msg.no.ref.to.set"
-         "%s is not a reference to set reference value to %s.")
-
-(js2-msg "msg.no.ref.from.function"
-         "Function %s can not be used as the left-hand "
-         "side of assignment or as an operand of ++ or -- operator.")
-
-(js2-msg "msg.bad.default.value"
-         "Object's getDefaultValue() method returned an object.")
-
-(js2-msg "msg.instanceof.not.object"
-         "Can't use instanceof on a non-object.")
-
-(js2-msg "msg.instanceof.bad.prototype"
-         "'prototype' property of %s is not an object.")
-
-(js2-msg "msg.bad.radix"
-         "illegal radix %s.")
-
-;; ScriptableObject
-(js2-msg "msg.default.value"
-         "Cannot find default value for object.")
-
-(js2-msg "msg.zero.arg.ctor"
-         "Cannot load class '%s' which has no zero-parameter constructor.")
-
-(js2-msg "msg.ctor.multiple.parms"
-         "Can't define constructor or class %s since more than "
-         "one constructor has multiple parameters.")
-
-(js2-msg "msg.extend.scriptable"
-         "%s must extend ScriptableObject in order to define property %s.")
-
-(js2-msg "msg.bad.getter.parms"
-         "In order to define a property, getter %s must have zero "
-         "parameters or a single ScriptableObject parameter.")
-
-(js2-msg "msg.obj.getter.parms"
-         "Expected static or delegated getter %s to take "
-         "a ScriptableObject parameter.")
-
-(js2-msg "msg.getter.static"
-         "Getter and setter must both be static or neither be static.")
-
-(js2-msg "msg.setter.return"
-         "Setter must have void return type: %s")
-
-(js2-msg "msg.setter2.parms"
-         "Two-parameter setter must take a ScriptableObject as "
-         "its first parameter.")
-
-(js2-msg "msg.setter1.parms"
-         "Expected single parameter setter for %s")
-
-(js2-msg "msg.setter2.expected"
-         "Expected static or delegated setter %s to take two parameters.")
-
-(js2-msg "msg.setter.parms"
-         "Expected either one or two parameters for setter.")
-
-(js2-msg "msg.setter.bad.type"
-         "Unsupported parameter type '%s' in setter '%s'.")
-
-(js2-msg "msg.add.sealed"
-         "Cannot add a property to a sealed object: %s.")
-
-(js2-msg "msg.remove.sealed"
-         "Cannot remove a property from a sealed object: %s.")
-
-(js2-msg "msg.modify.sealed"
-         "Cannot modify a property of a sealed object: %s.")
-
-(js2-msg "msg.modify.readonly"
-         "Cannot modify readonly property: %s.")
-
-;; TokenStream
-(js2-msg "msg.missing.exponent"
-         "missing exponent")
-
-(js2-msg "msg.caught.nfe"
-         "number format error")
-
-(js2-msg "msg.unterminated.string.lit"
-         "unterminated string literal")
-
-(js2-msg "msg.unterminated.comment"
-         "unterminated comment")
-
-(js2-msg "msg.unterminated.re.lit"
-         "unterminated regular expression literal")
-
-(js2-msg "msg.invalid.re.flag"
-         "invalid flag after regular expression")
-
-(js2-msg "msg.no.re.input.for"
-         "no input for %s")
-
-(js2-msg "msg.illegal.character"
-         "illegal character")
-
-(js2-msg "msg.invalid.escape"
-         "invalid Unicode escape sequence")
-
-(js2-msg "msg.bad.namespace"
-         "not a valid default namespace statement. "
-         "Syntax is: default xml namespace = EXPRESSION;")
-
-;; TokensStream warnings
-(js2-msg "msg.bad.octal.literal"
-         "illegal octal literal digit %s; "
-         "interpreting it as a decimal digit")
-
-(js2-msg "msg.missing.hex.digits"
-         "missing hexadecimal digits after '0x'")
-
-(js2-msg "msg.missing.binary.digits"
-         "missing binary digits after '0b'")
-
-(js2-msg "msg.missing.octal.digits"
-         "missing octal digits after '0o'")
-
-(js2-msg "msg.script.is.not.constructor"
-         "Script objects are not constructors.")
-
-;; Arrays
-(js2-msg "msg.arraylength.bad"
-         "Inappropriate array length.")
-
-;; Arrays
-(js2-msg "msg.arraylength.too.big"
-         "Array length %s exceeds supported capacity limit.")
-
-;; URI
-(js2-msg "msg.bad.uri"
-         "Malformed URI sequence.")
-
-;; Number
-(js2-msg "msg.bad.precision"
-         "Precision %s out of range.")
-
-;; NativeGenerator
-(js2-msg "msg.send.newborn"
-         "Attempt to send value to newborn generator")
-
-(js2-msg "msg.already.exec.gen"
-         "Already executing generator")
-
-(js2-msg "msg.StopIteration.invalid"
-         "StopIteration may not be changed to an arbitrary object.")
-
-;; Interpreter
-(js2-msg "msg.yield.closing"
-         "Yield from closing generator")
-
-;; Classes
-(js2-msg "msg.unnamed.class.stmt" ; added by js2-mode
-         "class statement requires a name")
-
-(js2-msg "msg.class.unexpected.comma" ; added by js2-mode
-         "unexpected ',' between class properties")
-
-(js2-msg "msg.unexpected.static" ; added by js2-mode
-         "unexpected 'static'")
-
-(js2-msg "msg.missing.extends" ; added by js2-mode
-         "name is required after extends")
-
-(js2-msg "msg.no.brace.class" ; added by js2-mode
-         "missing '{' before class body")
-
-(js2-msg "msg.missing.computed.rb" ; added by js2-mode
-         "missing ']' after computed property expression")
-
-;;; Tokens Buffer
-
-(defconst js2-ti-max-lookahead 2)
-(defconst js2-ti-ntokens (1+ js2-ti-max-lookahead))
-
-(defun js2-new-token (offset)
-  (let ((token (make-js2-token (+ offset js2-ts-cursor))))
-    (setq js2-ti-tokens-cursor (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens))
-    (aset js2-ti-tokens js2-ti-tokens-cursor token)
-    token))
-
-(defsubst js2-current-token ()
-  (aref js2-ti-tokens js2-ti-tokens-cursor))
-
-(defsubst js2-current-token-string ()
-  (js2-token-string (js2-current-token)))
-
-(defsubst js2-current-token-type ()
-  (js2-token-type (js2-current-token)))
-
-(defsubst js2-current-token-beg ()
-  (js2-token-beg (js2-current-token)))
-
-(defsubst js2-current-token-end ()
-  (js2-token-end (js2-current-token)))
-
-(defun js2-current-token-len ()
-  (let ((token (js2-current-token)))
-    (- (js2-token-end token)
-       (js2-token-beg token))))
-
-(defun js2-ts-seek (state)
-  (setq js2-ts-lineno (js2-ts-state-lineno state)
-        js2-ts-cursor (js2-ts-state-cursor state)
-        js2-ti-tokens (js2-ts-state-tokens state)
-        js2-ti-tokens-cursor (js2-ts-state-tokens-cursor state)
-        js2-ti-lookahead (js2-ts-state-lookahead state)))
-
-;;; Utilities
-
-(defun js2-delete-if (predicate list)
-  "Remove all items satisfying PREDICATE in LIST."
-  (cl-loop for item in list
-           if (not (funcall predicate item))
-           collect item))
-
-(defun js2-position (element list)
-  "Find 0-indexed position of ELEMENT in LIST comparing with `eq'.
-Returns nil if element is not found in the list."
-  (let ((count 0)
-        found)
-    (while (and list (not found))
-      (if (eq element (car list))
-          (setq found t)
-        (setq count (1+ count)
-              list (cdr list))))
-    (if found count)))
-
-(defun js2-find-if (predicate list)
-  "Find first item satisfying PREDICATE in LIST."
-  (let (result)
-    (while (and list (not result))
-      (if (funcall predicate (car list))
-          (setq result (car list)))
-      (setq list (cdr list)))
-    result))
-
-(defmacro js2-time (form)
-  "Evaluate FORM, discard result, and return elapsed time in sec."
-  (declare (debug t))
-  (let ((beg (make-symbol "--js2-time-beg--")))
-    `(let ((,beg (current-time)))
-       ,form
-       (/ (truncate (* (- (float-time (current-time))
-                          (float-time ,beg))
-                       10000))
-          10000.0))))
-
-(defsubst js2-same-line (pos)
-  "Return t if POS is on the same line as current point."
-  (and (>= pos (point-at-bol))
-       (<= pos (point-at-eol))))
-
-(defun js2-code-bug ()
-  "Signal an error when we encounter an unexpected code path."
-  (error "failed assertion"))
-
-(defsubst js2-record-text-property (beg end prop value)
-  "Record a text property to set when parsing finishes."
-  (push (list beg end prop value) js2-mode-deferred-properties))
-
-;; I'd like to associate errors with nodes, but for now the
-;; easiest thing to do is get the context info from the last token.
-(defun js2-record-parse-error (msg &optional arg pos len)
-  (push (list (list msg arg)
-              (or pos (js2-current-token-beg))
-              (or len (js2-current-token-len)))
-        js2-parsed-errors))
-
-(defun js2-report-error (msg &optional msg-arg pos len)
-  "Signal a syntax error or record a parse error."
-  (if js2-recover-from-parse-errors
-      (js2-record-parse-error msg msg-arg pos len)
-      (signal 'js2-syntax-error
-              (list msg
-                    js2-ts-lineno
-                    (save-excursion
-                     (goto-char js2-ts-cursor)
-                     (current-column))
-                    js2-ts-hit-eof))))
-
-(defun js2-report-warning (msg &optional msg-arg pos len face)
-  (if js2-compiler-report-warning-as-error
-      (js2-report-error msg msg-arg pos len)
-    (push (list (list msg msg-arg)
-                (or pos (js2-current-token-beg))
-                (or len (js2-current-token-len))
-                face)
-          js2-parsed-warnings)))
-
-(defun js2-add-strict-warning (msg-id &optional msg-arg beg end)
-  (if js2-compiler-strict-mode
-      (js2-report-warning msg-id msg-arg beg
-                          (and beg end (- end beg)))))
-
-(put 'js2-syntax-error 'error-conditions
-     '(error syntax-error js2-syntax-error))
-(put 'js2-syntax-error 'error-message "Syntax error")
-
-(put 'js2-parse-error 'error-conditions
-     '(error parse-error js2-parse-error))
-(put 'js2-parse-error 'error-message "Parse error")
-
-(defmacro js2-clear-flag (flags flag)
-  `(setq ,flags (logand ,flags (lognot ,flag))))
-
-(defmacro js2-set-flag (flags flag)
-  "Logical-or FLAG into FLAGS."
-  `(setq ,flags (logior ,flags ,flag)))
-
-(defsubst js2-flag-set-p (flags flag)
-  (/= 0 (logand flags flag)))
-
-(defsubst js2-flag-not-set-p (flags flag)
-  (zerop (logand flags flag)))
-
-;;; AST struct and function definitions
-
-;; flags for ast node property 'member-type (used for e4x operators)
-(defvar js2-property-flag    #x1 "Property access: element is valid name.")
-(defvar js2-attribute-flag   #x2 "x.@y or x..@y.")
-(defvar js2-descendants-flag #x4 "x..y or x..@i.")
-
-(defsubst js2-relpos (pos anchor)
-  "Convert POS to be relative to ANCHOR.
-If POS is nil, returns nil."
-  (and pos (- pos anchor)))
-
-(defun js2-make-pad (indent)
-  (if (zerop indent)
-      ""
-    (make-string (* indent js2-basic-offset) ? )))
-
-(defun js2-visit-ast (node callback)
-  "Visit every node in ast NODE with visitor CALLBACK.
-
-CALLBACK is a function that takes two arguments:  (NODE END-P).  It is
-called twice:  once to visit the node, and again after all the node's
-children have been processed.  The END-P argument is nil on the first
-call and non-nil on the second call.  The return value of the callback
-affects the traversal:  if non-nil, the children of NODE are processed.
-If the callback returns nil, or if the node has no children, then the
-callback is called immediately with a non-nil END-P argument.
-
-The node traversal is approximately lexical-order, although there
-are currently no guarantees around this."
-  (when node
-    (let ((vfunc (get (aref node 0) 'js2-visitor)))
-      ;; visit the node
-      (when  (funcall callback node nil)
-        ;; visit the kids
-        (cond
-         ((eq vfunc 'js2-visit-none)
-          nil)                            ; don't even bother calling it
-         ;; Each AST node type has to define a `js2-visitor' function
-         ;; that takes a node and a callback, and calls `js2-visit-ast'
-         ;; on each child of the node.
-         (vfunc
-          (funcall vfunc node callback))
-         (t
-          (error "%s does not define a visitor-traversal function"
-                 (aref node 0)))))
-      ;; call the end-visit
-      (funcall callback node t))))
-
-(cl-defstruct (js2-node
-               (:constructor nil))  ; abstract
-  "Base AST node type."
-  (type -1)  ; token type
-  (pos -1)   ; start position of this AST node in parsed input
-  (len 1)    ; num characters spanned by the node
-  props      ; optional node property list (an alist)
-  parent)    ; link to parent node; null for root
-
-(defsubst js2-node-get-prop (node prop &optional default)
-  (or (cadr (assoc prop (js2-node-props node))) default))
-
-(defsubst js2-node-set-prop (node prop value)
-  (setf (js2-node-props node)
-        (cons (list prop value) (js2-node-props node))))
-
-(defun js2-fixup-starts (n nodes)
-  "Adjust the start positions of NODES to be relative to N.
-Any node in the list may be nil, for convenience."
-  (dolist (node nodes)
-    (when node
-      (setf (js2-node-pos node) (- (js2-node-pos node)
-                                   (js2-node-pos n))))))
-
-(defun js2-node-add-children (parent &rest nodes)
-  "Set parent node of NODES to PARENT, and return PARENT.
-Does nothing if we're not recording parent links.
-If any given node in NODES is nil, doesn't record that link."
-  (js2-fixup-starts parent nodes)
-  (dolist (node nodes)
-    (and node
-         (setf (js2-node-parent node) parent))))
-
-;; Non-recursive since it's called a frightening number of times.
-(defun js2-node-abs-pos (n)
-  (let ((pos (js2-node-pos n)))
-    (while (setq n (js2-node-parent n))
-      (setq pos (+ pos (js2-node-pos n))))
-    pos))
-
-(defsubst js2-node-abs-end (n)
-  "Return absolute buffer position of end of N."
-  (+ (js2-node-abs-pos n) (js2-node-len n)))
-
-(defun js2--struct-put (name key value)
-  (put name key value)
-  (put (intern (format "cl-struct-%s" name)) key value))
-
-;; It's important to make sure block nodes have a Lisp list for the
-;; child nodes, to limit printing recursion depth in an AST that
-;; otherwise consists of defstruct vectors.  Emacs will crash printing
-;; a sufficiently large vector tree.
-
-(cl-defstruct (js2-block-node
-               (:include js2-node)
-               (:constructor make-js2-block-node (&key (type js2-BLOCK)
-                                                       (pos (js2-current-token-beg))
-                                                       len
-                                                       props
-                                                       kids)))
-  "A block of statements."
-  kids)  ; a Lisp list of the child statement nodes
-
-(js2--struct-put 'js2-block-node 'js2-visitor 'js2-visit-block)
-(js2--struct-put 'js2-block-node 'js2-printer 'js2-print-block)
-
-(defun js2-visit-block (ast callback)
-  "Visit the `js2-block-node' children of AST."
-  (dolist (kid (js2-block-node-kids ast))
-    (js2-visit-ast kid callback)))
-
-(defun js2-print-block (n i)
-  (let ((pad (js2-make-pad i)))
-    (insert pad "{\n")
-    (dolist (kid (js2-block-node-kids n))
-      (js2-print-ast kid (1+ i)))
-    (insert pad "}")))
-
-(cl-defstruct (js2-scope
-               (:include js2-block-node)
-               (:constructor make-js2-scope (&key (type js2-BLOCK)
-                                                  (pos (js2-current-token-beg))
-                                                  len
-                                                  kids)))
-  ;; The symbol-table is a LinkedHashMap<String,Symbol> in Rhino.
-  ;; I don't have one of those handy, so I'll use an alist for now.
-  ;; It's as fast as an emacs hashtable for up to about 50 elements,
-  ;; and is much lighter-weight to construct (both CPU and mem).
-  ;; The keys are interned strings (symbols) for faster lookup.
-  ;; Should switch to hybrid alist/hashtable eventually.
-  symbol-table  ; an alist of (symbol . js2-symbol)
-  parent-scope  ; a `js2-scope'
-  top)          ; top-level `js2-scope' (script/function)
-
-(js2--struct-put 'js2-scope 'js2-visitor 'js2-visit-block)
-(js2--struct-put 'js2-scope 'js2-printer 'js2-print-none)
-
-(defun js2-node-get-enclosing-scope (node)
-  "Return the innermost `js2-scope' node surrounding NODE.
-Returns nil if there is no enclosing scope node."
-  (while (and (setq node (js2-node-parent node))
-              (not (js2-scope-p node))))
-  node)
-
-(defun js2-get-defining-scope (scope name &optional point)
-  "Search up scope chain from SCOPE looking for NAME, a string or symbol.
-Returns `js2-scope' in which NAME is defined, or nil if not found.
-
-If POINT is non-nil, and if the found declaration type is
-`js2-LET', also check that the declaration node is before POINT."
-  (let ((sym (if (symbolp name)
-                 name
-               (intern name)))
-        result
-        (continue t))
-    (while (and scope continue)
-      (if (or
-           (let ((entry (cdr (assq sym (js2-scope-symbol-table scope)))))
-             (and entry
-                  (or (not point)
-                      (not (eq js2-LET (js2-symbol-decl-type entry)))
-                      (>= point
-                          (js2-node-abs-pos (js2-symbol-ast-node entry))))))
-           (and (eq sym 'arguments)
-                (js2-function-node-p scope)))
-          (setq continue nil
-                result scope)
-        (setq scope (js2-scope-parent-scope scope))))
-    result))
-
-(defun js2-scope-get-symbol (scope name)
-  "Return symbol table entry for NAME in SCOPE.
-NAME can be a string or symbol.   Returns a `js2-symbol' or nil if not found."
-  (and (js2-scope-symbol-table scope)
-       (cdr (assq (if (symbolp name)
-                      name
-                    (intern name))
-                  (js2-scope-symbol-table scope)))))
-
-(defun js2-scope-put-symbol (scope name symbol)
-  "Enter SYMBOL into symbol-table for SCOPE under NAME.
-NAME can be a Lisp symbol or string.  SYMBOL is a `js2-symbol'."
-  (let* ((table (js2-scope-symbol-table scope))
-         (sym (if (symbolp name) name (intern name)))
-         (entry (assq sym table)))
-    (if entry
-        (setcdr entry symbol)
-      (push (cons sym symbol)
-            (js2-scope-symbol-table scope)))))
-
-(cl-defstruct (js2-symbol
-               (:constructor make-js2-symbol (decl-type name &optional ast-node)))
-  "A symbol table entry."
-  ;; One of js2-FUNCTION, js2-LP (for parameters), js2-VAR,
-  ;; js2-LET, or js2-CONST
-  decl-type
-  name  ; string
-  ast-node) ; a `js2-node'
-
-(cl-defstruct (js2-error-node
-               (:include js2-node)
-               (:constructor make-js2-error-node (&key (type js2-ERROR)
-                                                       (pos (js2-current-token-beg))
-                                                       len)))
-  "AST node representing a parse error.")
-
-(js2--struct-put 'js2-error-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-error-node 'js2-printer 'js2-print-none)
-
-(cl-defstruct (js2-script-node
-               (:include js2-scope)
-               (:constructor make-js2-script-node (&key (type js2-SCRIPT)
-                                                        (pos (js2-current-token-beg))
-                                                        len)))
-  functions   ; Lisp list of nested functions
-  regexps     ; Lisp list of (string . flags)
-  symbols     ; alist (every symbol gets unique index)
-  (param-count 0)
-  var-names   ; vector of string names
-  consts      ; bool-vector matching var-decls
-  (temp-number 0))  ; for generating temp variables
-
-(js2--struct-put 'js2-script-node 'js2-visitor 'js2-visit-block)
-(js2--struct-put 'js2-script-node 'js2-printer 'js2-print-script)
-
-(defun js2-print-script (node indent)
-  (dolist (kid (js2-block-node-kids node))
-    (js2-print-ast kid indent)))
-
-(cl-defstruct (js2-ast-root
-               (:include js2-script-node)
-               (:constructor make-js2-ast-root (&key (type js2-SCRIPT)
-                                                     (pos (js2-current-token-beg))
-                                                     len
-                                                     buffer)))
-  "The root node of a js2 AST."
-  buffer         ; the source buffer from which the code was parsed
-  comments       ; a Lisp list of comments, ordered by start position
-  errors         ; a Lisp list of errors found during parsing
-  warnings       ; a Lisp list of warnings found during parsing
-  node-count)    ; number of nodes in the tree, including the root
-
-(js2--struct-put 'js2-ast-root 'js2-visitor 'js2-visit-ast-root)
-(js2--struct-put 'js2-ast-root 'js2-printer 'js2-print-script)
-
-(defun js2-visit-ast-root (ast callback)
-  (dolist (kid (js2-ast-root-kids ast))
-    (js2-visit-ast kid callback))
-  (dolist (comment (js2-ast-root-comments ast))
-    (js2-visit-ast comment callback)))
-
-(cl-defstruct (js2-comment-node
-               (:include js2-node)
-               (:constructor make-js2-comment-node (&key (type js2-COMMENT)
-                                                         (pos (js2-current-token-beg))
-                                                         len
-                                                         format)))
-  format)  ; 'line, 'block, 'jsdoc or 'html
-
-(js2--struct-put 'js2-comment-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-comment-node 'js2-printer 'js2-print-comment)
-
-(defun js2-print-comment (n i)
-  ;; We really ought to link end-of-line comments to their nodes.
-  ;; Or maybe we could add a new comment type, 'endline.
-  (insert (js2-make-pad i)
-          (js2-node-string n)))
-
-(cl-defstruct (js2-expr-stmt-node
-               (:include js2-node)
-               (:constructor make-js2-expr-stmt-node (&key (type js2-EXPR_VOID)
-                                                           (pos js2-ts-cursor)
-                                                           len
-                                                           expr)))
-  "An expression statement."
-  expr)
-
-(defsubst js2-expr-stmt-node-set-has-result (node)
-  "Change NODE type to `js2-EXPR_RESULT'.  Used for code generation."
-  (setf (js2-node-type node) js2-EXPR_RESULT))
-
-(js2--struct-put 'js2-expr-stmt-node 'js2-visitor 'js2-visit-expr-stmt-node)
-(js2--struct-put 'js2-expr-stmt-node 'js2-printer 'js2-print-expr-stmt-node)
-
-(defun js2-visit-expr-stmt-node (n v)
-  (js2-visit-ast (js2-expr-stmt-node-expr n) v))
-
-(defun js2-print-expr-stmt-node (n indent)
-  (js2-print-ast (js2-expr-stmt-node-expr n) indent)
-  (insert ";\n"))
-
-(cl-defstruct (js2-loop-node
-               (:include js2-scope)
-               (:constructor nil))
-  "Abstract supertype of loop nodes."
-  body      ; a `js2-block-node'
-  lp        ; position of left-paren, nil if omitted
-  rp)       ; position of right-paren, nil if omitted
-
-(cl-defstruct (js2-do-node
-               (:include js2-loop-node)
-               (:constructor make-js2-do-node (&key (type js2-DO)
-                                                    (pos (js2-current-token-beg))
-                                                    len
-                                                    body
-                                                    condition
-                                                    while-pos
-                                                    lp
-                                                    rp)))
-  "AST node for do-loop."
-  condition  ; while (expression)
-  while-pos) ; buffer position of 'while' keyword
-
-(js2--struct-put 'js2-do-node 'js2-visitor 'js2-visit-do-node)
-(js2--struct-put 'js2-do-node 'js2-printer 'js2-print-do-node)
-
-(defun js2-visit-do-node (n v)
-  (js2-visit-ast (js2-do-node-body n) v)
-  (js2-visit-ast (js2-do-node-condition n) v))
-
-(defun js2-print-do-node (n i)
-  (let ((pad (js2-make-pad i)))
-    (insert pad "do {\n")
-    (dolist (kid (js2-block-node-kids (js2-do-node-body n)))
-      (js2-print-ast kid (1+ i)))
-    (insert pad "} while (")
-    (js2-print-ast (js2-do-node-condition n) 0)
-    (insert ");\n")))
-
-(cl-defstruct (js2-export-node
-               (:include js2-node)
-               (:constructor make-js2-export-node (&key (type js2-EXPORT)
-                                                        (pos (js2-current-token-beg))
-                                                        len
-                                                        exports-list
-                                                        from-clause
-                                                        declaration
-                                                        default)))
-  "AST node for an export statement. There are many things that can be exported,
-so many of its properties will be nil.
-"
-  exports-list ; lisp list of js2-export-binding-node to export
-  from-clause ; js2-from-clause-node for re-exporting symbols from another module
-  declaration ; js2-var-decl-node (var, let, const) or js2-class-node
-  default) ; js2-function-node or js2-assign-node
-
-(js2--struct-put 'js2-export-node 'js2-visitor 'js2-visit-export-node)
-(js2--struct-put 'js2-export-node 'js2-printer 'js2-print-export-node)
-
-(defun js2-visit-export-node (n v)
-  (let ((exports-list (js2-export-node-exports-list n))
-        (from (js2-export-node-from-clause n))
-        (declaration (js2-export-node-declaration n))
-        (default (js2-export-node-default n)))
-    (when exports-list
-      (dolist (export exports-list)
-        (js2-visit-ast export v)))
-    (when from
-      (js2-visit-ast from v))
-    (when declaration
-      (js2-visit-ast declaration v))
-    (when default
-      (js2-visit-ast default v))))
-
-(defun js2-print-export-node (n i)
-  (let ((pad (js2-make-pad i))
-        (exports-list (js2-export-node-exports-list n))
-        (from (js2-export-node-from-clause n))
-        (declaration (js2-export-node-declaration n))
-        (default (js2-export-node-default n)))
-    (insert pad "export ")
-    (cond
-     (default
-       (insert "default ")
-       (js2-print-ast default i))
-     (declaration
-       (js2-print-ast declaration i))
-     ((and exports-list from)
-      (js2-print-named-imports exports-list)
-      (insert " ")
-      (js2-print-from-clause from))
-     (from
-      (insert "* ")
-      (js2-print-from-clause from))
-     (exports-list
-      (js2-print-named-imports exports-list)))
-    (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)
-               (:constructor make-js2-while-node (&key (type js2-WHILE)
-                                                       (pos (js2-current-token-beg))
-                                                       len body
-                                                       condition lp
-                                                       rp)))
-  "AST node for while-loop."
-  condition)    ; while-condition
-
-(js2--struct-put 'js2-while-node 'js2-visitor 'js2-visit-while-node)
-(js2--struct-put 'js2-while-node 'js2-printer 'js2-print-while-node)
-
-(defun js2-visit-while-node (n v)
-  (js2-visit-ast (js2-while-node-condition n) v)
-  (js2-visit-ast (js2-while-node-body n) v))
-
-(defun js2-print-while-node (n i)
-  (let ((pad (js2-make-pad i)))
-    (insert pad "while (")
-    (js2-print-ast (js2-while-node-condition n) 0)
-    (insert ") {\n")
-    (js2-print-body (js2-while-node-body n) (1+ i))
-    (insert pad "}\n")))
-
-(cl-defstruct (js2-for-node
-               (:include js2-loop-node)
-               (:constructor make-js2-for-node (&key (type js2-FOR)
-                                                     (pos js2-ts-cursor)
-                                                     len body init
-                                                     condition
-                                                     update lp rp)))
-  "AST node for a C-style for-loop."
-  init       ; initialization expression
-  condition  ; loop condition
-  update)    ; update clause
-
-(js2--struct-put 'js2-for-node 'js2-visitor 'js2-visit-for-node)
-(js2--struct-put 'js2-for-node 'js2-printer 'js2-print-for-node)
-
-(defun js2-visit-for-node (n v)
-  (js2-visit-ast (js2-for-node-init n) v)
-  (js2-visit-ast (js2-for-node-condition n) v)
-  (js2-visit-ast (js2-for-node-update n) v)
-  (js2-visit-ast (js2-for-node-body n) v))
-
-(defun js2-print-for-node (n i)
-  (let ((pad (js2-make-pad i)))
-    (insert pad "for (")
-    (js2-print-ast (js2-for-node-init n) 0)
-    (insert "; ")
-    (js2-print-ast (js2-for-node-condition n) 0)
-    (insert "; ")
-    (js2-print-ast (js2-for-node-update n) 0)
-    (insert ") {\n")
-    (js2-print-body (js2-for-node-body n) (1+ i))
-    (insert pad "}\n")))
-
-(cl-defstruct (js2-for-in-node
-               (:include js2-loop-node)
-               (:constructor make-js2-for-in-node (&key (type js2-FOR)
-                                                        (pos js2-ts-cursor)
-                                                        len body
-                                                        iterator
-                                                        object
-                                                        in-pos
-                                                        each-pos
-                                                        foreach-p forof-p
-                                                        lp rp)))
-  "AST node for a for..in loop."
-  iterator  ; [var] foo in ...
-  object    ; object over which we're iterating
-  in-pos    ; buffer position of 'in' keyword
-  each-pos  ; buffer position of 'each' keyword, if foreach-p
-  foreach-p ; t if it's a for-each loop
-  forof-p)  ; t if it's a for-of loop
-
-(js2--struct-put 'js2-for-in-node 'js2-visitor 'js2-visit-for-in-node)
-(js2--struct-put 'js2-for-in-node 'js2-printer 'js2-print-for-in-node)
-
-(defun js2-visit-for-in-node (n v)
-  (js2-visit-ast (js2-for-in-node-iterator n) v)
-  (js2-visit-ast (js2-for-in-node-object n) v)
-  (js2-visit-ast (js2-for-in-node-body n) v))
-
-(defun js2-print-for-in-node (n i)
-  (let ((pad (js2-make-pad i))
-        (foreach (js2-for-in-node-foreach-p n))
-        (forof (js2-for-in-node-forof-p n)))
-    (insert pad "for ")
-    (if foreach
-        (insert "each "))
-    (insert "(")
-    (js2-print-ast (js2-for-in-node-iterator n) 0)
-    (insert (if forof " of " " in "))
-    (js2-print-ast (js2-for-in-node-object n) 0)
-    (insert ") {\n")
-    (js2-print-body (js2-for-in-node-body n) (1+ i))
-    (insert pad "}\n")))
-
-(cl-defstruct (js2-return-node
-               (:include js2-node)
-               (:constructor make-js2-return-node (&key (type js2-RETURN)
-                                                        (pos js2-ts-cursor)
-                                                        len
-                                                        retval)))
-  "AST node for a return statement."
-  retval)  ; expression to return, or 'undefined
-
-(js2--struct-put 'js2-return-node 'js2-visitor 'js2-visit-return-node)
-(js2--struct-put 'js2-return-node 'js2-printer 'js2-print-return-node)
-
-(defun js2-visit-return-node (n v)
-  (js2-visit-ast (js2-return-node-retval n) v))
-
-(defun js2-print-return-node (n i)
-  (insert (js2-make-pad i) "return")
-  (when (js2-return-node-retval n)
-    (insert " ")
-    (js2-print-ast (js2-return-node-retval n) 0))
-  (insert ";\n"))
-
-(cl-defstruct (js2-if-node
-               (:include js2-node)
-               (:constructor make-js2-if-node (&key (type js2-IF)
-                                                    (pos js2-ts-cursor)
-                                                    len condition
-                                                    then-part
-                                                    else-pos
-                                                    else-part lp
-                                                    rp)))
-  "AST node for an if-statement."
-  condition   ; expression
-  then-part   ; statement or block
-  else-pos    ; optional buffer position of 'else' keyword
-  else-part   ; optional statement or block
-  lp          ; position of left-paren, nil if omitted
-  rp)         ; position of right-paren, nil if omitted
-
-(js2--struct-put 'js2-if-node 'js2-visitor 'js2-visit-if-node)
-(js2--struct-put 'js2-if-node 'js2-printer 'js2-print-if-node)
-
-(defun js2-visit-if-node (n v)
-  (js2-visit-ast (js2-if-node-condition n) v)
-  (js2-visit-ast (js2-if-node-then-part n) v)
-  (js2-visit-ast (js2-if-node-else-part n) v))
-
-(defun js2-print-if-node (n i)
-  (let ((pad (js2-make-pad i))
-        (then-part (js2-if-node-then-part n))
-        (else-part (js2-if-node-else-part n)))
-    (insert pad "if (")
-    (js2-print-ast (js2-if-node-condition n) 0)
-    (insert ") {\n")
-    (js2-print-body then-part (1+ i))
-    (insert pad "}")
-    (cond
-     ((not else-part)
-      (insert "\n"))
-     ((js2-if-node-p else-part)
-      (insert " else ")
-      (js2-print-body else-part i))
-     (t
-      (insert " else {\n")
-      (js2-print-body else-part (1+ i))
-      (insert pad "}\n")))))
-
-(cl-defstruct (js2-export-binding-node
-               (:include js2-node)
-               (:constructor make-js2-export-binding-node (&key (type -1)
-                                                                pos
-                                                                len
-                                                                local-name
-                                                                extern-name)))
-  "AST node for an external symbol binding.
-It contains a local-name node which is the name of the value in the
-current scope, and extern-name which is the name of the value in the
-imported or exported scope. By default these are the same, but if the
-name is aliased as in {foo as bar}, it would have an extern-name node
-containing 'foo' and a local-name node containing 'bar'."
-  local-name ; js2-name-node with the variable name in this scope
-  extern-name)   ; js2-name-node with the value name in the exporting module
-
-(js2--struct-put 'js2-export-binding-node 'js2-printer 'js2-print-extern-binding)
-(js2--struct-put 'js2-export-binding-node 'js2-visitor 'js2-visit-extern-binding)
-
-(defun js2-visit-extern-binding (n v)
-  "Visit an extern binding node. First visit the local-name, and, if
-different, visit the extern-name."
-  (let ((local-name (js2-export-binding-node-local-name n))
-        (extern-name (js2-export-binding-node-extern-name n)))
-    (when local-name
-      (js2-visit-ast local-name v))
-    (when (not (equal local-name extern-name))
-      (js2-visit-ast extern-name v))))
-
-(defun js2-print-extern-binding (n _i)
-  "Print a representation of a single extern binding. E.g. 'foo' or
-'foo as bar'."
-  (let ((local-name (js2-export-binding-node-local-name n))
-        (extern-name (js2-export-binding-node-extern-name n)))
-    (insert (js2-name-node-name extern-name))
-    (when (not (equal local-name extern-name))
-      (insert " as ")
-      (insert (js2-name-node-name local-name)))))
-
-
-(cl-defstruct (js2-import-node
-               (:include js2-node)
-               (:constructor make-js2-import-node (&key (type js2-IMPORT)
-                                                        (pos (js2-current-token-beg))
-                                                        len
-                                                        import
-                                                        from
-                                                        module-id)))
-  "AST node for an import statement. It follows the form
-
-import ModuleSpecifier;
-import ImportClause FromClause;"
-  import     ; js2-import-clause-node specifying which names are to imported.
-  from       ; js2-from-clause-node indicating the module from which to import.
-  module-id) ; module-id of the import. E.g. 'src/mylib'.
-
-(js2--struct-put 'js2-import-node 'js2-printer 'js2-print-import)
-(js2--struct-put 'js2-import-node 'js2-visitor 'js2-visit-import)
-
-(defun js2-visit-import (n v)
-  (let ((import-clause (js2-import-node-import n))
-        (from-clause (js2-import-node-from n)))
-    (when import-clause
-      (js2-visit-ast import-clause v))
-    (when from-clause
-      (js2-visit-ast from-clause v))))
-
-(defun js2-print-import (n i)
-  "Prints a representation of the import node"
-  (let ((pad (js2-make-pad i))
-        (import-clause (js2-import-node-import n))
-        (from-clause (js2-import-node-from n))
-        (module-id (js2-import-node-module-id n)))
-    (insert pad "import ")
-    (if import-clause
-        (progn
-          (js2-print-import-clause import-clause)
-          (insert " ")
-          (js2-print-from-clause from-clause))
-      (insert "'")
-      (insert module-id)
-      (insert "'"))
-    (insert ";\n")))
-
-(cl-defstruct (js2-import-clause-node
-               (:include js2-node)
-               (:constructor make-js2-import-clause-node (&key (type -1)
-                                                               pos
-                                                               len
-                                                               namespace-import
-                                                               named-imports
-                                                               default-binding)))
-  "AST node corresponding to the import clause of an import statement. This is
-the portion of the import that bindings names from the external context to the
-local context."
-  namespace-import ; js2-namespace-import-node. E.g. '* as lib'
-  named-imports    ; lisp list of js2-export-binding-node for all named imports.
-  default-binding) ; js2-export-binding-node for the default import binding
-
-(js2--struct-put 'js2-import-clause-node 'js2-visitor 'js2-visit-import-clause)
-(js2--struct-put 'js2-import-clause-node 'js2-printer 'js2-print-import-clause)
-
-(defun js2-visit-import-clause (n v)
-  (let ((ns-import (js2-import-clause-node-namespace-import n))
-        (named-imports (js2-import-clause-node-named-imports n))
-        (default (js2-import-clause-node-default-binding n)))
-    (when default
-      (js2-visit-ast default v))
-    (when ns-import
-      (js2-visit-ast ns-import v))
-    (when named-imports
-      (dolist (import named-imports)
-        (js2-visit-ast import v)))))
-
-(defun js2-print-import-clause (n)
-  (let ((ns-import (js2-import-clause-node-namespace-import n))
-        (named-imports (js2-import-clause-node-named-imports n))
-        (default (js2-import-clause-node-default-binding n)))
-    (cond
-     ((and default ns-import)
-      (js2-print-ast default)
-      (insert ", ")
-      (js2-print-namespace-import ns-import))
-     ((and default named-imports)
-      (js2-print-ast default)
-      (insert ", ")
-      (js2-print-named-imports named-imports))
-     (default
-      (js2-print-ast default))
-     (ns-import
-      (js2-print-namespace-import ns-import))
-     (named-imports
-      (js2-print-named-imports named-imports)))))
-
-(defun js2-print-namespace-import (node)
-  (insert "* as ")
-  (insert (js2-name-node-name (js2-namespace-import-node-name node))))
-
-(defun js2-print-named-imports (imports)
-  (insert "{")
-  (let ((len (length imports))
-        (n 0))
-    (while (< n len)
-      (js2-print-extern-binding (nth n imports) 0)
-      (unless (= n (- len 1))
-        (insert ", "))
-      (setq n (+ n 1))))
-  (insert "}"))
-
-(cl-defstruct (js2-namespace-import-node
-               (:include js2-node)
-               (:constructor make-js2-namespace-import-node (&key (type -1)
-                                                                  pos
-                                                                  len
-                                                                  name)))
-  "AST node for a complete namespace import.
-E.g. the '* as lib' expression in:
-
-import * as lib from 'src/lib'
-
-It contains a single name node referring to the bound name."
-  name) ; js2-name-node of the bound name.
-
-(defun js2-visit-namespace-import (n v)
-  (js2-visit-ast (js2-namespace-import-node-name n) v))
-
-(js2--struct-put 'js2-namespace-import-node 'js2-visitor 'js2-visit-namespace-import)
-(js2--struct-put 'js2-namespace-import-node 'js2-printer 'js2-print-namespace-import)
-
-(cl-defstruct (js2-from-clause-node
-               (:include js2-node)
-               (:constructor make-js2-from-clause-node (&key (type js2-NAME)
-                                                             pos
-                                                             len
-                                                             module-id
-                                                             metadata-p)))
-  "AST node for the from clause in an import or export statement.
-E.g. from 'my/module'. It can refere to either an external module, or to the
-modules metadata itself."
-  module-id ; string containing the module specifier.
-  metadata-p) ; true if this clause refers to the module's metadata
-
-(js2--struct-put 'js2-from-clause-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-from-clause-node 'js2-printer 'js2-print-from-clause)
-
-(defun js2-print-from-clause (n)
-  (insert "from ")
-  (if (js2-from-clause-node-metadata-p n)
-      (insert "this module")
-    (insert "'")
-    (insert (js2-from-clause-node-module-id n))
-    (insert "'")))
-
-(cl-defstruct (js2-try-node
-               (:include js2-node)
-               (:constructor make-js2-try-node (&key (type js2-TRY)
-                                                     (pos js2-ts-cursor)
-                                                     len
-                                                     try-block
-                                                     catch-clauses
-                                                     finally-block)))
-  "AST node for a try-statement."
-  try-block
-  catch-clauses  ; a Lisp list of `js2-catch-node'
-  finally-block) ; a `js2-finally-node'
-
-(js2--struct-put 'js2-try-node 'js2-visitor 'js2-visit-try-node)
-(js2--struct-put 'js2-try-node 'js2-printer 'js2-print-try-node)
-
-(defun js2-visit-try-node (n v)
-  (js2-visit-ast (js2-try-node-try-block n) v)
-  (dolist (clause (js2-try-node-catch-clauses n))
-    (js2-visit-ast clause v))
-  (js2-visit-ast (js2-try-node-finally-block n) v))
-
-(defun js2-print-try-node (n i)
-  (let ((pad (js2-make-pad i))
-        (catches (js2-try-node-catch-clauses n))
-        (finally (js2-try-node-finally-block n)))
-    (insert pad "try {\n")
-    (js2-print-body (js2-try-node-try-block n) (1+ i))
-    (insert pad "}")
-    (when catches
-      (dolist (catch catches)
-        (js2-print-ast catch i)))
-    (if finally
-        (js2-print-ast finally i)
-      (insert "\n"))))
-
-(cl-defstruct (js2-catch-node
-               (:include js2-scope)
-               (:constructor make-js2-catch-node (&key (type js2-CATCH)
-                                                       (pos js2-ts-cursor)
-                                                       len
-                                                       param
-                                                       guard-kwd
-                                                       guard-expr
-                                                       lp rp)))
-  "AST node for a catch clause."
-  param       ; destructuring form or simple name node
-  guard-kwd   ; relative buffer position of "if" in "catch (x if ...)"
-  guard-expr  ; catch condition, a `js2-node'
-  lp          ; buffer position of left-paren, nil if omitted
-  rp)         ; buffer position of right-paren, nil if omitted
-
-(js2--struct-put 'js2-catch-node 'js2-visitor 'js2-visit-catch-node)
-(js2--struct-put 'js2-catch-node 'js2-printer 'js2-print-catch-node)
-
-(defun js2-visit-catch-node (n v)
-  (js2-visit-ast (js2-catch-node-param n) v)
-  (when (js2-catch-node-guard-kwd n)
-    (js2-visit-ast (js2-catch-node-guard-expr n) v))
-  (js2-visit-block n v))
-
-(defun js2-print-catch-node (n i)
-  (let ((pad (js2-make-pad i))
-        (guard-kwd (js2-catch-node-guard-kwd n))
-        (guard-expr (js2-catch-node-guard-expr n)))
-    (insert " catch (")
-    (js2-print-ast (js2-catch-node-param n) 0)
-    (when guard-kwd
-      (insert " if ")
-      (js2-print-ast guard-expr 0))
-    (insert ") {\n")
-    (js2-print-body n (1+ i))
-    (insert pad "}")))
-
-(cl-defstruct (js2-finally-node
-               (:include js2-node)
-               (:constructor make-js2-finally-node (&key (type js2-FINALLY)
-                                                         (pos js2-ts-cursor)
-                                                         len body)))
-  "AST node for a finally clause."
-  body)  ; a `js2-node', often but not always a block node
-
-(js2--struct-put 'js2-finally-node 'js2-visitor 'js2-visit-finally-node)
-(js2--struct-put 'js2-finally-node 'js2-printer 'js2-print-finally-node)
-
-(defun js2-visit-finally-node (n v)
-  (js2-visit-ast (js2-finally-node-body n) v))
-
-(defun js2-print-finally-node (n i)
-  (let ((pad (js2-make-pad i)))
-    (insert " finally {\n")
-    (js2-print-body (js2-finally-node-body n) (1+ i))
-    (insert pad "}\n")))
-
-(cl-defstruct (js2-switch-node
-               (:include js2-scope)
-               (:constructor make-js2-switch-node (&key (type js2-SWITCH)
-                                                        (pos js2-ts-cursor)
-                                                        len
-                                                        discriminant
-                                                        cases lp
-                                                        rp)))
-  "AST node for a switch statement."
-  discriminant  ; a `js2-node' (switch expression)
-  cases  ; a Lisp list of `js2-case-node'
-  lp     ; position of open-paren for discriminant, nil if omitted
-  rp)    ; position of close-paren for discriminant, nil if omitted
-
-(js2--struct-put 'js2-switch-node 'js2-visitor 'js2-visit-switch-node)
-(js2--struct-put 'js2-switch-node 'js2-printer 'js2-print-switch-node)
-
-(defun js2-visit-switch-node (n v)
-  (js2-visit-ast (js2-switch-node-discriminant n) v)
-  (dolist (c (js2-switch-node-cases n))
-    (js2-visit-ast c v)))
-
-(defun js2-print-switch-node (n i)
-  (let ((pad (js2-make-pad i))
-        (cases (js2-switch-node-cases n)))
-    (insert pad "switch (")
-    (js2-print-ast (js2-switch-node-discriminant n) 0)
-    (insert ") {\n")
-    (dolist (case cases)
-      (js2-print-ast case i))
-    (insert pad "}\n")))
-
-(cl-defstruct (js2-case-node
-               (:include js2-block-node)
-               (:constructor make-js2-case-node (&key (type js2-CASE)
-                                                      (pos js2-ts-cursor)
-                                                      len kids expr)))
-  "AST node for a case clause of a switch statement."
-  expr)   ; the case expression (nil for default)
-
-(js2--struct-put 'js2-case-node 'js2-visitor 'js2-visit-case-node)
-(js2--struct-put 'js2-case-node 'js2-printer 'js2-print-case-node)
-
-(defun js2-visit-case-node (n v)
-  (js2-visit-ast (js2-case-node-expr n) v)
-  (js2-visit-block n v))
-
-(defun js2-print-case-node (n i)
-  (let ((pad (js2-make-pad i))
-        (expr (js2-case-node-expr n)))
-    (insert pad)
-    (if (null expr)
-        (insert "default:\n")
-      (insert "case ")
-      (js2-print-ast expr 0)
-      (insert ":\n"))
-    (dolist (kid (js2-case-node-kids n))
-      (js2-print-ast kid (1+ i)))))
-
-(cl-defstruct (js2-throw-node
-               (:include js2-node)
-               (:constructor make-js2-throw-node (&key (type js2-THROW)
-                                                       (pos js2-ts-cursor)
-                                                       len expr)))
-  "AST node for a throw statement."
-  expr)   ; the expression to throw
-
-(js2--struct-put 'js2-throw-node 'js2-visitor 'js2-visit-throw-node)
-(js2--struct-put 'js2-throw-node 'js2-printer 'js2-print-throw-node)
-
-(defun js2-visit-throw-node (n v)
-  (js2-visit-ast (js2-throw-node-expr n) v))
-
-(defun js2-print-throw-node (n i)
-  (insert (js2-make-pad i) "throw ")
-  (js2-print-ast (js2-throw-node-expr n) 0)
-  (insert ";\n"))
-
-(cl-defstruct (js2-with-node
-               (:include js2-node)
-               (:constructor make-js2-with-node (&key (type js2-WITH)
-                                                      (pos js2-ts-cursor)
-                                                      len object
-                                                      body lp rp)))
-  "AST node for a with-statement."
-  object
-  body
-  lp    ; buffer position of left-paren around object, nil if omitted
-  rp)   ; buffer position of right-paren around object, nil if omitted
-
-(js2--struct-put 'js2-with-node 'js2-visitor 'js2-visit-with-node)
-(js2--struct-put 'js2-with-node 'js2-printer 'js2-print-with-node)
-
-(defun js2-visit-with-node (n v)
-  (js2-visit-ast (js2-with-node-object n) v)
-  (js2-visit-ast (js2-with-node-body n) v))
-
-(defun js2-print-with-node (n i)
-  (let ((pad (js2-make-pad i)))
-    (insert pad "with (")
-    (js2-print-ast (js2-with-node-object n) 0)
-    (insert ") {\n")
-    (js2-print-body (js2-with-node-body n) (1+ i))
-    (insert pad "}\n")))
-
-(cl-defstruct (js2-label-node
-               (:include js2-node)
-               (:constructor make-js2-label-node (&key (type js2-LABEL)
-                                                       (pos js2-ts-cursor)
-                                                       len name)))
-  "AST node for a statement label or case label."
-  name   ; a string
-  loop)  ; for validating and code-generating continue-to-label
-
-(js2--struct-put 'js2-label-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-label-node 'js2-printer 'js2-print-label)
-
-(defun js2-print-label (n i)
-  (insert (js2-make-pad i)
-          (js2-label-node-name n)
-          ":\n"))
-
-(cl-defstruct (js2-labeled-stmt-node
-               (:include js2-node)
-               ;; type needs to be in `js2-side-effecting-tokens' to avoid spurious
-               ;; no-side-effects warnings, hence js2-EXPR_RESULT.
-               (:constructor make-js2-labeled-stmt-node (&key (type js2-EXPR_RESULT)
-                                                              (pos js2-ts-cursor)
-                                                              len labels stmt)))
-  "AST node for a statement with one or more labels.
-Multiple labels for a statement are collapsed into the labels field."
-  labels  ; Lisp list of `js2-label-node'
-  stmt)   ; the statement these labels are for
-
-(js2--struct-put 'js2-labeled-stmt-node 'js2-visitor 'js2-visit-labeled-stmt)
-(js2--struct-put 'js2-labeled-stmt-node 'js2-printer 'js2-print-labeled-stmt)
-
-(defun js2-get-label-by-name (lbl-stmt name)
-  "Return a `js2-label-node' by NAME from LBL-STMT's labels list.
-Returns nil if no such label is in the list."
-  (let ((label-list (js2-labeled-stmt-node-labels lbl-stmt))
-        result)
-    (while (and label-list (not result))
-      (if (string= (js2-label-node-name (car label-list)) name)
-          (setq result (car label-list))
-        (setq label-list (cdr label-list))))
-    result))
-
-(defun js2-visit-labeled-stmt (n v)
-  (dolist (label (js2-labeled-stmt-node-labels n))
-    (js2-visit-ast label v))
-  (js2-visit-ast (js2-labeled-stmt-node-stmt n) v))
-
-(defun js2-print-labeled-stmt (n i)
-  (dolist (label (js2-labeled-stmt-node-labels n))
-    (js2-print-ast label i))
-  (js2-print-ast (js2-labeled-stmt-node-stmt n) i))
-
-(defun js2-labeled-stmt-node-contains (node label)
-  "Return t if NODE contains LABEL in its label set.
-NODE is a `js2-labels-node'.  LABEL is an identifier."
-  (cl-loop for nl in (js2-labeled-stmt-node-labels node)
-           if (string= label (js2-label-node-name nl))
-           return t
-           finally return nil))
-
-(defsubst js2-labeled-stmt-node-add-label (node label)
-  "Add a `js2-label-node' to the label set for this statement."
-  (setf (js2-labeled-stmt-node-labels node)
-        (nconc (js2-labeled-stmt-node-labels node) (list label))))
-
-(cl-defstruct (js2-jump-node
-               (:include js2-node)
-               (:constructor nil))
-  "Abstract supertype of break and continue nodes."
-  label   ; `js2-name-node' for location of label identifier, if present
-  target) ; target js2-labels-node or loop/switch statement
-
-(defun js2-visit-jump-node (n v)
-  ;; We don't visit the target, since it's a back-link.
-  (js2-visit-ast (js2-jump-node-label n) v))
-
-(cl-defstruct (js2-break-node
-               (:include js2-jump-node)
-               (:constructor make-js2-break-node (&key (type js2-BREAK)
-                                                       (pos js2-ts-cursor)
-                                                       len label target)))
-  "AST node for a break statement.
-The label field is a `js2-name-node', possibly nil, for the named label
-if provided.  E.g. in 'break foo', it represents 'foo'.  The target field
-is the target of the break - a label node or enclosing loop/switch statement.")
-
-(js2--struct-put 'js2-break-node 'js2-visitor 'js2-visit-jump-node)
-(js2--struct-put 'js2-break-node 'js2-printer 'js2-print-break-node)
-
-(defun js2-print-break-node (n i)
-  (insert (js2-make-pad i) "break")
-  (when (js2-break-node-label n)
-    (insert " ")
-    (js2-print-ast (js2-break-node-label n) 0))
-  (insert ";\n"))
-
-(cl-defstruct (js2-continue-node
-               (:include js2-jump-node)
-               (:constructor make-js2-continue-node (&key (type js2-CONTINUE)
-                                                          (pos js2-ts-cursor)
-                                                          len label target)))
-  "AST node for a continue statement.
-The label field is the user-supplied enclosing label name, a `js2-name-node'.
-It is nil if continue specifies no label.  The target field is the jump target:
-a `js2-label-node' or the innermost enclosing loop.")
-
-(js2--struct-put 'js2-continue-node 'js2-visitor 'js2-visit-jump-node)
-(js2--struct-put 'js2-continue-node 'js2-printer 'js2-print-continue-node)
-
-(defun js2-print-continue-node (n i)
-  (insert (js2-make-pad i) "continue")
-  (when (js2-continue-node-label n)
-    (insert " ")
-    (js2-print-ast (js2-continue-node-label n) 0))
-  (insert ";\n"))
-
-(cl-defstruct (js2-function-node
-               (:include js2-script-node)
-               (:constructor make-js2-function-node (&key (type js2-FUNCTION)
-                                                          (pos js2-ts-cursor)
-                                                          len
-                                                          (ftype 'FUNCTION)
-                                                          (form 'FUNCTION_STATEMENT)
-                                                          (name "")
-                                                          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
-`js2-name-node', or if it's a destructuring-assignment parameter, a
-`js2-array-node' or `js2-object-node'."
-  ftype            ; FUNCTION, GETTER or SETTER
-  form             ; FUNCTION_{STATEMENT|EXPRESSION|ARROW}
-  name             ; function name (a `js2-name-node', or nil if anonymous)
-  params           ; a Lisp list of destructuring forms or simple name nodes
-  rest-p           ; if t, the last parameter is rest parameter
-  body             ; a `js2-block-node' or expression node (1.8 only)
-  lp               ; position of arg-list open-paren, or nil if omitted
-  rp               ; position of arg-list close-paren, or nil if omitted
-  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
-
-(js2--struct-put 'js2-function-node 'js2-visitor 'js2-visit-function-node)
-(js2--struct-put 'js2-function-node 'js2-printer 'js2-print-function-node)
-
-(defun js2-visit-function-node (n v)
-  (js2-visit-ast (js2-function-node-name n) v)
-  (dolist (p (js2-function-node-params n))
-    (js2-visit-ast p v))
-  (js2-visit-ast (js2-function-node-body n) v))
-
-(defun js2-print-function-node (n i)
-  (let* ((pad (js2-make-pad i))
-         (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))
-         (arrow (eq (js2-function-node-form n) 'FUNCTION_ARROW))
-         (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 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
-      (insert " ")
-      (js2-print-ast name 0))
-    (insert "(")
-    (cl-loop with len = (length params)
-             for param in params
-             for count from 1
-             do
-             (when (and rest-p (= count len))
-               (insert "..."))
-             (js2-print-ast param 0)
-             (when (< count len)
-               (insert ", ")))
-    (insert ") ")
-    (when arrow
-      (insert "=> "))
-    (insert "{")
-    ;; TODO:  fix this to be smarter about indenting, etc.
-    (unless expr
-      (insert "\n"))
-    (if (js2-block-node-p body)
-        (js2-print-body body (1+ i))
-      (js2-print-ast body 0))
-    (insert pad "}")
-    (unless expr
-      (insert "\n"))))
-
-(defun js2-function-name (node)
-  "Return function name for NODE, a `js2-function-node', or nil if anonymous."
-  (and (js2-function-node-name node)
-       (js2-name-node-name (js2-function-node-name node))))
-
-;; Having this be an expression node makes it more flexible.
-;; There are IDE contexts, such as indentation in a for-loop initializer,
-;; that work better if you assume it's an expression.  Whenever we have
-;; a standalone var/const declaration, we just wrap with an expr stmt.
-;; Eclipse apparently screwed this up and now has two versions, expr and stmt.
-(cl-defstruct (js2-var-decl-node
-               (:include js2-node)
-               (:constructor make-js2-var-decl-node (&key (type js2-VAR)
-                                                          (pos (js2-current-token-beg))
-                                                          len kids
-                                                          decl-type)))
-  "AST node for a variable declaration list (VAR, CONST or LET).
-The node bounds differ depending on the declaration type.  For VAR or
-CONST declarations, the bounds include the var/const keyword.  For LET
-declarations, the node begins at the position of the first child."
-  kids        ; a Lisp list of `js2-var-init-node' structs.
-  decl-type)  ; js2-VAR, js2-CONST or js2-LET
-
-(js2--struct-put 'js2-var-decl-node 'js2-visitor 'js2-visit-var-decl)
-(js2--struct-put 'js2-var-decl-node 'js2-printer 'js2-print-var-decl)
-
-(defun js2-visit-var-decl (n v)
-  (dolist (kid (js2-var-decl-node-kids n))
-    (js2-visit-ast kid v)))
-
-(defun js2-print-var-decl (n i)
-  (let ((pad (js2-make-pad i))
-        (tt (js2-var-decl-node-decl-type n)))
-    (insert pad)
-    (insert (cond
-             ((= tt js2-VAR) "var ")
-             ((= tt js2-LET) "let ")
-             ((= tt js2-CONST) "const ")
-             (t
-              (error "malformed var-decl node"))))
-    (cl-loop with kids = (js2-var-decl-node-kids n)
-             with len = (length kids)
-             for kid in kids
-             for count from 1
-             do
-             (js2-print-ast kid 0)
-             (if (< count len)
-                 (insert ", ")))))
-
-(cl-defstruct (js2-var-init-node
-               (:include js2-node)
-               (:constructor make-js2-var-init-node (&key (type js2-VAR)
-                                                          (pos js2-ts-cursor)
-                                                          len target
-                                                          initializer)))
-  "AST node for a variable declaration.
-The type field will be js2-CONST for a const decl."
-  target        ; `js2-name-node', `js2-object-node', or `js2-array-node'
-  initializer)  ; initializer expression, a `js2-node'
-
-(js2--struct-put 'js2-var-init-node 'js2-visitor 'js2-visit-var-init-node)
-(js2--struct-put 'js2-var-init-node 'js2-printer 'js2-print-var-init-node)
-
-(defun js2-visit-var-init-node (n v)
-  (js2-visit-ast (js2-var-init-node-target n) v)
-  (js2-visit-ast (js2-var-init-node-initializer n) v))
-
-(defun js2-print-var-init-node (n i)
-  (let ((pad (js2-make-pad i))
-        (name (js2-var-init-node-target n))
-        (init (js2-var-init-node-initializer n)))
-    (insert pad)
-    (js2-print-ast name 0)
-    (when init
-      (insert " = ")
-      (js2-print-ast init 0))))
-
-(cl-defstruct (js2-cond-node
-               (:include js2-node)
-               (:constructor make-js2-cond-node (&key (type js2-HOOK)
-                                                      (pos js2-ts-cursor)
-                                                      len
-                                                      test-expr
-                                                      true-expr
-                                                      false-expr
-                                                      q-pos c-pos)))
-  "AST node for the ternary operator"
-  test-expr
-  true-expr
-  false-expr
-  q-pos   ; buffer position of ?
-  c-pos)  ; buffer position of :
-
-(js2--struct-put 'js2-cond-node 'js2-visitor 'js2-visit-cond-node)
-(js2--struct-put 'js2-cond-node 'js2-printer 'js2-print-cond-node)
-
-(defun js2-visit-cond-node (n v)
-  (js2-visit-ast (js2-cond-node-test-expr n) v)
-  (js2-visit-ast (js2-cond-node-true-expr n) v)
-  (js2-visit-ast (js2-cond-node-false-expr n) v))
-
-(defun js2-print-cond-node (n i)
-  (let ((pad (js2-make-pad i)))
-    (insert pad)
-    (js2-print-ast (js2-cond-node-test-expr n) 0)
-    (insert " ? ")
-    (js2-print-ast (js2-cond-node-true-expr n) 0)
-    (insert " : ")
-    (js2-print-ast (js2-cond-node-false-expr n) 0)))
-
-(cl-defstruct (js2-infix-node
-               (:include js2-node)
-               (:constructor make-js2-infix-node (&key type
-                                                       (pos js2-ts-cursor)
-                                                       len op-pos
-                                                       left right)))
-  "Represents infix expressions.
-Includes assignment ops like `|=', and the comma operator.
-The type field inherited from `js2-node' holds the operator."
-  op-pos    ; buffer position where operator begins
-  left      ; any `js2-node'
-  right)    ; any `js2-node'
-
-(js2--struct-put 'js2-infix-node 'js2-visitor 'js2-visit-infix-node)
-(js2--struct-put 'js2-infix-node 'js2-printer 'js2-print-infix-node)
-
-(defun js2-visit-infix-node (n v)
-  (js2-visit-ast (js2-infix-node-left n) v)
-  (js2-visit-ast (js2-infix-node-right n) v))
-
-(defconst js2-operator-tokens
-  (let ((table (make-hash-table :test 'eq))
-        (tokens
-         (list (cons js2-IN "in")
-               (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 "||")
-               (cons js2-AND "&&")
-               (cons js2-INC "++")
-               (cons js2-DEC "--")
-               (cons js2-BITOR "|")
-               (cons js2-BITXOR "^")
-               (cons js2-BITAND "&")
-               (cons js2-EQ "==")
-               (cons js2-NE "!=")
-               (cons js2-LT "<")
-               (cons js2-LE "<=")
-               (cons js2-GT ">")
-               (cons js2-GE ">=")
-               (cons js2-LSH "<<")
-               (cons js2-RSH ">>")
-               (cons js2-URSH ">>>")
-               (cons js2-ADD "+")       ; infix plus
-               (cons js2-SUB "-")       ; infix minus
-               (cons js2-MUL "*")
-               (cons js2-EXPON "**")
-               (cons js2-DIV "/")
-               (cons js2-MOD "%")
-               (cons js2-NOT "!")
-               (cons js2-BITNOT "~")
-               (cons js2-POS "+")       ; unary plus
-               (cons js2-NEG "-")       ; unary minus
-               (cons js2-TRIPLEDOT "...")
-               (cons js2-SHEQ "===")    ; shallow equality
-               (cons js2-SHNE "!==")    ; shallow inequality
-               (cons js2-ASSIGN "=")
-               (cons js2-ASSIGN_BITOR "|=")
-               (cons js2-ASSIGN_BITXOR "^=")
-               (cons js2-ASSIGN_BITAND "&=")
-               (cons js2-ASSIGN_LSH "<<=")
-               (cons js2-ASSIGN_RSH ">>=")
-               (cons js2-ASSIGN_URSH ">>>=")
-               (cons js2-ASSIGN_ADD "+=")
-               (cons js2-ASSIGN_SUB "-=")
-               (cons js2-ASSIGN_MUL "*=")
-               (cons js2-ASSIGN_EXPON "**=")
-               (cons js2-ASSIGN_DIV "/=")
-               (cons js2-ASSIGN_MOD "%="))))
-    (cl-loop for (k . v) in tokens do
-             (puthash k v table))
-    table))
-
-(defun js2-print-infix-node (n i)
-  (let* ((tt (js2-node-type n))
-         (op (gethash tt js2-operator-tokens)))
-    (unless op
-      (error "unrecognized infix operator %s" (js2-node-type n)))
-    (insert (js2-make-pad i))
-    (js2-print-ast (js2-infix-node-left n) 0)
-    (unless (= tt js2-COMMA)
-      (insert " "))
-    (insert op)
-    (insert " ")
-    (js2-print-ast (js2-infix-node-right n) 0)))
-
-(cl-defstruct (js2-assign-node
-               (:include js2-infix-node)
-               (:constructor make-js2-assign-node (&key type
-                                                        (pos js2-ts-cursor)
-                                                        len op-pos
-                                                        left right)))
-  "Represents any assignment.
-The type field holds the actual assignment operator.")
-
-(js2--struct-put 'js2-assign-node 'js2-visitor 'js2-visit-infix-node)
-(js2--struct-put 'js2-assign-node 'js2-printer 'js2-print-infix-node)
-
-(cl-defstruct (js2-unary-node
-               (:include js2-node)
-               (:constructor make-js2-unary-node (&key type ; required
-                                                       (pos js2-ts-cursor)
-                                                       len operand)))
-  "AST node type for unary operator nodes.
-The type field can be NOT, BITNOT, POS, NEG, INC, DEC,
-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
-
-(js2--struct-put 'js2-unary-node 'js2-visitor 'js2-visit-unary-node)
-(js2--struct-put 'js2-unary-node 'js2-printer 'js2-print-unary-node)
-
-(defun js2-visit-unary-node (n v)
-  (js2-visit-ast (js2-unary-node-operand n) v))
-
-(defun js2-print-unary-node (n i)
-  (let* ((tt (js2-node-type n))
-         (op (gethash tt js2-operator-tokens))
-         (postfix (js2-node-get-prop n 'postfix)))
-    (unless op
-      (error "unrecognized unary operator %s" tt))
-    (insert (js2-make-pad i))
-    (unless postfix
-      (insert op))
-    (if (or (= tt js2-TYPEOF)
-            (= tt js2-DELPROP)
-            (= tt js2-AWAIT)
-            (= tt js2-VOID))
-        (insert " "))
-    (js2-print-ast (js2-unary-node-operand n) 0)
-    (when postfix
-      (insert op))))
-
-(cl-defstruct (js2-let-node
-               (:include js2-scope)
-               (:constructor make-js2-let-node (&key (type js2-LETEXPR)
-                                                     (pos (js2-current-token-beg))
-                                                     len vars body
-                                                     lp rp)))
-  "AST node for a let expression or a let statement.
-Note that a let declaration such as let x=6, y=7 is a `js2-var-decl-node'."
-  vars   ; a `js2-var-decl-node'
-  body   ; a `js2-node' representing the expression or body block
-  lp
-  rp)
-
-(js2--struct-put 'js2-let-node 'js2-visitor 'js2-visit-let-node)
-(js2--struct-put 'js2-let-node 'js2-printer 'js2-print-let-node)
-
-(defun js2-visit-let-node (n v)
-  (js2-visit-ast (js2-let-node-vars n) v)
-  (js2-visit-ast (js2-let-node-body n) v))
-
-(defun js2-print-let-node (n i)
-  (insert (js2-make-pad i) "let (")
-  (let ((p (point)))
-    (js2-print-ast (js2-let-node-vars n) 0)
-    (delete-region p (+ p 4)))
-  (insert ") ")
-  (js2-print-ast (js2-let-node-body n) i))
-
-(cl-defstruct (js2-keyword-node
-               (:include js2-node)
-               (:constructor make-js2-keyword-node (&key type
-                                                         (pos (js2-current-token-beg))
-                                                         (len (- js2-ts-cursor pos)))))
-  "AST node representing a literal keyword such as `null'.
-Used for `null', `this', `true', `false' and `debugger'.
-The node type is set to js2-NULL, js2-THIS, etc.")
-
-(js2--struct-put 'js2-keyword-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-keyword-node 'js2-printer 'js2-print-keyword-node)
-
-(defun js2-print-keyword-node (n i)
-  (insert (js2-make-pad i)
-          (let ((tt (js2-node-type n)))
-            (cond
-             ((= tt js2-THIS) "this")
-             ((= tt js2-SUPER) "super")
-             ((= tt js2-NULL) "null")
-             ((= tt js2-TRUE) "true")
-             ((= tt js2-FALSE) "false")
-             ((= tt js2-DEBUGGER) "debugger")
-             (t (error "Invalid keyword literal type: %d" tt))))))
-
-(defsubst js2-this-or-super-node-p (node)
-  "Return t if NODE is a `js2-literal-node' of type js2-THIS or js2-SUPER."
-  (let ((type (js2-node-type node)))
-    (or (eq type js2-THIS) (eq type js2-SUPER))))
-
-(cl-defstruct (js2-new-node
-               (:include js2-node)
-               (:constructor make-js2-new-node (&key (type js2-NEW)
-                                                     (pos (js2-current-token-beg))
-                                                     len target
-                                                     args initializer
-                                                     lp rp)))
-  "AST node for new-expression such as new Foo()."
-  target  ; an identifier or reference
-  args    ; a Lisp list of argument nodes
-  lp      ; position of left-paren, nil if omitted
-  rp      ; position of right-paren, nil if omitted
-  initializer) ; experimental Rhino syntax:  optional `js2-object-node'
-
-(js2--struct-put 'js2-new-node 'js2-visitor 'js2-visit-new-node)
-(js2--struct-put 'js2-new-node 'js2-printer 'js2-print-new-node)
-
-(defun js2-visit-new-node (n v)
-  (js2-visit-ast (js2-new-node-target n) v)
-  (dolist (arg (js2-new-node-args n))
-    (js2-visit-ast arg v))
-  (js2-visit-ast (js2-new-node-initializer n) v))
-
-(defun js2-print-new-node (n i)
-  (insert (js2-make-pad i) "new ")
-  (js2-print-ast (js2-new-node-target n))
-  (insert "(")
-  (js2-print-list (js2-new-node-args n))
-  (insert ")")
-  (when (js2-new-node-initializer n)
-    (insert " ")
-    (js2-print-ast (js2-new-node-initializer n))))
-
-(cl-defstruct (js2-name-node
-               (:include js2-node)
-               (:constructor make-js2-name-node (&key (type js2-NAME)
-                                                      (pos (js2-current-token-beg))
-                                                      (len (- js2-ts-cursor
-                                                              (js2-current-token-beg)))
-                                                      (name (js2-current-token-string)))))
-  "AST node for a JavaScript identifier"
-  name   ; a string
-  scope) ; a `js2-scope' (optional, used for codegen)
-
-(js2--struct-put 'js2-name-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-name-node 'js2-printer 'js2-print-name-node)
-
-(defun js2-print-name-node (n i)
-  (insert (js2-make-pad i)
-          (js2-name-node-name n)))
-
-(defsubst js2-name-node-length (node)
-  "Return identifier length of NODE, a `js2-name-node'.
-Returns 0 if NODE is nil or its identifier field is nil."
-  (if node
-      (length (js2-name-node-name node))
-    0))
-
-(cl-defstruct (js2-number-node
-               (:include js2-node)
-               (:constructor make-js2-number-node (&key (type js2-NUMBER)
-                                                        (pos (js2-current-token-beg))
-                                                        (len (- js2-ts-cursor
-                                                                (js2-current-token-beg)))
-                                                        (value (js2-current-token-string))
-                                                        (num-value (js2-token-number
-                                                                    (js2-current-token)))
-                                                        (num-base (js2-token-number-base
-                                                                   (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
-  legacy-octal-p)  ; whether the number is a legacy octal (0123 instead of 0o123)
-
-(js2--struct-put 'js2-number-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-number-node 'js2-printer 'js2-print-number-node)
-
-(defun js2-print-number-node (n i)
-  (insert (js2-make-pad i)
-          (number-to-string (js2-number-node-num-value n))))
-
-(cl-defstruct (js2-regexp-node
-               (:include js2-node)
-               (:constructor make-js2-regexp-node (&key (type js2-REGEXP)
-                                                        (pos (js2-current-token-beg))
-                                                        (len (- js2-ts-cursor
-                                                                (js2-current-token-beg)))
-                                                        value flags)))
-  "AST node for a regular expression literal."
-  value  ; the regexp string, without // delimiters
-  flags) ; a string of flags, e.g. `mi'.
-
-(js2--struct-put 'js2-regexp-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-regexp-node 'js2-printer 'js2-print-regexp)
-
-(defun js2-print-regexp (n i)
-  (insert (js2-make-pad i)
-          "/"
-          (js2-regexp-node-value n)
-          "/")
-  (if (js2-regexp-node-flags n)
-      (insert (js2-regexp-node-flags n))))
-
-(cl-defstruct (js2-string-node
-               (:include js2-node)
-               (:constructor make-js2-string-node (&key (type js2-STRING)
-                                                        (pos (js2-current-token-beg))
-                                                        (len (- js2-ts-cursor
-                                                                (js2-current-token-beg)))
-                                                        (value (js2-current-token-string)))))
-  "String literal.
-Escape characters are not evaluated; e.g. \n is 2 chars in value field.
-You can tell the quote type by looking at the first character."
-  value) ; the characters of the string, including the quotes
-
-(js2--struct-put 'js2-string-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-string-node 'js2-printer 'js2-print-string-node)
-
-(defun js2-print-string-node (n i)
-  (insert (js2-make-pad i)
-          (js2-node-string n)))
-
-(cl-defstruct (js2-template-node
-               (:include js2-node)
-               (:constructor make-js2-template-node (&key (type js2-TEMPLATE_HEAD)
-                                                          pos len kids)))
-  "Template literal."
-  kids)  ; `js2-string-node' is used for string segments, other nodes
-         ; for substitutions inside.
-
-(js2--struct-put 'js2-template-node 'js2-visitor 'js2-visit-template)
-(js2--struct-put 'js2-template-node 'js2-printer 'js2-print-template)
-
-(defun js2-visit-template (n callback)
-  (dolist (kid (js2-template-node-kids n))
-    (js2-visit-ast kid callback)))
-
-(defun js2-print-template (n i)
-  (insert (js2-make-pad i))
-  (dolist (kid (js2-template-node-kids n))
-    (if (js2-string-node-p kid)
-        (insert (js2-node-string kid))
-      (js2-print-ast kid))))
-
-(cl-defstruct (js2-tagged-template-node
-               (:include js2-node)
-               (:constructor make-js2-tagged-template-node (&key (type js2-TAGGED_TEMPLATE)
-                                                                 pos len tag template)))
-  "Tagged template literal."
-  tag       ; `js2-node' with the tag expression.
-  template) ; `js2-template-node' with the template.
-
-(js2--struct-put 'js2-tagged-template-node 'js2-visitor 'js2-visit-tagged-template)
-(js2--struct-put 'js2-tagged-template-node 'js2-printer 'js2-print-tagged-template)
-
-(defun js2-visit-tagged-template (n callback)
-  (js2-visit-ast (js2-tagged-template-node-tag n) callback)
-  (js2-visit-ast (js2-tagged-template-node-template n) callback))
-
-(defun js2-print-tagged-template (n i)
-  (insert (js2-make-pad i))
-  (js2-print-ast (js2-tagged-template-node-tag n))
-  (js2-print-ast (js2-tagged-template-node-template n)))
-
-(cl-defstruct (js2-array-node
-               (:include js2-node)
-               (:constructor make-js2-array-node (&key (type js2-ARRAYLIT)
-                                                       (pos js2-ts-cursor)
-                                                       len elems)))
-  "AST node for an array literal."
-  elems)  ; list of expressions.  [foo,,bar] yields a nil middle element.
-
-(js2--struct-put 'js2-array-node 'js2-visitor 'js2-visit-array-node)
-(js2--struct-put 'js2-array-node 'js2-printer 'js2-print-array-node)
-
-(defun js2-visit-array-node (n v)
-  (dolist (e (js2-array-node-elems n))
-    (js2-visit-ast e v)))  ; Can be nil; e.g. [a, ,b].
-
-(defun js2-print-array-node (n i)
-  (insert (js2-make-pad i) "[")
-  (let ((elems (js2-array-node-elems n)))
-    (js2-print-list elems)
-    (when (and elems (null (car (last elems))))
-      (insert ",")))
-  (insert "]"))
-
-(cl-defstruct (js2-object-node
-               (:include js2-node)
-               (: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)
-
-(js2--struct-put 'js2-object-node 'js2-visitor 'js2-visit-object-node)
-(js2--struct-put '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 make-js2-class-node (&key (type js2-CLASS)
-                                                       (pos js2-ts-cursor)
-                                                       (form 'CLASS_STATEMENT)
-                                                       (name "")
-                                                       extends len elems)))
-  "AST node for an class expression.
-`elems' is a list of `js2-object-prop-node', and `extends' is an
-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)
-  )
-
-(js2--struct-put 'js2-class-node 'js2-visitor 'js2-visit-class-node)
-(js2--struct-put 'js2-class-node 'js2-printer 'js2-print-class-node)
-
-(defun js2-visit-class-node (n v)
-  (js2-visit-ast (js2-class-node-name n) v)
-  (js2-visit-ast (js2-class-node-extends n) v)
-  (dolist (e (js2-class-node-elems n))
-    (js2-visit-ast e v)))
-
-(defun js2-print-class-node (n i)
-  (let* ((pad (js2-make-pad i))
-         (name (js2-class-node-name n))
-         (extends (js2-class-node-extends n))
-         (elems (js2-class-node-elems n)))
-    (insert pad "class")
-    (when name
-      (insert " ")
-      (js2-print-ast name 0))
-    (when extends
-      (insert " extends ")
-      (js2-print-ast extends))
-    (insert " {")
-    (dolist (elem elems)
-      (insert "\n")
-      (if (js2-node-get-prop elem 'STATIC)
-          (progn (insert (js2-make-pad (1+ i)) "static ")
-                 (js2-print-ast elem 0)) ;; TODO(sdh): indentation isn't quite right
-        (js2-print-ast elem (1+ i))))
-    (insert "\n" pad "}")))
-
-(cl-defstruct (js2-computed-prop-name-node
-               (:include js2-node)
-               (: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)
-
-(js2--struct-put 'js2-computed-prop-name-node 'js2-visitor 'js2-visit-computed-prop-name-node)
-(js2--struct-put 'js2-computed-prop-name-node 'js2-printer 'js2-print-computed-prop-name-node)
-
-(defun js2-visit-computed-prop-name-node (n v)
-  (js2-visit-ast (js2-computed-prop-name-node-expr n) v))
-
-(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)
-               (:constructor make-js2-object-prop-node (&key (type js2-COLON)
-                                                             (pos js2-ts-cursor)
-                                                             len left
-                                                             right op-pos)))
-  "AST node for an object literal prop:value entry.
-The `left' field is the property: a name node, string node,
-number node or expression node.  The `right' field is a
-`js2-node' representing the initializer value.  If the property
-is abbreviated, the node's `SHORTHAND' property is non-nil and
-both fields have the same value.")
-
-(js2--struct-put 'js2-object-prop-node 'js2-visitor 'js2-visit-infix-node)
-(js2--struct-put 'js2-object-prop-node 'js2-printer 'js2-print-object-prop-node)
-
-(defun js2-print-object-prop-node (n i)
-  (let* ((left (js2-object-prop-node-left n))
-         (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-method-node
-               (:include js2-infix-node)
-               (:constructor make-js2-method-node (&key (pos js2-ts-cursor)
-                                                        len left right)))
-  "AST node for a method in an object literal or a class body.
-The `left' field is the `js2-name-node' naming the method.
-The `right' field is always an anonymous `js2-function-node' with a node
-property `METHOD_TYPE' set to 'GET or 'SET. ")
-
-(js2--struct-put 'js2-method-node 'js2-visitor 'js2-visit-infix-node)
-(js2--struct-put 'js2-method-node 'js2-printer 'js2-print-method)
-
-(defun js2-print-method (n i)
-  (let* ((pad (js2-make-pad i))
-         (left (js2-method-node-left n))
-         (right (js2-method-node-right n))
-         (type (js2-node-get-prop right 'METHOD_TYPE)))
-    (insert pad)
-    (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 "*"))
-    (js2-print-ast left 0)
-    (js2-print-ast right 0)))
-
-(cl-defstruct (js2-prop-get-node
-               (:include js2-infix-node)
-               (:constructor make-js2-prop-get-node (&key (type js2-GETPROP)
-                                                          (pos js2-ts-cursor)
-                                                          len left right)))
-  "AST node for a dotted property reference, e.g. foo.bar or foo().bar")
-
-(js2--struct-put 'js2-prop-get-node 'js2-visitor 'js2-visit-prop-get-node)
-(js2--struct-put 'js2-prop-get-node 'js2-printer 'js2-print-prop-get-node)
-
-(defun js2-visit-prop-get-node (n v)
-  (js2-visit-ast (js2-prop-get-node-left n) v)
-  (js2-visit-ast (js2-prop-get-node-right n) v))
-
-(defun js2-print-prop-get-node (n i)
-  (insert (js2-make-pad i))
-  (js2-print-ast (js2-prop-get-node-left n) 0)
-  (insert ".")
-  (js2-print-ast (js2-prop-get-node-right n) 0))
-
-(cl-defstruct (js2-elem-get-node
-               (:include js2-node)
-               (:constructor make-js2-elem-get-node (&key (type js2-GETELEM)
-                                                          (pos js2-ts-cursor)
-                                                          len target element
-                                                          lb rb)))
-  "AST node for an array index expression such as foo[bar]."
-  target  ; a `js2-node' - the expression preceding the "."
-  element ; a `js2-node' - the expression in brackets
-  lb      ; position of left-bracket, nil if omitted
-  rb)     ; position of right-bracket, nil if omitted
-
-(js2--struct-put 'js2-elem-get-node 'js2-visitor 'js2-visit-elem-get-node)
-(js2--struct-put 'js2-elem-get-node 'js2-printer 'js2-print-elem-get-node)
-
-(defun js2-visit-elem-get-node (n v)
-  (js2-visit-ast (js2-elem-get-node-target n) v)
-  (js2-visit-ast (js2-elem-get-node-element n) v))
-
-(defun js2-print-elem-get-node (n i)
-  (insert (js2-make-pad i))
-  (js2-print-ast (js2-elem-get-node-target n) 0)
-  (insert "[")
-  (js2-print-ast (js2-elem-get-node-element n) 0)
-  (insert "]"))
-
-(cl-defstruct (js2-call-node
-               (:include js2-node)
-               (:constructor make-js2-call-node (&key (type js2-CALL)
-                                                      (pos js2-ts-cursor)
-                                                      len target args
-                                                      lp rp)))
-  "AST node for a JavaScript function call."
-  target  ; a `js2-node' evaluating to the function to call
-  args  ; a Lisp list of `js2-node' arguments
-  lp    ; position of open-paren, or nil if missing
-  rp)   ; position of close-paren, or nil if missing
-
-(js2--struct-put 'js2-call-node 'js2-visitor 'js2-visit-call-node)
-(js2--struct-put 'js2-call-node 'js2-printer 'js2-print-call-node)
-
-(defun js2-visit-call-node (n v)
-  (js2-visit-ast (js2-call-node-target n) v)
-  (dolist (arg (js2-call-node-args n))
-    (js2-visit-ast arg v)))
-
-(defun js2-print-call-node (n i)
-  (insert (js2-make-pad i))
-  (js2-print-ast (js2-call-node-target n) 0)
-  (insert "(")
-  (js2-print-list (js2-call-node-args n))
-  (insert ")"))
-
-(cl-defstruct (js2-yield-node
-               (:include js2-node)
-               (:constructor make-js2-yield-node (&key (type js2-YIELD)
-                                                       (pos js2-ts-cursor)
-                                                       len value star-p)))
-  "AST node for yield statement or expression."
-  star-p ; whether it's yield*
-  value) ; optional:  value to be yielded
-
-(js2--struct-put 'js2-yield-node 'js2-visitor 'js2-visit-yield-node)
-(js2--struct-put 'js2-yield-node 'js2-printer 'js2-print-yield-node)
-
-(defun js2-visit-yield-node (n v)
-  (js2-visit-ast (js2-yield-node-value n) v))
-
-(defun js2-print-yield-node (n i)
-  (insert (js2-make-pad i))
-  (insert "yield")
-  (when (js2-yield-node-star-p n)
-    (insert "*"))
-  (when (js2-yield-node-value n)
-    (insert " ")
-    (js2-print-ast (js2-yield-node-value n) 0)))
-
-(cl-defstruct (js2-paren-node
-               (:include js2-node)
-               (:constructor make-js2-paren-node (&key (type js2-LP)
-                                                       (pos js2-ts-cursor)
-                                                       len expr)))
-  "AST node for a parenthesized expression.
-In particular, used when the parens are syntactically optional,
-as opposed to required parens such as those enclosing an if-conditional."
-  expr)   ; `js2-node'
-
-(js2--struct-put 'js2-paren-node 'js2-visitor 'js2-visit-paren-node)
-(js2--struct-put 'js2-paren-node 'js2-printer 'js2-print-paren-node)
-
-(defun js2-visit-paren-node (n v)
-  (js2-visit-ast (js2-paren-node-expr n) v))
-
-(defun js2-print-paren-node (n i)
-  (insert (js2-make-pad i))
-  (insert "(")
-  (js2-print-ast (js2-paren-node-expr n) 0)
-  (insert ")"))
-
-(cl-defstruct (js2-comp-node
-               (:include js2-scope)
-               (:constructor make-js2-comp-node (&key (type js2-ARRAYCOMP)
-                                                      (pos js2-ts-cursor)
-                                                      len result
-                                                      loops filters
-                                                      form)))
-  "AST node for an Array comprehension such as [[x,y] for (x in foo) for (y in bar)]."
-  result  ; result expression (just after left-bracket)
-  loops   ; a Lisp list of `js2-comp-loop-node'
-  filters ; a Lisp list of guard/filter expressions
-  form    ; ARRAY, LEGACY_ARRAY or STAR_GENERATOR
-          ; SpiderMonkey also supports "legacy generator expressions", but we dont.
-  )
-
-(js2--struct-put 'js2-comp-node 'js2-visitor 'js2-visit-comp-node)
-(js2--struct-put 'js2-comp-node 'js2-printer 'js2-print-comp-node)
-
-(defun js2-visit-comp-node (n v)
-  (js2-visit-ast (js2-comp-node-result n) v)
-  (dolist (l (js2-comp-node-loops n))
-    (js2-visit-ast l v))
-  (dolist (f (js2-comp-node-filters n))
-    (js2-visit-ast f v)))
-
-(defun js2-print-comp-node (n i)
-  (let ((pad (js2-make-pad i))
-        (result (js2-comp-node-result n))
-        (loops (js2-comp-node-loops n))
-        (filters (js2-comp-node-filters n))
-        (legacy-p (eq (js2-comp-node-form n) 'LEGACY_ARRAY))
-        (gen-p (eq (js2-comp-node-form n) 'STAR_GENERATOR)))
-    (insert pad (if gen-p "(" "["))
-    (when legacy-p
-      (js2-print-ast result 0))
-    (dolist (l loops)
-      (when legacy-p
-        (insert " "))
-      (js2-print-ast l 0)
-      (unless legacy-p
-        (insert " ")))
-    (dolist (f filters)
-      (when legacy-p
-        (insert " "))
-      (insert "if (")
-      (js2-print-ast f 0)
-      (insert ")")
-      (unless legacy-p
-        (insert " ")))
-    (unless legacy-p
-      (js2-print-ast result 0))
-    (insert (if gen-p ")" "]"))))
-
-(cl-defstruct (js2-comp-loop-node
-               (:include js2-for-in-node)
-               (:constructor make-js2-comp-loop-node (&key (type js2-FOR)
-                                                           (pos js2-ts-cursor)
-                                                           len iterator
-                                                           object in-pos
-                                                           foreach-p
-                                                           each-pos
-                                                           forof-p
-                                                           lp rp)))
-  "AST subtree for each 'for (foo in bar)' loop in an array comprehension.")
-
-(js2--struct-put 'js2-comp-loop-node 'js2-visitor 'js2-visit-comp-loop)
-(js2--struct-put 'js2-comp-loop-node 'js2-printer 'js2-print-comp-loop)
-
-(defun js2-visit-comp-loop (n v)
-  (js2-visit-ast (js2-comp-loop-node-iterator n) v)
-  (js2-visit-ast (js2-comp-loop-node-object n) v))
-
-(defun js2-print-comp-loop (n _i)
-  (insert "for ")
-  (when (js2-comp-loop-node-foreach-p n) (insert "each "))
-  (insert "(")
-  (js2-print-ast (js2-comp-loop-node-iterator n) 0)
-  (insert (if (js2-comp-loop-node-forof-p n)
-              " of " " in "))
-  (js2-print-ast (js2-comp-loop-node-object n) 0)
-  (insert ")"))
-
-(cl-defstruct (js2-empty-expr-node
-               (:include js2-node)
-               (:constructor make-js2-empty-expr-node (&key (type js2-EMPTY)
-                                                            (pos (js2-current-token-beg))
-                                                            len)))
-  "AST node for an empty expression.")
-
-(js2--struct-put 'js2-empty-expr-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-empty-expr-node 'js2-printer 'js2-print-none)
-
-(cl-defstruct (js2-xml-node
-               (:include js2-block-node)
-               (:constructor make-js2-xml-node (&key (type js2-XML)
-                                                     (pos (js2-current-token-beg))
-                                                     len kids)))
-  "AST node for initial parse of E4X literals.
-The kids field is a list of XML fragments, each a `js2-string-node' or
-a `js2-xml-js-expr-node'.  Equivalent to Rhino's XmlLiteral node.")
-
-(js2--struct-put 'js2-xml-node 'js2-visitor 'js2-visit-block)
-(js2--struct-put 'js2-xml-node 'js2-printer 'js2-print-xml-node)
-
-(defun js2-print-xml-node (n i)
-  (dolist (kid (js2-xml-node-kids n))
-    (js2-print-ast kid i)))
-
-(cl-defstruct (js2-xml-js-expr-node
-               (:include js2-xml-node)
-               (:constructor make-js2-xml-js-expr-node (&key (type js2-XML)
-                                                             (pos js2-ts-cursor)
-                                                             len expr)))
-  "AST node for an embedded JavaScript {expression} in an E4X literal.
-The start and end fields correspond to the curly-braces."
-  expr)  ; a `js2-expr-node' of some sort
-
-(js2--struct-put 'js2-xml-js-expr-node 'js2-visitor 'js2-visit-xml-js-expr)
-(js2--struct-put 'js2-xml-js-expr-node 'js2-printer 'js2-print-xml-js-expr)
-
-(defun js2-visit-xml-js-expr (n v)
-  (js2-visit-ast (js2-xml-js-expr-node-expr n) v))
-
-(defun js2-print-xml-js-expr (n i)
-  (insert (js2-make-pad i))
-  (insert "{")
-  (js2-print-ast (js2-xml-js-expr-node-expr n) 0)
-  (insert "}"))
-
-(cl-defstruct (js2-xml-dot-query-node
-               (:include js2-infix-node)
-               (:constructor make-js2-xml-dot-query-node (&key (type js2-DOTQUERY)
-                                                               (pos js2-ts-cursor)
-                                                               op-pos len left
-                                                               right rp)))
-  "AST node for an E4X foo.(bar) filter expression.
-Note that the left-paren is automatically the character immediately
-following the dot (.) in the operator.  No whitespace is permitted
-between the dot and the lp by the scanner."
-  rp)
-
-(js2--struct-put 'js2-xml-dot-query-node 'js2-visitor 'js2-visit-infix-node)
-(js2--struct-put 'js2-xml-dot-query-node 'js2-printer 'js2-print-xml-dot-query)
-
-(defun js2-print-xml-dot-query (n i)
-  (insert (js2-make-pad i))
-  (js2-print-ast (js2-xml-dot-query-node-left n) 0)
-  (insert ".(")
-  (js2-print-ast (js2-xml-dot-query-node-right n) 0)
-  (insert ")"))
-
-(cl-defstruct (js2-xml-ref-node
-               (:include js2-node)
-               (:constructor nil))  ; abstract
-  "Base type for E4X XML attribute-access or property-get expressions.
-Such expressions can take a variety of forms.  The general syntax has
-three parts:
-
-  - (optional) an @ (specifying an attribute access)
-  - (optional) a namespace (a `js2-name-node') and double-colon
-  - (required) either a `js2-name-node' or a bracketed [expression]
-
-The property-name expressions (examples:  ns::name, @name) are
-represented as `js2-xml-prop-ref' nodes.  The bracketed-expression
-versions (examples:  ns::[name], @[name]) become `js2-xml-elem-ref' nodes.
-
-This node type (or more specifically, its subclasses) will sometimes
-be the right-hand child of a `js2-prop-get-node' or a
-`js2-infix-node' of type `js2-DOTDOT', the .. xml-descendants operator.
-The `js2-xml-ref-node' may also be a standalone primary expression with
-no explicit target, which is valid in certain expression contexts such as
-
-  company..employee.(@id < 100)
-
-in this case, the @id is a `js2-xml-ref' that is part of an infix '<'
-expression whose parent is a `js2-xml-dot-query-node'."
-  namespace
-  at-pos
-  colon-pos)
-
-(defsubst js2-xml-ref-node-attr-access-p (node)
-  "Return non-nil if this expression began with an @-token."
-  (and (numberp (js2-xml-ref-node-at-pos node))
-       (cl-plusp (js2-xml-ref-node-at-pos node))))
-
-(cl-defstruct (js2-xml-prop-ref-node
-               (:include js2-xml-ref-node)
-               (:constructor make-js2-xml-prop-ref-node (&key (type js2-REF_NAME)
-                                                              (pos (js2-current-token-beg))
-                                                              len propname
-                                                              namespace at-pos
-                                                              colon-pos)))
-  "AST node for an E4X XML [expr] property-ref expression.
-The JavaScript syntax is an optional @, an optional ns::, and a name.
-
-  [ '@' ] [ name '::' ] name
-
-Examples include name, ns::name, ns::*, *::name, *::*, @attr, @ns::attr,
-@ns::*, @*::attr, @*::*, and @*.
-
-The node starts at the @ token, if present.  Otherwise it starts at the
-namespace name.  The node bounds extend through the closing right-bracket,
-or if it is missing due to a syntax error, through the end of the index
-expression."
-  propname)
-
-(js2--struct-put 'js2-xml-prop-ref-node 'js2-visitor 'js2-visit-xml-prop-ref-node)
-(js2--struct-put 'js2-xml-prop-ref-node 'js2-printer 'js2-print-xml-prop-ref-node)
-
-(defun js2-visit-xml-prop-ref-node (n v)
-  (js2-visit-ast (js2-xml-prop-ref-node-namespace n) v)
-  (js2-visit-ast (js2-xml-prop-ref-node-propname n) v))
-
-(defun js2-print-xml-prop-ref-node (n i)
-  (insert (js2-make-pad i))
-  (if (js2-xml-ref-node-attr-access-p n)
-      (insert "@"))
-  (when (js2-xml-prop-ref-node-namespace n)
-    (js2-print-ast (js2-xml-prop-ref-node-namespace n) 0)
-    (insert "::"))
-  (if (js2-xml-prop-ref-node-propname n)
-      (js2-print-ast (js2-xml-prop-ref-node-propname n) 0)))
-
-(cl-defstruct (js2-xml-elem-ref-node
-               (:include js2-xml-ref-node)
-               (:constructor make-js2-xml-elem-ref-node (&key (type js2-REF_MEMBER)
-                                                              (pos (js2-current-token-beg))
-                                                              len expr lb rb
-                                                              namespace at-pos
-                                                              colon-pos)))
-  "AST node for an E4X XML [expr] member-ref expression.
-Syntax:
-
- [ '@' ] [ name '::' ] '[' expr ']'
-
-Examples include ns::[expr], @ns::[expr], @[expr], *::[expr] and @*::[expr].
-
-Note that the form [expr] (i.e. no namespace or attribute-qualifier)
-is not a legal E4X XML element-ref expression, since it's already used
-for standard JavaScript element-get array indexing.  Hence, a
-`js2-xml-elem-ref-node' always has either the attribute-qualifier, a
-non-nil namespace node, or both.
-
-The node starts at the @ token, if present.  Otherwise it starts
-at the namespace name.  The node bounds extend through the closing
-right-bracket, or if it is missing due to a syntax error, through the
-end of the index expression."
-  expr  ; the bracketed index expression
-  lb
-  rb)
-
-(js2--struct-put 'js2-xml-elem-ref-node 'js2-visitor 'js2-visit-xml-elem-ref-node)
-(js2--struct-put 'js2-xml-elem-ref-node 'js2-printer 'js2-print-xml-elem-ref-node)
-
-(defun js2-visit-xml-elem-ref-node (n v)
-  (js2-visit-ast (js2-xml-elem-ref-node-namespace n) v)
-  (js2-visit-ast (js2-xml-elem-ref-node-expr n) v))
-
-(defun js2-print-xml-elem-ref-node (n i)
-  (insert (js2-make-pad i))
-  (if (js2-xml-ref-node-attr-access-p n)
-      (insert "@"))
-  (when (js2-xml-elem-ref-node-namespace n)
-    (js2-print-ast (js2-xml-elem-ref-node-namespace n) 0)
-    (insert "::"))
-  (insert "[")
-  (if (js2-xml-elem-ref-node-expr n)
-      (js2-print-ast (js2-xml-elem-ref-node-expr n) 0))
-  (insert "]"))
-
-;;; Placeholder nodes for when we try parsing the XML literals structurally.
-
-(cl-defstruct (js2-xml-start-tag-node
-               (:include js2-xml-node)
-               (:constructor make-js2-xml-start-tag-node (&key (type js2-XML)
-                                                               (pos js2-ts-cursor)
-                                                               len name attrs kids
-                                                               empty-p)))
-  "AST node for an XML start-tag.  Not currently used.
-The `kids' field is a Lisp list of child content nodes."
-  name      ; a `js2-xml-name-node'
-  attrs     ; a Lisp list of `js2-xml-attr-node'
-  empty-p)  ; t if this is an empty element such as <foo bar="baz"/>
-
-(js2--struct-put 'js2-xml-start-tag-node 'js2-visitor 'js2-visit-xml-start-tag)
-(js2--struct-put 'js2-xml-start-tag-node 'js2-printer 'js2-print-xml-start-tag)
-
-(defun js2-visit-xml-start-tag (n v)
-  (js2-visit-ast (js2-xml-start-tag-node-name n) v)
-  (dolist (attr (js2-xml-start-tag-node-attrs n))
-    (js2-visit-ast attr v))
-  (js2-visit-block n v))
-
-(defun js2-print-xml-start-tag (n i)
-  (insert (js2-make-pad i) "<")
-  (js2-print-ast (js2-xml-start-tag-node-name n) 0)
-  (when (js2-xml-start-tag-node-attrs n)
-    (insert " ")
-    (js2-print-list (js2-xml-start-tag-node-attrs n) " "))
-  (insert ">"))
-
-;; I -think- I'm going to make the parent node the corresponding start-tag,
-;; and add the end-tag to the kids list of the parent as well.
-(cl-defstruct (js2-xml-end-tag-node
-               (:include js2-xml-node)
-               (:constructor make-js2-xml-end-tag-node (&key (type js2-XML)
-                                                             (pos js2-ts-cursor)
-                                                             len name)))
-  "AST node for an XML end-tag.  Not currently used."
-  name)  ; a `js2-xml-name-node'
-
-(js2--struct-put 'js2-xml-end-tag-node 'js2-visitor 'js2-visit-xml-end-tag)
-(js2--struct-put 'js2-xml-end-tag-node 'js2-printer 'js2-print-xml-end-tag)
-
-(defun js2-visit-xml-end-tag (n v)
-  (js2-visit-ast (js2-xml-end-tag-node-name n) v))
-
-(defun js2-print-xml-end-tag (n i)
-  (insert (js2-make-pad i))
-  (insert "</")
-  (js2-print-ast (js2-xml-end-tag-node-name n) 0)
-  (insert ">"))
-
-(cl-defstruct (js2-xml-name-node
-               (:include js2-xml-node)
-               (:constructor make-js2-xml-name-node (&key (type js2-XML)
-                                                          (pos js2-ts-cursor)
-                                                          len namespace kids)))
-  "AST node for an E4X XML name.  Not currently used.
-Any XML name can be qualified with a namespace, hence the namespace field.
-Further, any E4X name can be comprised of arbitrary JavaScript {} expressions.
-The kids field is a list of `js2-name-node' and `js2-xml-js-expr-node'.
-For a simple name, the kids list has exactly one node, a `js2-name-node'."
-  namespace)  ; a `js2-string-node'
-
-(js2--struct-put 'js2-xml-name-node 'js2-visitor 'js2-visit-xml-name-node)
-(js2--struct-put 'js2-xml-name-node 'js2-printer 'js2-print-xml-name-node)
-
-(defun js2-visit-xml-name-node (n v)
-  (js2-visit-ast (js2-xml-name-node-namespace n) v))
-
-(defun js2-print-xml-name-node (n i)
-  (insert (js2-make-pad i))
-  (when (js2-xml-name-node-namespace n)
-    (js2-print-ast (js2-xml-name-node-namespace n) 0)
-    (insert "::"))
-  (dolist (kid (js2-xml-name-node-kids n))
-    (js2-print-ast kid 0)))
-
-(cl-defstruct (js2-xml-pi-node
-               (:include js2-xml-node)
-               (:constructor make-js2-xml-pi-node (&key (type js2-XML)
-                                                        (pos js2-ts-cursor)
-                                                        len name attrs)))
-  "AST node for an E4X XML processing instruction.  Not currently used."
-  name   ; a `js2-xml-name-node'
-  attrs) ; a list of `js2-xml-attr-node'
-
-(js2--struct-put 'js2-xml-pi-node 'js2-visitor 'js2-visit-xml-pi-node)
-(js2--struct-put 'js2-xml-pi-node 'js2-printer 'js2-print-xml-pi-node)
-
-(defun js2-visit-xml-pi-node (n v)
-  (js2-visit-ast (js2-xml-pi-node-name n) v)
-  (dolist (attr (js2-xml-pi-node-attrs n))
-    (js2-visit-ast attr v)))
-
-(defun js2-print-xml-pi-node (n i)
-  (insert (js2-make-pad i) "<?")
-  (js2-print-ast (js2-xml-pi-node-name n))
-  (when (js2-xml-pi-node-attrs n)
-    (insert " ")
-    (js2-print-list (js2-xml-pi-node-attrs n)))
-  (insert "?>"))
-
-(cl-defstruct (js2-xml-cdata-node
-               (:include js2-xml-node)
-               (:constructor make-js2-xml-cdata-node (&key (type js2-XML)
-                                                           (pos js2-ts-cursor)
-                                                           len content)))
-  "AST node for a CDATA escape section.  Not currently used."
-  content)  ; a `js2-string-node' with node-property 'quote-type 'cdata
-
-(js2--struct-put 'js2-xml-cdata-node 'js2-visitor 'js2-visit-xml-cdata-node)
-(js2--struct-put 'js2-xml-cdata-node 'js2-printer 'js2-print-xml-cdata-node)
-
-(defun js2-visit-xml-cdata-node (n v)
-  (js2-visit-ast (js2-xml-cdata-node-content n) v))
-
-(defun js2-print-xml-cdata-node (n i)
-  (insert (js2-make-pad i))
-  (js2-print-ast (js2-xml-cdata-node-content n)))
-
-(cl-defstruct (js2-xml-attr-node
-               (:include js2-xml-node)
-               (:constructor make-js2-attr-node (&key (type js2-XML)
-                                                      (pos js2-ts-cursor)
-                                                      len name value
-                                                      eq-pos quote-type)))
-  "AST node representing a foo='bar' XML attribute value.  Not yet used."
-  name   ; a `js2-xml-name-node'
-  value  ; a `js2-xml-name-node'
-  eq-pos ; buffer position of "=" sign
-  quote-type) ; 'single or 'double
-
-(js2--struct-put 'js2-xml-attr-node 'js2-visitor 'js2-visit-xml-attr-node)
-(js2--struct-put 'js2-xml-attr-node 'js2-printer 'js2-print-xml-attr-node)
-
-(defun js2-visit-xml-attr-node (n v)
-  (js2-visit-ast (js2-xml-attr-node-name n) v)
-  (js2-visit-ast (js2-xml-attr-node-value n) v))
-
-(defun js2-print-xml-attr-node (n i)
-  (let ((quote (if (eq (js2-xml-attr-node-quote-type n) 'single)
-                   "'"
-                 "\"")))
-    (insert (js2-make-pad i))
-    (js2-print-ast (js2-xml-attr-node-name n) 0)
-    (insert "=" quote)
-    (js2-print-ast (js2-xml-attr-node-value n) 0)
-    (insert quote)))
-
-(cl-defstruct (js2-xml-text-node
-               (:include js2-xml-node)
-               (:constructor make-js2-text-node (&key (type js2-XML)
-                                                      (pos js2-ts-cursor)
-                                                      len content)))
-  "AST node for an E4X XML text node.  Not currently used."
-  content)  ; a Lisp list of `js2-string-node' and `js2-xml-js-expr-node'
-
-(js2--struct-put 'js2-xml-text-node 'js2-visitor 'js2-visit-xml-text-node)
-(js2--struct-put 'js2-xml-text-node 'js2-printer 'js2-print-xml-text-node)
-
-(defun js2-visit-xml-text-node (n v)
-  (js2-visit-ast (js2-xml-text-node-content n) v))
-
-(defun js2-print-xml-text-node (n i)
-  (insert (js2-make-pad i))
-  (dolist (kid (js2-xml-text-node-content n))
-    (js2-print-ast kid)))
-
-(cl-defstruct (js2-xml-comment-node
-               (:include js2-xml-node)
-               (:constructor make-js2-xml-comment-node (&key (type js2-XML)
-                                                             (pos js2-ts-cursor)
-                                                             len)))
-  "AST node for E4X XML comment.  Not currently used.")
-
-(js2--struct-put 'js2-xml-comment-node 'js2-visitor 'js2-visit-none)
-(js2--struct-put 'js2-xml-comment-node 'js2-printer 'js2-print-xml-comment)
-
-(defun js2-print-xml-comment (n i)
-  (insert (js2-make-pad i)
-          (js2-node-string n)))
-
-;;; Node utilities
-
-(defsubst js2-node-line (n)
-  "Fetch the source line number at the start of node N.
-This is O(n) in the length of the source buffer; use prudently."
-  (1+ (count-lines (point-min) (js2-node-abs-pos n))))
-
-(defsubst js2-block-node-kid (n i)
-  "Return child I of node N, or nil if there aren't that many."
-  (nth i (js2-block-node-kids n)))
-
-(defsubst js2-block-node-first (n)
-  "Return first child of block node N, or nil if there is none."
-  (cl-first (js2-block-node-kids n)))
-
-(defun js2-node-root (n)
-  "Return the root of the AST containing N.
-If N has no parent pointer, returns N."
-  (let ((parent (js2-node-parent n)))
-    (if parent
-        (js2-node-root parent)
-      n)))
-
-(defsubst js2-node-short-name (n)
-  "Return the short name of node N as a string, e.g. `js2-if-node'."
-  (let ((name (symbol-name (aref n 0))))
-    (if (string-prefix-p "cl-struct-" name)
-        (substring (symbol-name (aref n 0))
-                   (length "cl-struct-"))
-      name)))
-
-(defun js2-node-child-list (node)
-  "Return the child list for NODE, a Lisp list of nodes.
-Works for block nodes, array nodes, obj literals, funarg lists,
-var decls and try nodes (for catch clauses).  Note that you should call
-`js2-block-node-kids' on the function body for the body statements.
-Returns nil for zero-length child lists or unsupported nodes."
-  (cond
-   ((js2-function-node-p node)
-    (js2-function-node-params node))
-   ((js2-block-node-p node)
-    (js2-block-node-kids node))
-   ((js2-try-node-p node)
-    (js2-try-node-catch-clauses node))
-   ((js2-array-node-p node)
-    (js2-array-node-elems node))
-   ((js2-object-node-p node)
-    (js2-object-node-elems node))
-   ((js2-call-node-p node)
-    (js2-call-node-args node))
-   ((js2-new-node-p node)
-    (js2-new-node-args node))
-   ((js2-var-decl-node-p node)
-    (js2-var-decl-node-kids node))
-   (t
-    nil)))
-
-(defun js2-node-set-child-list (node kids)
-  "Set the child list for NODE to KIDS."
-   (cond
-    ((js2-function-node-p node)
-     (setf (js2-function-node-params node) kids))
-    ((js2-block-node-p node)
-     (setf (js2-block-node-kids node) kids))
-    ((js2-try-node-p node)
-     (setf (js2-try-node-catch-clauses node) kids))
-    ((js2-array-node-p node)
-     (setf (js2-array-node-elems node) kids))
-    ((js2-object-node-p node)
-     (setf (js2-object-node-elems node) kids))
-    ((js2-call-node-p node)
-     (setf (js2-call-node-args node) kids))
-    ((js2-new-node-p node)
-     (setf (js2-new-node-args node) kids))
-    ((js2-var-decl-node-p node)
-     (setf (js2-var-decl-node-kids node) kids))
-    (t
-     (error "Unsupported node type: %s" (js2-node-short-name node))))
-   kids)
-
-;; All because Common Lisp doesn't support multiple inheritance for defstructs.
-(defconst js2-paren-expr-nodes
-  '(cl-struct-js2-comp-loop-node
-    cl-struct-js2-comp-node
-    cl-struct-js2-call-node
-    cl-struct-js2-catch-node
-    cl-struct-js2-do-node
-    cl-struct-js2-elem-get-node
-    cl-struct-js2-for-in-node
-    cl-struct-js2-for-node
-    cl-struct-js2-function-node
-    cl-struct-js2-if-node
-    cl-struct-js2-let-node
-    cl-struct-js2-new-node
-    cl-struct-js2-paren-node
-    cl-struct-js2-switch-node
-    cl-struct-js2-while-node
-    cl-struct-js2-with-node
-    cl-struct-js2-xml-dot-query-node)
-  "Node types that can have a parenthesized child expression.
-In particular, nodes that respond to `js2-node-lp' and `js2-node-rp'.")
-
-(defsubst js2-paren-expr-node-p (node)
-  "Return t for nodes that typically have a parenthesized child expression.
-Useful for computing the indentation anchors for arg-lists and conditions.
-Note that it may return a false positive, for instance when NODE is
-a `js2-new-node' and there are no arguments or parentheses."
-  (memq (aref node 0) js2-paren-expr-nodes))
-
-;; Fake polymorphism... yech.
-(defun js2-node-lp (node)
-  "Return relative left-paren position for NODE, if applicable.
-For `js2-elem-get-node' structs, returns left-bracket position.
-Note that the position may be nil in the case of a parse error."
-  (cond
-   ((js2-elem-get-node-p node)
-    (js2-elem-get-node-lb node))
-   ((js2-loop-node-p node)
-    (js2-loop-node-lp node))
-   ((js2-function-node-p node)
-    (js2-function-node-lp node))
-   ((js2-if-node-p node)
-    (js2-if-node-lp node))
-   ((js2-new-node-p node)
-    (js2-new-node-lp node))
-   ((js2-call-node-p node)
-    (js2-call-node-lp node))
-   ((js2-paren-node-p node)
-    0)
-   ((js2-switch-node-p node)
-    (js2-switch-node-lp node))
-   ((js2-catch-node-p node)
-    (js2-catch-node-lp node))
-   ((js2-let-node-p node)
-    (js2-let-node-lp node))
-   ((js2-comp-node-p node)
-    0)
-   ((js2-with-node-p node)
-    (js2-with-node-lp node))
-   ((js2-xml-dot-query-node-p node)
-    (1+ (js2-infix-node-op-pos node)))
-   (t
-    (error "Unsupported node type: %s" (js2-node-short-name node)))))
-
-;; Fake polymorphism... blech.
-(defun js2-node-rp (node)
-  "Return relative right-paren position for NODE, if applicable.
-For `js2-elem-get-node' structs, returns right-bracket position.
-Note that the position may be nil in the case of a parse error."
-  (cond
-   ((js2-elem-get-node-p node)
-    (js2-elem-get-node-rb node))
-   ((js2-loop-node-p node)
-    (js2-loop-node-rp node))
-   ((js2-function-node-p node)
-    (js2-function-node-rp node))
-   ((js2-if-node-p node)
-    (js2-if-node-rp node))
-   ((js2-new-node-p node)
-    (js2-new-node-rp node))
-   ((js2-call-node-p node)
-    (js2-call-node-rp node))
-   ((js2-paren-node-p node)
-    (1- (js2-node-len node)))
-   ((js2-switch-node-p node)
-    (js2-switch-node-rp node))
-   ((js2-catch-node-p node)
-    (js2-catch-node-rp node))
-   ((js2-let-node-p node)
-    (js2-let-node-rp node))
-   ((js2-comp-node-p node)
-    (1- (js2-node-len node)))
-   ((js2-with-node-p node)
-    (js2-with-node-rp node))
-   ((js2-xml-dot-query-node-p node)
-    (1+ (js2-xml-dot-query-node-rp node)))
-   (t
-    (error "Unsupported node type: %s" (js2-node-short-name node)))))
-
-(defsubst js2-node-first-child (node)
-  "Return the first element of `js2-node-child-list' for NODE."
-  (car (js2-node-child-list node)))
-
-(defsubst js2-node-last-child (node)
-  "Return the last element of `js2-node-last-child' for NODE."
-  (car (last (js2-node-child-list node))))
-
-(defun js2-node-prev-sibling (node)
-  "Return the previous statement in parent.
-Works for parents supported by `js2-node-child-list'.
-Returns nil if NODE is not in the parent, or PARENT is
-not a supported node, or if NODE is the first child."
-  (let* ((p (js2-node-parent node))
-         (kids (js2-node-child-list p))
-         (sib (car kids)))
-    (while (and kids
-                (not (eq node (cadr kids))))
-      (setq kids (cdr kids)
-            sib (car kids)))
-    sib))
-
-(defun js2-node-next-sibling (node)
-  "Return the next statement in parent block.
-Returns nil if NODE is not in the block, or PARENT is not
-a block node, or if NODE is the last statement."
-  (let* ((p (js2-node-parent node))
-         (kids (js2-node-child-list p)))
-    (while (and kids
-                (not (eq node (car kids))))
-      (setq kids (cdr kids)))
-    (cadr kids)))
-
-(defun js2-node-find-child-before (pos parent &optional after)
-  "Find the last child that starts before POS in parent.
-If AFTER is non-nil, returns first child starting after POS.
-POS is an absolute buffer position.  PARENT is any node
-supported by `js2-node-child-list'.
-Returns nil if no applicable child is found."
-  (let ((kids (if (js2-function-node-p parent)
-                  (js2-block-node-kids (js2-function-node-body parent))
-                (js2-node-child-list parent)))
-        (beg (js2-node-abs-pos (if (js2-function-node-p parent)
-                                   (js2-function-node-body parent)
-                                 parent)))
-        kid result fn
-        (continue t))
-    (setq fn (if after '>= '<))
-    (while (and kids continue)
-      (setq kid (car kids))
-      (if (funcall fn (+ beg (js2-node-pos kid)) pos)
-          (setq result kid
-                continue (not after))
-        (setq continue after))
-      (setq kids (cdr kids)))
-    result))
-
-(defun js2-node-find-child-after (pos parent)
-  "Find first child that starts after POS in parent.
-POS is an absolute buffer position.  PARENT is any node
-supported by `js2-node-child-list'.
-Returns nil if no applicable child is found."
-  (js2-node-find-child-before pos parent 'after))
-
-(defun js2-node-replace-child (pos parent new-node)
-  "Replace node at index POS in PARENT with NEW-NODE.
-Only works for parents supported by `js2-node-child-list'."
-  (let ((kids (js2-node-child-list parent))
-        (i 0))
-    (while (< i pos)
-      (setq kids (cdr kids)
-            i (1+ i)))
-    (setcar kids new-node)
-    (js2-node-add-children parent new-node)))
-
-(defun js2-node-buffer (n)
-  "Return the buffer associated with AST N.
-Returns nil if the buffer is not set as a property on the root
-node, or if parent links were not recorded during parsing."
-  (let ((root (js2-node-root n)))
-    (and root
-         (js2-ast-root-p root)
-         (js2-ast-root-buffer root))))
-
-(defun js2-block-node-push (n kid)
-  "Push js2-node KID onto the end of js2-block-node N's child list.
-KID is always added to the -end- of the kids list.
-Function also calls `js2-node-add-children' to add the parent link."
-  (let ((kids (js2-node-child-list n)))
-    (if kids
-        (setcdr kids (nconc (cdr kids) (list kid)))
-      (js2-node-set-child-list n (list kid)))
-    (js2-node-add-children n kid)))
-
-(defun js2-node-string (node)
-  (with-current-buffer (or (js2-node-buffer node)
-                           (error "No buffer available for node %s" node))
-    (let ((pos (js2-node-abs-pos node)))
-      (buffer-substring-no-properties pos (+ pos (js2-node-len node))))))
-
-;; Container for storing the node we're looking for in a traversal.
-(js2-deflocal js2-discovered-node nil)
-
-;; Keep track of absolute node position during traversals.
-(js2-deflocal js2-visitor-offset nil)
-
-(js2-deflocal js2-node-search-point nil)
-
-(when js2-mode-dev-mode-p
-  (defun js2-find-node-at-point ()
-    (interactive)
-    (let ((node (js2-node-at-point)))
-      (message "%s" (or node "No node found at point"))))
-  (defun js2-node-name-at-point ()
-    (interactive)
-    (let ((node (js2-node-at-point)))
-      (message "%s" (if node
-                        (js2-node-short-name node)
-                      "No node found at point.")))))
-
-(defun js2-node-at-point (&optional pos skip-comments)
-  "Return AST node at POS, a buffer position, defaulting to current point.
-The `js2-mode-ast' variable must be set to the current parse tree.
-Signals an error if the AST (`js2-mode-ast') is nil.
-Always returns a node - if it can't find one, it returns the root.
-If SKIP-COMMENTS is non-nil, comment nodes are ignored."
-  (let ((ast js2-mode-ast)
-        result)
-    (unless ast
-      (error "No JavaScript AST available"))
-    ;; Look through comments first, since they may be inside nodes that
-    ;; would otherwise report a match.
-    (setq pos (or pos (point))
-          result (if (> pos (js2-node-abs-end ast))
-                     ast
-                   (if (not skip-comments)
-                       (js2-comment-at-point pos))))
-    (unless result
-      (setq js2-discovered-node nil
-            js2-visitor-offset 0
-            js2-node-search-point pos)
-      (unwind-protect
-          (catch 'js2-visit-done
-            (js2-visit-ast ast #'js2-node-at-point-visitor))
-        (setq js2-visitor-offset nil
-              js2-node-search-point nil))
-      (setq result js2-discovered-node))
-    ;; may have found a comment beyond end of last child node,
-    ;; since visiting the ast-root looks at the comment-list last.
-    (if (and skip-comments
-             (js2-comment-node-p result))
-        (setq result nil))
-    (or result js2-mode-ast)))
-
-(defun js2-node-at-point-visitor (node end-p)
-  (let ((rel-pos (js2-node-pos node))
-        abs-pos
-        abs-end
-        (point js2-node-search-point))
-    (cond
-     (end-p
-      ;; this evaluates to a non-nil return value, even if it's zero
-      (cl-decf js2-visitor-offset rel-pos))
-     ;; we already looked for comments before visiting, and don't want them now
-     ((js2-comment-node-p node)
-      nil)
-     (t
-      (setq abs-pos (cl-incf js2-visitor-offset rel-pos)
-            ;; we only want to use the node if the point is before
-            ;; the last character position in the node, so we decrement
-            ;; the absolute end by 1.
-            abs-end (+ abs-pos (js2-node-len node) -1))
-      (cond
-       ;; If this node starts after search-point, stop the search.
-       ((> abs-pos point)
-        (throw 'js2-visit-done nil))
-       ;; If this node ends before the search-point, don't check kids.
-       ((> point abs-end)
-        nil)
-       (t
-        ;; Otherwise point is within this node, possibly in a child.
-        (setq js2-discovered-node node)
-        t))))))  ; keep processing kids to look for more specific match
-
-(defsubst js2-block-comment-p (node)
-  "Return non-nil if NODE is a comment node of format `jsdoc' or `block'."
-  (and (js2-comment-node-p node)
-       (memq (js2-comment-node-format node) '(jsdoc block))))
-
-;; TODO:  put the comments in a vector and binary-search them instead
-(defun js2-comment-at-point (&optional pos)
-  "Look through scanned comment nodes for one containing POS.
-POS is a buffer position that defaults to current point.
-Function returns nil if POS was not in any comment node."
-  (let ((ast js2-mode-ast)
-        (x (or pos (point)))
-        beg end)
-    (unless ast
-      (error "No JavaScript AST available"))
-    (catch 'done
-      ;; Comments are stored in lexical order.
-      (dolist (comment (js2-ast-root-comments ast) nil)
-        (setq beg (js2-node-abs-pos comment)
-              end (+ beg (js2-node-len comment)))
-        (if (and (>= x beg)
-                 (<= x end))
-          (throw 'done comment))))))
-
-(defun js2-comments-between (start end comments-list)
-  "Return comment nodes between START and END, nil if not found.
-START and END are absolute positions in current buffer.
-COMMENTS-LIST is the comments list to check."
-  (let (comments c-start c-end)
-    (nreverse
-      (dolist (comment comments-list comments)
-        (setq c-start (js2-node-abs-pos comment)
-              c-end (1- (+ c-start (js2-node-len comment))))
-        (unless (or (< c-end start)
-                    (> c-start end))
-          (push comment comments))))))
-
-(defun js2-mode-find-parent-fn (node)
-  "Find function enclosing NODE.
-Returns nil if NODE is not inside a function."
-  (setq node (js2-node-parent node))
-  (while (and node (not (js2-function-node-p node)))
-    (setq node (js2-node-parent node)))
-  (and (js2-function-node-p node) node))
-
-(defun js2-mode-find-enclosing-fn (node)
-  "Find function or root enclosing NODE."
-  (if (js2-ast-root-p node)
-      node
-    (setq node (js2-node-parent node))
-    (while (not (or (js2-ast-root-p node)
-                    (js2-function-node-p node)))
-      (setq node (js2-node-parent node)))
-    node))
-
- (defun js2-mode-find-enclosing-node (beg end)
-  "Find node fully enclosing BEG and END."
-  (let ((node (js2-node-at-point beg))
-        pos
-        (continue t))
-    (while continue
-      (if (or (js2-ast-root-p node)
-              (and
-               (<= (setq pos (js2-node-abs-pos node)) beg)
-               (>= (+ pos (js2-node-len node)) end)))
-          (setq continue nil)
-        (setq node (js2-node-parent node))))
-    node))
-
-(defun js2-node-parent-script-or-fn (node)
-  "Find script or function immediately enclosing NODE.
-If NODE is the ast-root, returns nil."
-  (if (js2-ast-root-p node)
-      nil
-    (setq node (js2-node-parent node))
-    (while (and node (not (or (js2-function-node-p node)
-                              (js2-script-node-p node))))
-      (setq node (js2-node-parent node)))
-    node))
-
-(defun js2-node-is-descendant (node ancestor)
-  "Return t if NODE is a descendant of ANCESTOR."
-  (while (and node
-              (not (eq node ancestor)))
-    (setq node (js2-node-parent node)))
-  node)
-
-;;; visitor infrastructure
-
-(defun js2-visit-none (_node _callback)
-  "Visitor for AST node that have no node children."
-  nil)
-
-(defun js2-print-none (_node _indent)
-  "Visitor for AST node with no printed representation.")
-
-(defun js2-print-body (node indent)
-  "Print a statement, or a block without braces."
-  (if (js2-block-node-p node)
-      (dolist (kid (js2-block-node-kids node))
-        (js2-print-ast kid indent))
-    (js2-print-ast node indent)))
-
-(defun js2-print-list (args &optional delimiter)
-  (cl-loop with len = (length args)
-           for arg in args
-           for count from 1
-           do
-           (when arg (js2-print-ast arg 0))
-           (if (< count len)
-               (insert (or delimiter ", ")))))
-
-(defun js2-print-tree (ast)
-  "Prints an AST to the current buffer.
-Makes `js2-ast-parent-nodes' available to the printer functions."
-  (let ((max-lisp-eval-depth (max max-lisp-eval-depth 1500)))
-    (js2-print-ast ast)))
-
-(defun js2-print-ast (node &optional indent)
-  "Helper function for printing AST nodes.
-Requires `js2-ast-parent-nodes' to be non-nil.
-You should use `js2-print-tree' instead of this function."
-  (let ((printer (get (aref node 0) 'js2-printer))
-        (i (or indent 0)))
-    ;; TODO:  wedge comments in here somewhere
-    (if printer
-        (funcall printer node i))))
-
-(defconst js2-side-effecting-tokens
-  (let ((tokens (make-bool-vector js2-num-tokens nil)))
-    (dolist (tt (list js2-ASSIGN
-                      js2-ASSIGN_ADD
-                      js2-ASSIGN_BITAND
-                      js2-ASSIGN_BITOR
-                      js2-ASSIGN_BITXOR
-                      js2-ASSIGN_DIV
-                      js2-ASSIGN_LSH
-                      js2-ASSIGN_MOD
-                      js2-ASSIGN_MUL
-                      js2-ASSIGN_RSH
-                      js2-ASSIGN_SUB
-                      js2-ASSIGN_URSH
-                      js2-ASSIGN_EXPON
-                      js2-BLOCK
-                      js2-BREAK
-                      js2-CALL
-                      js2-CATCH
-                      js2-CATCH_SCOPE
-                      js2-CLASS
-                      js2-CONST
-                      js2-CONTINUE
-                      js2-DEBUGGER
-                      js2-DEC
-                      js2-DELPROP
-                      js2-DEL_REF
-                      js2-DO
-                      js2-ELSE
-                      js2-EMPTY
-                      js2-ENTERWITH
-                      js2-EXPORT
-                      js2-EXPR_RESULT
-                      js2-FINALLY
-                      js2-FOR
-                      js2-FUNCTION
-                      js2-GOTO
-                      js2-IF
-                      js2-IFEQ
-                      js2-IFNE
-                      js2-IMPORT
-                      js2-INC
-                      js2-JSR
-                      js2-LABEL
-                      js2-LEAVEWITH
-                      js2-LET
-                      js2-LETEXPR
-                      js2-LOCAL_BLOCK
-                      js2-LOOP
-                      js2-NEW
-                      js2-REF_CALL
-                      js2-RETHROW
-                      js2-RETURN
-                      js2-RETURN_RESULT
-                      js2-SEMI
-                      js2-SETELEM
-                      js2-SETELEM_OP
-                      js2-SETNAME
-                      js2-SETPROP
-                      js2-SETPROP_OP
-                      js2-SETVAR
-                      js2-SET_REF
-                      js2-SET_REF_OP
-                      js2-SWITCH
-                      js2-TARGET
-                      js2-THROW
-                      js2-TRY
-                      js2-VAR
-                      js2-WHILE
-                      js2-WITH
-                      js2-WITHEXPR
-                      js2-YIELD))
-      (aset tokens tt t))
-    tokens))
-
-(defun js2-node-has-side-effects (node)
-  "Return t if NODE has side effects."
-  (when node  ; makes it easier to handle malformed expressions
-    (let ((tt (js2-node-type node)))
-      (cond
-       ;; This doubtless needs some work, since EXPR_VOID is used
-       ;; in several ways in Rhino and I may not have caught them all.
-       ;; I'll wait for people to notice incorrect warnings.
-       ((and (= tt js2-EXPR_VOID)
-             (js2-expr-stmt-node-p node)) ; but not if EXPR_RESULT
-        (let ((expr (js2-expr-stmt-node-expr node)))
-          (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) t)
-       ((= tt js2-COMMA)
-        (js2-node-has-side-effects (js2-infix-node-right node)))
-       ((or (= tt js2-AND)
-            (= tt js2-OR))
-        (or (js2-node-has-side-effects (js2-infix-node-right node))
-            (js2-node-has-side-effects (js2-infix-node-left node))))
-       ((= tt js2-HOOK)
-        (and (js2-node-has-side-effects (js2-cond-node-true-expr node))
-             (js2-node-has-side-effects (js2-cond-node-false-expr node))))
-       ((js2-paren-node-p node)
-        (js2-node-has-side-effects (js2-paren-node-expr node)))
-       ((= tt js2-ERROR) ; avoid cascaded error messages
-        nil)
-       ((or (and js2-instanceof-has-side-effects (= tt js2-INSTANCEOF))
-            (and js2-getprop-has-side-effects (= tt js2-GETPROP)))
-        t)
-       (t
-        (aref js2-side-effecting-tokens tt))))))
-
-(defconst js2-stmt-node-types
-  (list js2-BLOCK
-        js2-BREAK
-        js2-CONTINUE
-        js2-DEFAULT  ; e4x "default xml namespace" statement
-        js2-DO
-        js2-EXPORT
-        js2-EXPR_RESULT
-        js2-EXPR_VOID
-        js2-FOR
-        js2-IF
-        js2-IMPORT
-        js2-RETURN
-        js2-SWITCH
-        js2-THROW
-        js2-TRY
-        js2-WHILE
-        js2-WITH)
-  "Node types that only appear in statement contexts.
-The list does not include nodes that always appear as the child
-of another specific statement type, such as switch-cases,
-catch and finally blocks, and else-clauses.  The list also excludes
-nodes like yield, let and var, which may appear in either expression
-or statement context, and in the latter context always have a
-`js2-expr-stmt-node' parent.  Finally, the list does not include
-functions or scripts, which are treated separately from statements
-by the JavaScript parser and runtime.")
-
-(defun js2-stmt-node-p (node)
-  "Heuristic for figuring out if NODE is a statement.
-Some node types can appear in either an expression context or a
-statement context, e.g. let-nodes, yield-nodes, and var-decl nodes.
-For these node types in a statement context, the parent will be a
-`js2-expr-stmt-node'.
-Functions aren't included in the check."
-  (memq (js2-node-type node) js2-stmt-node-types))
-
-(defun js2-mode-find-first-stmt (node)
-  "Search upward starting from NODE looking for a statement.
-For purposes of this function, a `js2-function-node' counts."
-  (while (not (or (js2-stmt-node-p node)
-                  (js2-function-node-p node)))
-    (setq node (js2-node-parent node)))
-  node)
-
-(defun js2-node-parent-stmt (node)
-  "Return the node's first ancestor that is a statement.
-Returns nil if NODE is a `js2-ast-root'.  Note that any expression
-appearing in a statement context will have a parent that is a
-`js2-expr-stmt-node' that will be returned by this function."
-  (let ((parent (js2-node-parent node)))
-    (if (or (null parent)
-            (js2-stmt-node-p parent)
-            (and (js2-function-node-p parent)
-                 (eq (js2-function-node-form parent) 'FUNCTION_STATEMENT)))
-        parent
-      (js2-node-parent-stmt parent))))
-
-;; In the Mozilla Rhino sources, Roshan James writes:
-;;  Does consistent-return analysis on the function body when strict mode is
-;;  enabled.
-;;
-;;    function (x) { return (x+1) }
-;;
-;;  is ok, but
-;;
-;;    function (x) { if (x < 0) return (x+1); }
-;;
-;;  is not because the function can potentially return a value when the
-;;  condition is satisfied and if not, the function does not explicitly
-;;  return a value.
-;;
-;;  This extends to checking mismatches such as "return" and "return <value>"
-;;  used in the same function. Warnings are not emitted if inconsistent
-;;  returns exist in code that can be statically shown to be unreachable.
-;;  Ex.
-;;    function (x) { while (true) { ... if (..) { return value } ... } }
-;;
-;;  emits no warning. However if the loop had a break statement, then a
-;;  warning would be emitted.
-;;
-;;  The consistency analysis looks at control structures such as loops, ifs,
-;;  switch, try-catch-finally blocks, examines the reachable code paths and
-;;  warns the user about an inconsistent set of termination possibilities.
-;;
-;;  These flags enumerate the possible ways a statement/function can
-;;  terminate. These flags are used by endCheck() and by the Parser to
-;;  detect inconsistent return usage.
-;;
-;;  END_UNREACHED is reserved for code paths that are assumed to always be
-;;  able to execute (example: throw, continue)
-;;
-;;  END_DROPS_OFF indicates if the statement can transfer control to the
-;;  next one. Statement such as return dont. A compound statement may have
-;;  some branch that drops off control to the next statement.
-;;
-;;  END_RETURNS indicates that the statement can return with no value.
-;;  END_RETURNS_VALUE indicates that the statement can return a value.
-;;
-;;  A compound statement such as
-;;  if (condition) {
-;;    return value;
-;;  }
-;;  Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
-
-(defconst js2-END_UNREACHED 0)
-(defconst js2-END_DROPS_OFF 1)
-(defconst js2-END_RETURNS 2)
-(defconst js2-END_RETURNS_VALUE 4)
-(defconst js2-END_YIELDS 8)
-
-(defun js2-has-consistent-return-usage (node)
-  "Check that every return usage in a function body is consistent.
-Returns t if the function satisfies strict mode requirement."
-  (let ((n (js2-end-check node)))
-    ;; either it doesn't return a value in any branch...
-    (or (js2-flag-not-set-p n js2-END_RETURNS_VALUE)
-        ;; or it returns a value (or is unreached) at every branch
-        (js2-flag-not-set-p n (logior js2-END_DROPS_OFF
-                                      js2-END_RETURNS
-                                      js2-END_YIELDS)))))
-
-(defun js2-end-check-if (node)
-  "Ensure that return usage in then/else blocks is consistent.
-If there is no else block, then the return statement can fall through.
-Returns logical OR of END_* flags"
-  (let ((th (js2-if-node-then-part node))
-        (el (js2-if-node-else-part node)))
-    (if (null th)
-        js2-END_UNREACHED
-      (logior (js2-end-check th) (if el
-                                     (js2-end-check el)
-                                   js2-END_DROPS_OFF)))))
-
-(defun js2-end-check-switch (node)
-  "Consistency of return statements is checked between the case statements.
-If there is no default, then the switch can fall through. If there is a
-default, we check to see if all code paths in the default return or if
-there is a code path that can fall through.
-Returns logical OR of END_* flags."
-  (let ((rv js2-END_UNREACHED)
-        default-case)
-    ;; examine the cases
-    (catch 'break
-      (dolist (c (js2-switch-node-cases node))
-        (if (js2-case-node-expr c)
-            (js2-set-flag rv (js2-end-check-block c))
-          (setq default-case c)
-          (throw 'break nil))))
-    ;; we don't care how the cases drop into each other
-    (js2-clear-flag rv js2-END_DROPS_OFF)
-    ;; examine the default
-    (js2-set-flag rv (if default-case
-                         (js2-end-check default-case)
-                       js2-END_DROPS_OFF))
-    rv))
-
-(defun js2-end-check-try (node)
- "If the block has a finally, return consistency is checked in the
-finally block. If all code paths in the finally return, then the
-returns in the try-catch blocks don't matter. If there is a code path
-that does not return or if there is no finally block, the returns
-of the try and catch blocks are checked for mismatch.
-Returns logical OR of END_* flags."
- (let ((finally (js2-try-node-finally-block node))
-       rv)
-   ;; check the finally if it exists
-   (setq rv (if finally
-                (js2-end-check (js2-finally-node-body finally))
-              js2-END_DROPS_OFF))
-   ;; If the finally block always returns, then none of the returns
-   ;; in the try or catch blocks matter.
-   (when (js2-flag-set-p rv js2-END_DROPS_OFF)
-     (js2-clear-flag rv js2-END_DROPS_OFF)
-     ;; examine the try block
-     (js2-set-flag rv (js2-end-check (js2-try-node-try-block node)))
-     ;; check each catch block
-     (dolist (cb (js2-try-node-catch-clauses node))
-       (js2-set-flag rv (js2-end-check cb))))
-   rv))
-
-(defun js2-end-check-loop (node)
-  "Return statement in the loop body must be consistent.
-The default assumption for any kind of a loop is that it will eventually
-terminate.  The only exception is a loop with a constant true condition.
-Code that follows such a loop is examined only if one can determine
-statically that there is a break out of the loop.
-
-    for(... ; ... ; ...) {}
-    for(... in ... ) {}
-    while(...) { }
-    do { } while(...)
-
-Returns logical OR of END_* flags."
-  (let ((rv (js2-end-check (js2-loop-node-body node)))
-        (condition (cond
-                    ((js2-while-node-p node)
-                     (js2-while-node-condition node))
-                     ((js2-do-node-p node)
-                      (js2-do-node-condition node))
-                     ((js2-for-node-p node)
-                      (js2-for-node-condition node)))))
-
-    ;; check to see if the loop condition is always true
-    (if (and condition
-             (eq (js2-always-defined-boolean-p condition) 'ALWAYS_TRUE))
-        (js2-clear-flag rv js2-END_DROPS_OFF))
-
-    ;; look for effect of breaks
-    (js2-set-flag rv (js2-node-get-prop node
-                                        'CONTROL_BLOCK_PROP
-                                        js2-END_UNREACHED))
-    rv))
-
-(defun js2-end-check-block (node)
-  "A general block of code is examined statement by statement.
-If any statement (even a compound one) returns in all branches, then
-subsequent statements are not examined.
-Returns logical OR of END_* flags."
-  (let* ((rv js2-END_DROPS_OFF)
-         (kids (js2-block-node-kids node))
-         (n (car kids)))
-    ;; Check each statement.  If the statement can continue onto the next
-    ;; one (i.e. END_DROPS_OFF is set), then check the next statement.
-    (while (and n (js2-flag-set-p rv js2-END_DROPS_OFF))
-      (js2-clear-flag rv js2-END_DROPS_OFF)
-      (js2-set-flag rv (js2-end-check n))
-      (setq kids (cdr kids)
-            n (car kids)))
-    rv))
-
-(defun js2-end-check-label (node)
-  "A labeled statement implies that there may be a break to the label.
-The function processes the labeled statement and then checks the
-CONTROL_BLOCK_PROP property to see if there is ever a break to the
-particular label.
-Returns logical OR of END_* flags."
-  (let ((rv (js2-end-check (js2-labeled-stmt-node-stmt node))))
-    (logior rv (js2-node-get-prop node
-                                  'CONTROL_BLOCK_PROP
-                                  js2-END_UNREACHED))))
-
-(defun js2-end-check-break (node)
-  "When a break is encountered annotate the statement being broken
-out of by setting its CONTROL_BLOCK_PROP property.
-Returns logical OR of END_* flags."
-  (and (js2-break-node-target node)
-       (js2-node-set-prop (js2-break-node-target node)
-                          'CONTROL_BLOCK_PROP
-                          js2-END_DROPS_OFF))
-  js2-END_UNREACHED)
-
-(defun js2-end-check (node)
-  "Examine the body of a function, doing a basic reachability analysis.
-Returns a combination of flags END_* flags that indicate
-how the function execution can terminate. These constitute only the
-pessimistic set of termination conditions. It is possible that at
-runtime certain code paths will never be actually taken. Hence this
-analysis will flag errors in cases where there may not be errors.
-Returns logical OR of END_* flags"
-  (let (kid)
-    (cond
-     ((js2-break-node-p node)
-      (js2-end-check-break node))
-     ((js2-expr-stmt-node-p node)
-      (if (setq kid (js2-expr-stmt-node-expr node))
-          (js2-end-check kid)
-        js2-END_DROPS_OFF))
-     ((or (js2-continue-node-p node)
-          (js2-throw-node-p node))
-      js2-END_UNREACHED)
-     ((js2-return-node-p node)
-      (if (setq kid (js2-return-node-retval node))
-          js2-END_RETURNS_VALUE
-        js2-END_RETURNS))
-     ((js2-loop-node-p node)
-      (js2-end-check-loop node))
-     ((js2-switch-node-p node)
-      (js2-end-check-switch node))
-     ((js2-labeled-stmt-node-p node)
-      (js2-end-check-label node))
-     ((js2-if-node-p node)
-      (js2-end-check-if node))
-     ((js2-try-node-p node)
-      (js2-end-check-try node))
-     ((js2-block-node-p node)
-      (if (null (js2-block-node-kids node))
-          js2-END_DROPS_OFF
-        (js2-end-check-block node)))
-     ((js2-yield-node-p node)
-      js2-END_YIELDS)
-     (t
-      js2-END_DROPS_OFF))))
-
-(defun js2-always-defined-boolean-p (node)
-  "Check if NODE always evaluates to true or false in boolean context.
-Returns 'ALWAYS_TRUE, 'ALWAYS_FALSE, or nil if it's neither always true
-nor always false."
-  (let ((tt (js2-node-type node))
-        num)
-    (cond
-     ((or (= tt js2-FALSE) (= tt js2-NULL))
-      'ALWAYS_FALSE)
-     ((= tt js2-TRUE)
-      'ALWAYS_TRUE)
-     ((= tt js2-NUMBER)
-      (setq num (js2-number-node-num-value node))
-      (if (and (not (eq num 0.0e+NaN))
-               (not (zerop num)))
-          'ALWAYS_TRUE
-        'ALWAYS_FALSE))
-     (t
-      nil))))
-
-;;; Scanner -- a port of Mozilla Rhino's lexer.
-;; Corresponds to Rhino files Token.java and TokenStream.java.
-
-(defvar js2-tokens nil
-  "List of all defined token names.")  ; initialized in `js2-token-names'
-
-(defconst js2-token-names
-  (let* ((names (make-vector js2-num-tokens -1))
-         (case-fold-search nil)  ; only match js2-UPPER_CASE
-         (syms (apropos-internal "^js2-\\(?:[[:upper:]_]+\\)")))
-    (cl-loop for sym in syms
-             for i from 0
-             do
-             (unless (or (memq sym '(js2-EOF_CHAR js2-ERROR))
-                         (not (boundp sym)))
-               (aset names (symbol-value sym)           ; code, e.g. 152
-                     (downcase
-                      (substring (symbol-name sym) 4))) ; name, e.g. "let"
-               (push sym js2-tokens)))
-    names)
-  "Vector mapping int values to token string names, sans `js2-' prefix.")
-
-(defun js2-tt-name (tok)
-  "Return a string name for TOK, a token symbol or code.
-Signals an error if it's not a recognized token."
-  (let ((code tok))
-    (if (symbolp tok)
-        (setq code (symbol-value tok)))
-    (if (eq code -1)
-        "ERROR"
-      (if (and (numberp code)
-               (not (cl-minusp code))
-               (< code js2-num-tokens))
-          (aref js2-token-names code)
-        (error "Invalid token: %s" code)))))
-
-(defsubst js2-tt-sym (tok)
-  "Return symbol for TOK given its code, e.g. 'js2-LP for code 86."
-  (intern (js2-tt-name tok)))
-
-(defconst js2-token-codes
-  (let ((table (make-hash-table :test 'eq :size 256)))
-    (cl-loop for name across js2-token-names
-             for sym = (intern (concat "js2-" (upcase name)))
-             do
-             (puthash sym (symbol-value sym) table))
-    ;; clean up a few that are "wrong" in Rhino's token codes
-    (puthash 'js2-DELETE js2-DELPROP table)
-    table)
-  "Hashtable mapping token type symbols to their bytecodes.")
-
-(defsubst js2-tt-code (sym)
-  "Return code for token symbol SYM, e.g. 86 for 'js2-LP."
-  (or (gethash sym js2-token-codes)
-      (error "Invalid token symbol: %s " sym)))  ; signal code bug
-
-(defun js2-report-scan-error (msg &optional no-throw beg len)
-  (setf (js2-token-end (js2-current-token)) js2-ts-cursor)
-  (js2-report-error msg nil
-                    (or beg (js2-current-token-beg))
-                    (or len (js2-current-token-len)))
-  (unless no-throw
-    (throw 'return js2-ERROR)))
-
-(defun js2-set-string-from-buffer (token)
-  "Set `string' and `end' slots for TOKEN, return the string."
-  (setf (js2-token-end token) js2-ts-cursor
-        (js2-token-string token) (js2-collect-string js2-ts-string-buffer)))
-
-;; TODO:  could potentially avoid a lot of consing by allocating a
-;; char buffer the way Rhino does.
-(defsubst js2-add-to-string (c)
-  (push c js2-ts-string-buffer))
-
-;; Note that when we "read" the end-of-file, we advance js2-ts-cursor
-;; to (1+ (point-max)), which lets the scanner treat end-of-file like
-;; any other character:  when it's not part of the current token, we
-;; unget it, allowing it to be read again by the following call.
-(defsubst js2-unget-char ()
-  (cl-decf js2-ts-cursor))
-
-;; Rhino distinguishes \r and \n line endings.  We don't need to
-;; because we only scan from Emacs buffers, which always use \n.
-(defun js2-get-char ()
-  "Read and return the next character from the input buffer.
-Increments `js2-ts-lineno' if the return value is a newline char.
-Updates `js2-ts-cursor' to the point after the returned char.
-Returns `js2-EOF_CHAR' if we hit the end of the buffer.
-Also updates `js2-ts-hit-eof' and `js2-ts-line-start' as needed."
-  (let (c)
-    ;; check for end of buffer
-    (if (>= js2-ts-cursor (point-max))
-        (setq js2-ts-hit-eof t
-              js2-ts-cursor (1+ js2-ts-cursor)
-              c js2-EOF_CHAR)  ; return value
-      ;; otherwise read next char
-      (setq c (char-before (cl-incf js2-ts-cursor)))
-      ;; if we read a newline, update counters
-      (if (= c ?\n)
-          (setq js2-ts-line-start js2-ts-cursor
-                js2-ts-lineno (1+ js2-ts-lineno)))
-      ;; TODO:  skip over format characters
-      c)))
-
-(defun js2-read-unicode-escape ()
-  "Read a \\uNNNN sequence from the input.
-Assumes the ?\ and ?u have already been read.
-Returns the unicode character, or nil if it wasn't a valid character.
-Doesn't change the values of any scanner variables."
-  ;; I really wish I knew a better way to do this, but I can't
-  ;; find the Emacs function that takes a 16-bit int and converts
-  ;; it to a Unicode/utf-8 character.  So I basically eval it with (read).
-  ;; Have to first check that it's 4 hex characters or it may stop
-  ;; the read early.
-  (ignore-errors
-    (let ((s (buffer-substring-no-properties js2-ts-cursor
-                                             (+ 4 js2-ts-cursor))))
-      (if (string-match "[0-9a-fA-F]\\{4\\}" s)
-          (read (concat "?\\u" s))))))
-
-(defun js2-match-char (test)
-  "Consume and return next character if it matches TEST, a character.
-Returns nil and consumes nothing if TEST is not the next character."
-  (let ((c (js2-get-char)))
-    (if (eq c test)
-        t
-      (js2-unget-char)
-      nil)))
-
-(defun js2-peek-char ()
-  (prog1
-      (js2-get-char)
-    (js2-unget-char)))
-
-(defun js2-identifier-start-p (c)
-  "Is C a valid start to an ES5 Identifier?
-See http://es5.github.io/#x7.6"
-  (or
-   (memq c '(?$ ?_))
-   (memq (get-char-code-property c 'general-category)
-         ;; Letters
-         '(Lu Ll Lt Lm Lo Nl))))
-
-(defun js2-identifier-part-p (c)
-  "Is C a valid part of an ES5 Identifier?
-See http://es5.github.io/#x7.6"
-  (or
-   (memq c '(?$ ?_ ?\u200c  ?\u200d))
-   (memq (get-char-code-property c 'general-category)
-         '(;; Letters
-           Lu Ll Lt Lm Lo Nl
-           ;; Combining Marks
-           Mn Mc
-           ;; Digits
-           Nd
-           ;; Connector Punctuation
-           Pc))))
-
-(defun js2-alpha-p (c)
-  (cond ((and (<= ?A c) (<= c ?Z)) t)
-        ((and (<= ?a c) (<= c ?z)) t)
-        (t nil)))
-
-(defsubst js2-digit-p (c)
-  (and (<= ?0 c) (<= c ?9)))
-
-(defun js2-js-space-p (c)
-  (if (<= c 127)
-      (memq c '(#x20 #x9 #xB #xC #xD))
-    (or
-     (eq c #xA0)
-     ;; TODO:  change this nil to check for Unicode space character
-     nil)))
-
-(defconst js2-eol-chars (list js2-EOF_CHAR ?\n ?\r))
-
-(defun js2-skip-line ()
-  "Skip to end of line."
-  (while (not (memq (js2-get-char) js2-eol-chars)))
-  (js2-unget-char)
-  (setf (js2-token-end (js2-current-token)) js2-ts-cursor))
-
-(defun js2-init-scanner (&optional buf line)
-  "Create token stream for BUF starting on LINE.
-BUF defaults to `current-buffer' and LINE defaults to 1.
-
-A buffer can only have one scanner active at a time, which yields
-dramatically simpler code than using a defstruct.  If you need to
-have simultaneous scanners in a buffer, copy the regions to scan
-into temp buffers."
-  (with-current-buffer (or buf (current-buffer))
-    (setq js2-ts-dirty-line nil
-          js2-ts-hit-eof nil
-          js2-ts-line-start 0
-          js2-ts-lineno (or line 1)
-          js2-ts-line-end-char -1
-          js2-ts-cursor (point-min)
-          js2-ti-tokens (make-vector js2-ti-ntokens nil)
-          js2-ti-tokens-cursor 0
-          js2-ti-lookahead 0
-          js2-ts-is-xml-attribute nil
-          js2-ts-xml-is-tag-content nil
-          js2-ts-xml-open-tags-count 0
-          js2-ts-string-buffer nil)))
-
-;; This function uses the cached op, string and number fields in
-;; TokenStream; if getToken has been called since the passed token
-;; was scanned, the op or string printed may be incorrect.
-(defun js2-token-to-string (token)
-  ;; Not sure where this function is used in Rhino.  Not tested.
-  (if (not js2-debug-print-trees)
-      ""
-    (let ((name (js2-tt-name token)))
-      (cond
-       ((memq token '(js2-STRING js2-REGEXP js2-NAME
-                      js2-TEMPLATE_HEAD js2-NO_SUBS_TEMPLATE))
-        (concat name " `" (js2-current-token-string) "'"))
-       ((eq token js2-NUMBER)
-        (format "NUMBER %g" (js2-token-number (js2-current-token))))
-       (t
-        name)))))
-
-(defconst js2-keywords
-  '(break
-    case catch class const continue
-    debugger default delete do
-    else extends export
-    false finally for function
-    if in instanceof import
-    let
-    new null
-    return
-    super switch
-    this throw true try typeof
-    var void
-    while with
-    yield))
-
-;; Token names aren't exactly the same as the keywords, unfortunately.
-;; E.g. delete is js2-DELPROP.
-(defconst js2-kwd-tokens
-  (let ((table (make-vector js2-num-tokens nil))
-        (tokens
-         (list js2-BREAK
-               js2-CASE js2-CATCH js2-CLASS js2-CONST js2-CONTINUE
-               js2-DEBUGGER js2-DEFAULT js2-DELPROP js2-DO
-               js2-ELSE js2-EXPORT
-               js2-ELSE js2-EXTENDS js2-EXPORT
-               js2-FALSE js2-FINALLY js2-FOR js2-FUNCTION
-               js2-IF js2-IN js2-INSTANCEOF js2-IMPORT
-               js2-LET
-               js2-NEW js2-NULL
-               js2-RETURN
-               js2-SUPER js2-SWITCH
-               js2-THIS js2-THROW js2-TRUE js2-TRY js2-TYPEOF
-               js2-VAR
-               js2-WHILE js2-WITH
-               js2-YIELD)))
-    (dolist (i tokens)
-      (aset table i 'font-lock-keyword-face))
-    (aset table js2-STRING 'font-lock-string-face)
-    (aset table js2-REGEXP 'font-lock-string-face)
-    (aset table js2-NO_SUBS_TEMPLATE 'font-lock-string-face)
-    (aset table js2-TEMPLATE_HEAD 'font-lock-string-face)
-    (aset table js2-COMMENT 'font-lock-comment-face)
-    (aset table js2-THIS 'font-lock-builtin-face)
-    (aset table js2-SUPER 'font-lock-builtin-face)
-    (aset table js2-VOID 'font-lock-constant-face)
-    (aset table js2-NULL 'font-lock-constant-face)
-    (aset table js2-TRUE 'font-lock-constant-face)
-    (aset table js2-FALSE 'font-lock-constant-face)
-    (aset table js2-NOT 'font-lock-negation-char-face)
-    table)
-  "Vector whose values are non-nil for tokens that are keywords.
-The values are default faces to use for highlighting the keywords.")
-
-;; FIXME: Support strict mode-only future reserved words, after we know
-;; which parts scopes are in strict mode, and which are not.
-(defconst js2-reserved-words '(class enum export extends import static super)
-  "Future reserved keywords in ECMAScript 5.1.")
-
-(defconst js2-keyword-names
-  (let ((table (make-hash-table :test 'equal)))
-    (cl-loop for k in js2-keywords
-             do (puthash
-                 (symbol-name k)                            ; instanceof
-                 (intern (concat "js2-"
-                                 (upcase (symbol-name k)))) ; js2-INSTANCEOF
-                 table))
-    table)
-  "JavaScript keywords by name, mapped to their symbols.")
-
-(defconst js2-reserved-word-names
-  (let ((table (make-hash-table :test 'equal)))
-    (cl-loop for k in js2-reserved-words
-             do
-             (puthash (symbol-name k) 'js2-RESERVED table))
-    table)
-  "JavaScript reserved words by name, mapped to 'js2-RESERVED.")
-
-(defun js2-collect-string (buf)
-  "Convert BUF, a list of chars, to a string.
-Reverses BUF before converting."
-  (if buf
-      (apply #'string (nreverse buf))
-    ""))
-
-(defun js2-string-to-keyword (s)
-  "Return token for S, a string, if S is a keyword or reserved word.
-Returns a symbol such as 'js2-BREAK, or nil if not keyword/reserved."
-  (or (gethash s js2-keyword-names)
-      (gethash s js2-reserved-word-names)))
-
-(defsubst js2-ts-set-char-token-bounds (token)
-  "Used when next token is one character."
-  (setf (js2-token-beg token) (1- js2-ts-cursor)
-        (js2-token-end token) js2-ts-cursor))
-
-(defsubst js2-ts-return (token type)
-  "Update the `end' and `type' slots of TOKEN,
-then throw `return' with value TYPE."
-  (setf (js2-token-end token) js2-ts-cursor
-        (js2-token-type token) type)
-  (throw 'return type))
-
-(defun js2-x-digit-to-int (c accumulator)
-  "Build up a hex number.
-If C is a hexadecimal digit, return ACCUMULATOR * 16 plus
-corresponding number.  Otherwise return -1."
-  (catch 'return
-    (catch 'check
-      ;; Use 0..9 < A..Z < a..z
-      (cond
-       ((<= c ?9)
-        (cl-decf c ?0)
-        (if (<= 0 c)
-            (throw 'check nil)))
-       ((<= c ?F)
-        (when (<= ?A c)
-          (cl-decf c (- ?A 10))
-          (throw 'check nil)))
-       ((<= c ?f)
-        (when (<= ?a c)
-          (cl-decf c (- ?a 10))
-          (throw 'check nil))))
-      (throw 'return -1))
-    (logior c (lsh accumulator 4))))
-
-(defun js2-get-token (&optional modifier)
-  "If `js2-ti-lookahead' is zero, call scanner to get new token.
-Otherwise, move `js2-ti-tokens-cursor' and return the type of
-next saved token.
-
-This function will not return a newline (js2-EOL) - instead, it
-gobbles newlines until it finds a non-newline token.  Call
-`js2-peek-token-or-eol' when you care about newlines.
-
-This function will also not return a js2-COMMENT.  Instead, it
-records comments found in `js2-scanned-comments'.  If the token
-returned by this function immediately follows a jsdoc comment,
-the token is flagged as such."
-  (if (zerop js2-ti-lookahead)
-      (js2-get-token-internal modifier)
-    (cl-decf js2-ti-lookahead)
-    (setq js2-ti-tokens-cursor (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens))
-    (let ((tt (js2-current-token-type)))
-      (cl-assert (not (= tt js2-EOL)))
-      tt)))
-
-(defun js2-unget-token ()
-  (cl-assert (< js2-ti-lookahead js2-ti-max-lookahead))
-  (cl-incf js2-ti-lookahead)
-  (setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens)))
-
-(defun js2-get-token-internal (modifier)
-  (let* ((token (js2-get-token-internal-1 modifier)) ; call scanner
-         (tt (js2-token-type token))
-         saw-eol
-         face)
-    ;; process comments
-    (while (or (= tt js2-EOL) (= tt js2-COMMENT))
-      (if (= tt js2-EOL)
-          (setq saw-eol t)
-        (setq saw-eol nil)
-        (when js2-record-comments
-          (js2-record-comment token)))
-      (setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens))
-      (setq token (js2-get-token-internal-1 modifier) ; call scanner again
-            tt (js2-token-type token)))
-
-    (when saw-eol
-      (setf (js2-token-follows-eol-p token) t))
-
-    ;; perform lexical fontification as soon as token is scanned
-    (when js2-parse-ide-mode
-      (cond
-       ((cl-minusp tt)
-        (js2-record-face 'js2-error token))
-       ((setq face (aref js2-kwd-tokens tt))
-        (js2-record-face face token))
-       ((and (= tt js2-NAME)
-             (equal (js2-token-string token) "undefined"))
-        (js2-record-face 'font-lock-constant-face token))))
-    tt))
-
-(defsubst js2-string-to-number (str base)
-  ;; TODO:  Maybe port ScriptRuntime.stringToNumber.
-  (condition-case nil
-      (string-to-number str base)
-    (overflow-error -1)))
-
-(defun js2-get-token-internal-1 (modifier)
-  "Return next JavaScript token type, an int such as js2-RETURN.
-During operation, creates an instance of `js2-token' struct, sets
-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 legacy-octal
-        (token (js2-new-token 0)))
-    (setq
-     tt
-     (catch 'return
-       (when (eq modifier 'TEMPLATE_TAIL)
-         (setf (js2-token-beg token) (1- js2-ts-cursor))
-         (throw 'return (js2-get-string-or-template-token ?` token)))
-       (while t
-         ;; Eat whitespace, possibly sensitive to newlines.
-         (setq continue t)
-         (while continue
-           (setq c (js2-get-char))
-           (cond
-            ((eq c js2-EOF_CHAR)
-             (js2-unget-char)
-             (js2-ts-set-char-token-bounds token)
-             (throw 'return js2-EOF))
-            ((eq c ?\n)
-             (js2-ts-set-char-token-bounds token)
-             (setq js2-ts-dirty-line nil)
-             (throw 'return js2-EOL))
-            ((not (js2-js-space-p c))
-             (if (/= c ?-)              ; in case end of HTML comment
-                 (setq js2-ts-dirty-line t))
-             (setq continue nil))))
-         ;; Assume the token will be 1 char - fixed up below.
-         (js2-ts-set-char-token-bounds token)
-         (when (eq c ?@)
-           (throw 'return js2-XMLATTR))
-         ;; identifier/keyword/instanceof?
-         ;; watch out for starting with a <backslash>
-         (cond
-          ((eq c ?\\)
-           (setq c (js2-get-char))
-           (if (eq c ?u)
-               (setq identifier-start t
-                     is-unicode-escape-start t
-                     js2-ts-string-buffer nil)
-             (setq identifier-start nil)
-             (js2-unget-char)
-             (setq c ?\\)))
-          (t
-           (when (setq identifier-start (js2-identifier-start-p c))
-             (setq js2-ts-string-buffer nil)
-             (js2-add-to-string c))))
-         (when identifier-start
-           (setq contains-escape is-unicode-escape-start)
-           (catch 'break
-             (while t
-               (if is-unicode-escape-start
-                   ;; strictly speaking we should probably push-back
-                   ;; all the bad characters if the <backslash>uXXXX
-                   ;; sequence is malformed. But since there isn't a
-                   ;; correct context(is there?) for a bad Unicode
-                   ;; escape sequence in an identifier, we can report
-                   ;; an error here.
-                   (progn
-                     (setq escape-val 0)
-                     (dotimes (_ 4)
-                       (setq c (js2-get-char)
-                             escape-val (js2-x-digit-to-int c escape-val))
-                       ;; Next check takes care of c < 0 and bad escape
-                       (if (cl-minusp escape-val)
-                           (throw 'break nil)))
-                     (if (cl-minusp escape-val)
-                         (js2-report-scan-error "msg.invalid.escape" t))
-                     (js2-add-to-string escape-val)
-                     (setq is-unicode-escape-start nil))
-                 (setq c (js2-get-char))
-                 (cond
-                  ((eq c ?\\)
-                   (setq c (js2-get-char))
-                   (if (eq c ?u)
-                       (setq is-unicode-escape-start t
-                             contains-escape t)
-                     (js2-report-scan-error "msg.illegal.character" t)))
-                  (t
-                   (if (or (eq c js2-EOF_CHAR)
-                           (not (js2-identifier-part-p c)))
-                       (throw 'break nil))
-                   (js2-add-to-string c))))))
-           (js2-unget-char)
-           (setf str (js2-collect-string js2-ts-string-buffer)
-                 (js2-token-end token) js2-ts-cursor)
-           ;; FIXME: Invalid in ES5 and ES6, see
-           ;; https://bugzilla.mozilla.org/show_bug.cgi?id=694360
-           ;; Probably should just drop this conditional.
-           (unless contains-escape
-             ;; OPT we shouldn't have to make a string (object!) to
-             ;; check if it's a keyword.
-             ;; Return the corresponding token if it's a keyword
-             (when (and (not (eq modifier 'KEYWORD_IS_NAME))
-                        (setq result (js2-string-to-keyword str)))
-               (if (and (< js2-language-version 170)
-                        (memq result '(js2-LET js2-YIELD)))
-                   ;; LET and YIELD are tokens only in 1.7 and later
-                   (setq result 'js2-NAME))
-               (when (eq result 'js2-RESERVED)
-                 (setf (js2-token-string token) str))
-               (throw 'return (js2-tt-code result))))
-           ;; If we want to intern these as Rhino does, just use (intern str)
-           (setf (js2-token-string token) str)
-           (throw 'return js2-NAME))    ; end identifier/kwd check
-         ;; is it a number?
-         (when (or (js2-digit-p c)
-                   (and (eq c ?.) (js2-digit-p (js2-peek-char))))
-           (setq js2-ts-string-buffer nil
-                 base 10)
-           (when (eq c ?0)
-             (setq c (js2-get-char))
-             (cond
-              ((or (eq c ?x) (eq c ?X))
-               (setq base 16)
-               (setq c (js2-get-char)))
-              ((and (or (eq c ?b) (eq c ?B))
-                    (>= js2-language-version 200))
-               (setq base 2)
-               (setq c (js2-get-char)))
-              ((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))
-              (t
-               (js2-add-to-string ?0))))
-           (cond
-            ((eq base 16)
-             (if (> 0 (js2-x-digit-to-int c 0))
-                 (js2-report-scan-error "msg.missing.hex.digits")
-               (while (<= 0 (js2-x-digit-to-int c 0))
-                 (js2-add-to-string c)
-                 (setq c (js2-get-char)))))
-            ((eq base 2)
-             (if (not (memq c '(?0 ?1)))
-                 (js2-report-scan-error "msg.missing.binary.digits")
-               (while (memq c '(?0 ?1))
-                 (js2-add-to-string c)
-                 (setq c (js2-get-char)))))
-            ((eq base 8)
-             (if (or (> ?0 c) (< ?7 c))
-                 (js2-report-scan-error "msg.missing.octal.digits")
-               (while (and (<= ?0 c) (>= ?7 c))
-                 (js2-add-to-string c)
-                 (setq c (js2-get-char)))))
-            (t
-             (while (and (<= ?0 c) (<= c ?9))
-               ;; We permit 08 and 09 as decimal numbers, which
-               ;; makes our behavior a superset of the ECMA
-               ;; numeric grammar.  We might not always be so
-               ;; permissive, so we warn about it.
-               (when (and (eq base 'maybe-8) (>= c ?8))
-                 (js2-report-warning "msg.bad.octal.literal"
-                                     (if (eq c ?8) "8" "9"))
-                 (setq base 10))
-               (js2-add-to-string c)
-               (setq c (js2-get-char)))
-             (when (eq base 'maybe-8)
-               (setq base 8
-                     legacy-octal t))))
-           (when (and (eq base 10) (memq c '(?. ?e ?E)))
-             (when (eq c ?.)
-               (cl-loop do
-                        (js2-add-to-string c)
-                        (setq c (js2-get-char))
-                        while (js2-digit-p c)))
-             (when (memq c '(?e ?E))
-               (js2-add-to-string c)
-               (setq c (js2-get-char))
-               (when (memq c '(?+ ?-))
-                 (js2-add-to-string c)
-                 (setq c (js2-get-char)))
-               (unless (js2-digit-p c)
-                 (js2-report-scan-error "msg.missing.exponent" t))
-               (cl-loop do
-                        (js2-add-to-string c)
-                        (setq c (js2-get-char))
-                        while (js2-digit-p c))))
-           (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-legacy-octal-p token) (and (= base 8) legacy-octal)))
-           (throw 'return js2-NUMBER))
-         ;; is it a string?
-         (when (or (memq c '(?\" ?\'))
-                   (and (>= js2-language-version 200)
-                        (= c ?`)))
-           (throw 'return
-                  (js2-get-string-or-template-token c token)))
-         (js2-ts-return token
-                        (cl-case c
-                          (?\;
-                           (throw 'return js2-SEMI))
-                          (?\[
-                           (throw 'return js2-LB))
-                          (?\]
-                           (throw 'return js2-RB))
-                          (?{
-                           (throw 'return js2-LC))
-                          (?}
-                           (throw 'return js2-RC))
-                          (?\(
-                           (throw 'return js2-LP))
-                          (?\)
-                           (throw 'return js2-RP))
-                          (?,
-                           (throw 'return js2-COMMA))
-                          (??
-                           (throw 'return js2-HOOK))
-                          (?:
-                           (if (js2-match-char ?:)
-                               js2-COLONCOLON
-                             (throw 'return js2-COLON)))
-                          (?.
-                           (if (js2-match-char ?.)
-                               (if (js2-match-char ?.)
-                                   js2-TRIPLEDOT js2-DOTDOT)
-                             (if (js2-match-char ?\()
-                                 js2-DOTQUERY
-                               (throw 'return js2-DOT))))
-                          (?|
-                           (if (js2-match-char ?|)
-                               (throw 'return js2-OR)
-                             (if (js2-match-char ?=)
-                                 js2-ASSIGN_BITOR
-                               (throw 'return js2-BITOR))))
-                          (?^
-                           (if (js2-match-char ?=)
-                               js2-ASSIGN_BITOR
-                             (throw 'return js2-BITXOR)))
-                          (?&
-                           (if (js2-match-char ?&)
-                               (throw 'return js2-AND)
-                             (if (js2-match-char ?=)
-                                 js2-ASSIGN_BITAND
-                               (throw 'return js2-BITAND))))
-                          (?=
-                           (if (js2-match-char ?=)
-                               (if (js2-match-char ?=)
-                                   js2-SHEQ
-                                 (throw 'return js2-EQ))
-                             (if (js2-match-char ?>)
-                                 (js2-ts-return token js2-ARROW)
-                               (throw 'return js2-ASSIGN))))
-                          (?!
-                           (if (js2-match-char ?=)
-                               (if (js2-match-char ?=)
-                                   js2-SHNE
-                                 js2-NE)
-                             (throw 'return js2-NOT)))
-                          (?<
-                           ;; NB:treat HTML begin-comment as comment-till-eol
-                           (when (js2-match-char ?!)
-                             (when (js2-match-char ?-)
-                               (when (js2-match-char ?-)
-                                 (js2-skip-line)
-                                 (setf (js2-token-comment-type (js2-current-token)) 'html)
-                                 (throw 'return js2-COMMENT)))
-                             (js2-unget-char))
-                           (if (js2-match-char ?<)
-                               (if (js2-match-char ?=)
-                                   js2-ASSIGN_LSH
-                                 js2-LSH)
-                             (if (js2-match-char ?=)
-                                 js2-LE
-                               (throw 'return js2-LT))))
-                          (?>
-                           (if (js2-match-char ?>)
-                               (if (js2-match-char ?>)
-                                   (if (js2-match-char ?=)
-                                       js2-ASSIGN_URSH
-                                     js2-URSH)
-                                 (if (js2-match-char ?=)
-                                     js2-ASSIGN_RSH
-                                   js2-RSH))
-                             (if (js2-match-char ?=)
-                                 js2-GE
-                               (throw 'return js2-GT))))
-                          (?*
-                           (if (js2-match-char ?=)
-                               js2-ASSIGN_MUL
-                             (if (js2-match-char ?*)
-                                 (if (js2-match-char ?=)
-                                     js2-ASSIGN_EXPON
-                                   js2-EXPON)
-                               (throw 'return js2-MUL))))
-                          (?/
-                           ;; is it a // comment?
-                           (when (js2-match-char ?/)
-                             (setf (js2-token-beg token) (- js2-ts-cursor 2))
-                             (js2-skip-line)
-                             (setf (js2-token-comment-type token) 'line)
-                             ;; include newline so highlighting goes to end of
-                             ;; window, if there actually is a newline; if we
-                             ;; hit eof, then implicitly there isn't
-                             (unless js2-ts-hit-eof
-                               (cl-incf (js2-token-end token)))
-                             (throw 'return js2-COMMENT))
-                           ;; is it a /* comment?
-                           (when (js2-match-char ?*)
-                             (setf look-for-slash nil
-                                   (js2-token-beg token) (- js2-ts-cursor 2)
-                                   (js2-token-comment-type token)
-                                   (if (js2-match-char ?*)
-                                       (progn
-                                         (setq look-for-slash t)
-                                         'jsdoc)
-                                     'block))
-                             (while t
-                               (setq c (js2-get-char))
-                               (cond
-                                ((eq c js2-EOF_CHAR)
-                                 (setf (js2-token-end token) (1- js2-ts-cursor))
-                                 (js2-report-error "msg.unterminated.comment")
-                                 (throw 'return js2-COMMENT))
-                                ((eq c ?*)
-                                 (setq look-for-slash t))
-                                ((eq c ?/)
-                                 (if look-for-slash
-                                     (js2-ts-return token js2-COMMENT)))
-                                (t
-                                 (setf look-for-slash nil
-                                       (js2-token-end token) js2-ts-cursor)))))
-                           (if (js2-match-char ?=)
-                               js2-ASSIGN_DIV
-                             (throw 'return js2-DIV)))
-                          (?#
-                           (when js2-skip-preprocessor-directives
-                             (js2-skip-line)
-                             (setf (js2-token-comment-type token) 'preprocessor
-                                   (js2-token-end token) js2-ts-cursor)
-                             (throw 'return js2-COMMENT))
-                           (throw 'return js2-ERROR))
-                          (?%
-                           (if (js2-match-char ?=)
-                               js2-ASSIGN_MOD
-                             (throw 'return js2-MOD)))
-                          (?~
-                           (throw 'return js2-BITNOT))
-                          (?+
-                           (if (js2-match-char ?=)
-                               js2-ASSIGN_ADD
-                             (if (js2-match-char ?+)
-                                 js2-INC
-                               (throw 'return js2-ADD))))
-                          (?-
-                           (cond
-                            ((js2-match-char ?=)
-                             (setq c js2-ASSIGN_SUB))
-                            ((js2-match-char ?-)
-                             (unless js2-ts-dirty-line
-                               ;; treat HTML end-comment after possible whitespace
-                               ;; after line start as comment-until-eol
-                               (when (js2-match-char ?>)
-                                 (js2-skip-line)
-                                 (setf (js2-token-comment-type (js2-current-token)) 'html)
-                                 (throw 'return js2-COMMENT)))
-                             (setq c js2-DEC))
-                            (t
-                             (setq c js2-SUB)))
-                           (setq js2-ts-dirty-line t)
-                           c)
-                          (otherwise
-                           (js2-report-scan-error "msg.illegal.character")))))))
-    (setf (js2-token-type token) tt)
-    token))
-
-(defun js2-get-string-or-template-token (quote-char token)
-  ;; We attempt to accumulate a string the fast way, by
-  ;; building it directly out of the reader.  But if there
-  ;; are any escaped characters in the string, we revert to
-  ;; building it out of a string buffer.
-  (let ((c (js2-get-char))
-        js2-ts-string-buffer
-        nc c1 val escape-val)
-    (catch 'break
-      (while (/= c quote-char)
-        (catch 'continue
-          (when (eq c js2-EOF_CHAR)
-            (js2-unget-char)
-            (js2-report-error "msg.unterminated.string.lit")
-            (throw 'break nil))
-          (when (and (eq c ?\n) (not (eq quote-char ?`)))
-            (js2-unget-char)
-            (js2-report-error "msg.unterminated.string.lit")
-            (throw 'break nil))
-          (when (eq c ?\\)
-            ;; We've hit an escaped character
-            (setq c (js2-get-char))
-            (cl-case c
-              (?b (setq c ?\b))
-              (?f (setq c ?\f))
-              (?n (setq c ?\n))
-              (?r (setq c ?\r))
-              (?t (setq c ?\t))
-              (?v (setq c ?\v))
-              (?u
-               (setq c1 (js2-read-unicode-escape))
-               (if js2-parse-ide-mode
-                   (if c1
-                       (progn
-                         ;; just copy the string in IDE-mode
-                         (js2-add-to-string ?\\)
-                         (js2-add-to-string ?u)
-                         (dotimes (_ 3)
-                           (js2-add-to-string (js2-get-char)))
-                         (setq c (js2-get-char))) ; added at end of loop
-                     ;; flag it as an invalid escape
-                     (js2-report-warning "msg.invalid.escape"
-                                         nil (- js2-ts-cursor 2) 6))
-                 ;; Get 4 hex digits; if the u escape is not
-                 ;; followed by 4 hex digits, use 'u' + the
-                 ;; literal character sequence that follows.
-                 (js2-add-to-string ?u)
-                 (setq escape-val 0)
-                 (dotimes (_ 4)
-                   (setq c (js2-get-char)
-                         escape-val (js2-x-digit-to-int c escape-val))
-                   (if (cl-minusp escape-val)
-                       (throw 'continue nil))
-                   (js2-add-to-string c))
-                 ;; prepare for replace of stored 'u' sequence by escape value
-                 (setq js2-ts-string-buffer (nthcdr 5 js2-ts-string-buffer)
-                       c escape-val)))
-              (?x
-               ;; Get 2 hex digits, defaulting to 'x'+literal
-               ;; sequence, as above.
-               (setq c (js2-get-char)
-                     escape-val (js2-x-digit-to-int c 0))
-               (if (cl-minusp escape-val)
-                   (progn
-                     (js2-add-to-string ?x)
-                     (throw 'continue nil))
-                 (setq c1 c
-                       c (js2-get-char)
-                       escape-val (js2-x-digit-to-int c escape-val))
-                 (if (cl-minusp escape-val)
-                     (progn
-                       (js2-add-to-string ?x)
-                       (js2-add-to-string c1)
-                       (throw 'continue nil))
-                   ;; got 2 hex digits
-                   (setq c escape-val))))
-              (?\n
-               ;; Remove line terminator after escape to follow
-               ;; SpiderMonkey and C/C++
-               (setq c (js2-get-char))
-               (throw 'continue nil))
-              (t
-               (when (and (<= ?0 c) (< c ?8))
-                 (setq val (- c ?0)
-                       c (js2-get-char))
-                 (when (and (<= ?0 c) (< c ?8))
-                   (setq val (- (+ (* 8 val) c) ?0)
-                         c (js2-get-char))
-                   (when (and (<= ?0 c)
-                              (< c ?8)
-                              (< val #o37))
-                     ;; c is 3rd char of octal sequence only
-                     ;; if the resulting val <= 0377
-                     (setq val (- (+ (* 8 val) c) ?0)
-                           c (js2-get-char))))
-                 (js2-unget-char)
-                 (setq c val)))))
-          (when (and (eq quote-char ?`) (eq c ?$))
-            (when (eq (setq nc (js2-get-char)) ?\{)
-              (throw 'break nil))
-            (js2-unget-char))
-          (js2-add-to-string c)
-          (setq c (js2-get-char)))))
-    (js2-set-string-from-buffer token)
-    (if (not (eq quote-char ?`))
-        js2-STRING
-      (if (and (eq c ?$) (eq nc ?\{))
-          js2-TEMPLATE_HEAD
-        js2-NO_SUBS_TEMPLATE))))
-
-(defun js2-read-regexp (start-tt start-pos)
-  "Called by parser when it gets / or /= in literal context."
-  (let (c err
-        in-class  ; inside a '[' .. ']' character-class
-        flags
-        (continue t)
-        (token (js2-new-token 0)))
-    (js2-record-text-property start-pos (1+ start-pos)
-                              'syntax-table (string-to-syntax "\"/"))
-    (setq js2-ts-string-buffer nil)
-    (if (eq start-tt js2-ASSIGN_DIV)
-        ;; mis-scanned /=
-        (js2-add-to-string ?=)
-      (if (not (eq start-tt js2-DIV))
-          (error "failed assertion")))
-    (while (and (not err)
-                (or (/= (setq c (js2-get-char)) ?/)
-                    in-class))
-      (cond
-       ((or (= c ?\n)
-            (= c js2-EOF_CHAR))
-        (setf (js2-token-end token) (1- js2-ts-cursor)
-              err t
-              (js2-token-string token) (js2-collect-string js2-ts-string-buffer))
-        (js2-report-error "msg.unterminated.re.lit"))
-       (t (cond
-           ((= c ?\\)
-            (js2-add-to-string c)
-            (setq c (js2-get-char)))
-           ((= c ?\[)
-            (setq in-class t))
-           ((= c ?\])
-            (setq in-class nil)))
-          (js2-add-to-string c))))
-    (unless err
-      (js2-record-text-property (1- js2-ts-cursor) js2-ts-cursor
-                                'syntax-table (string-to-syntax "\"/"))
-      (while continue
-        (cond
-         ((js2-match-char ?g)
-          (push ?g flags))
-         ((js2-match-char ?i)
-          (push ?i flags))
-         ((js2-match-char ?m)
-          (push ?m flags))
-         ((and (js2-match-char ?u)
-               (>= js2-language-version 200))
-          (push ?u flags))
-         ((and (js2-match-char ?y)
-               (>= js2-language-version 200))
-          (push ?y flags))
-         (t
-          (setq continue nil))))
-      (if (js2-alpha-p (js2-peek-char))
-          (js2-report-scan-error "msg.invalid.re.flag" t
-                                 js2-ts-cursor 1))
-      (js2-set-string-from-buffer token))
-    (js2-collect-string flags)))
-
-(defun js2-get-first-xml-token ()
-  (setq js2-ts-xml-open-tags-count 0
-        js2-ts-is-xml-attribute nil
-        js2-ts-xml-is-tag-content nil)
-  (js2-unget-char)
-  (js2-get-next-xml-token))
-
-(defun js2-xml-discard-string (token)
-  "Throw away the string in progress and flag an XML parse error."
-  (setf js2-ts-string-buffer nil
-        (js2-token-string token) nil)
-  (js2-report-scan-error "msg.XML.bad.form" t))
-
-(defun js2-get-next-xml-token ()
-  (setq js2-ts-string-buffer nil)  ; for recording the XML
-  (let ((token (js2-new-token 0))
-        c result)
-    (setq result
-          (catch 'return
-            (while t
-              (setq c (js2-get-char))
-              (cond
-               ((= c js2-EOF_CHAR)
-                (throw 'return js2-ERROR))
-               (js2-ts-xml-is-tag-content
-                (cl-case c
-                  (?>
-                   (js2-add-to-string c)
-                   (setq js2-ts-xml-is-tag-content nil
-                         js2-ts-is-xml-attribute nil))
-                  (?/
-                   (js2-add-to-string c)
-                   (when (eq ?> (js2-peek-char))
-                     (setq c (js2-get-char))
-                     (js2-add-to-string c)
-                     (setq js2-ts-xml-is-tag-content nil)
-                     (cl-decf js2-ts-xml-open-tags-count)))
-                  (?{
-                   (js2-unget-char)
-                   (js2-set-string-from-buffer token)
-                   (throw 'return js2-XML))
-                  ((?\' ?\")
-                   (js2-add-to-string c)
-                   (unless (js2-read-quoted-string c token)
-                     (throw 'return js2-ERROR)))
-                  (?=
-                   (js2-add-to-string c)
-                   (setq js2-ts-is-xml-attribute t))
-                  ((? ?\t ?\r ?\n)
-                   (js2-add-to-string c))
-                  (t
-                   (js2-add-to-string c)
-                   (setq js2-ts-is-xml-attribute nil)))
-                (when (and (not js2-ts-xml-is-tag-content)
-                           (zerop js2-ts-xml-open-tags-count))
-                  (js2-set-string-from-buffer token)
-                  (throw 'return js2-XMLEND)))
-               (t
-                ;; else not tag content
-                (cl-case c
-                  (?<
-                   (js2-add-to-string c)
-                   (setq c (js2-peek-char))
-                   (cl-case c
-                     (?!
-                      (setq c (js2-get-char)) ;; skip !
-                      (js2-add-to-string c)
-                      (setq c (js2-peek-char))
-                      (cl-case c
-                        (?-
-                         (setq c (js2-get-char)) ;; skip -
-                         (js2-add-to-string c)
-                         (if (eq c ?-)
-                             (progn
-                               (js2-add-to-string c)
-                               (unless (js2-read-xml-comment token)
-                                 (throw 'return js2-ERROR)))
-                           (js2-xml-discard-string token)
-                           (throw 'return js2-ERROR)))
-                        (?\[
-                         (setq c (js2-get-char)) ;; skip [
-                         (js2-add-to-string c)
-                         (if (and (= (js2-get-char) ?C)
-                                  (= (js2-get-char) ?D)
-                                  (= (js2-get-char) ?A)
-                                  (= (js2-get-char) ?T)
-                                  (= (js2-get-char) ?A)
-                                  (= (js2-get-char) ?\[))
-                             (progn
-                               (js2-add-to-string ?C)
-                               (js2-add-to-string ?D)
-                               (js2-add-to-string ?A)
-                               (js2-add-to-string ?T)
-                               (js2-add-to-string ?A)
-                               (js2-add-to-string ?\[)
-                               (unless (js2-read-cdata token)
-                                 (throw 'return js2-ERROR)))
-                           (js2-xml-discard-string token)
-                           (throw 'return js2-ERROR)))
-                        (t
-                         (unless (js2-read-entity token)
-                           (throw 'return js2-ERROR))))
-                      ;; Allow bare CDATA section, e.g.:
-                      ;;   let xml = <![CDATA[ foo bar baz ]]>;
-                      (when (zerop js2-ts-xml-open-tags-count)
-                        (throw 'return js2-XMLEND)))
-                     (??
-                      (setq c (js2-get-char)) ;; skip ?
-                      (js2-add-to-string c)
-                      (unless (js2-read-PI token)
-                        (throw 'return js2-ERROR)))
-                     (?/
-                      ;; end tag
-                      (setq c (js2-get-char)) ;; skip /
-                      (js2-add-to-string c)
-                      (when (zerop js2-ts-xml-open-tags-count)
-                        (js2-xml-discard-string token)
-                        (throw 'return js2-ERROR))
-                      (setq js2-ts-xml-is-tag-content t)
-                      (cl-decf js2-ts-xml-open-tags-count))
-                     (t
-                      ;; start tag
-                      (setq js2-ts-xml-is-tag-content t)
-                      (cl-incf js2-ts-xml-open-tags-count))))
-                  (?{
-                   (js2-unget-char)
-                   (js2-set-string-from-buffer token)
-                   (throw 'return js2-XML))
-                  (t
-                   (js2-add-to-string c))))))))
-    (setf (js2-token-end token) js2-ts-cursor)
-    (setf (js2-token-type token) result)
-    result))
-
-(defun js2-read-quoted-string (quote token)
-  (let (c)
-    (catch 'return
-      (while (/= (setq c (js2-get-char)) js2-EOF_CHAR)
-        (js2-add-to-string c)
-        (if (eq c quote)
-            (throw 'return t)))
-      (js2-xml-discard-string token)  ;; throw away string in progress
-      nil)))
-
-(defun js2-read-xml-comment (token)
-  (let ((c (js2-get-char)))
-    (catch 'return
-      (while (/= c js2-EOF_CHAR)
-        (catch 'continue
-          (js2-add-to-string c)
-          (when (and (eq c ?-) (eq ?- (js2-peek-char)))
-            (setq c (js2-get-char))
-            (js2-add-to-string c)
-            (if (eq (js2-peek-char) ?>)
-                (progn
-                  (setq c (js2-get-char)) ;; skip >
-                  (js2-add-to-string c)
-                  (throw 'return t))
-              (throw 'continue nil)))
-          (setq c (js2-get-char))))
-      (js2-xml-discard-string token)
-      nil)))
-
-(defun js2-read-cdata (token)
-  (let ((c (js2-get-char)))
-    (catch 'return
-      (while (/= c js2-EOF_CHAR)
-        (catch 'continue
-          (js2-add-to-string c)
-          (when (and (eq c ?\]) (eq (js2-peek-char) ?\]))
-            (setq c (js2-get-char))
-            (js2-add-to-string c)
-            (if (eq (js2-peek-char) ?>)
-                (progn
-                  (setq c (js2-get-char)) ;; Skip >
-                  (js2-add-to-string c)
-                  (throw 'return t))
-              (throw 'continue nil)))
-          (setq c (js2-get-char))))
-      (js2-xml-discard-string token)
-      nil)))
-
-(defun js2-read-entity (token)
-  (let ((decl-tags 1)
-        c)
-    (catch 'return
-      (while (/= js2-EOF_CHAR (setq c (js2-get-char)))
-        (js2-add-to-string c)
-        (cl-case c
-          (?<
-           (cl-incf decl-tags))
-          (?>
-           (cl-decf decl-tags)
-           (if (zerop decl-tags)
-               (throw 'return t)))))
-      (js2-xml-discard-string token)
-      nil)))
-
-(defun js2-read-PI (token)
-  "Scan an XML processing instruction."
-  (let (c)
-    (catch 'return
-      (while (/= js2-EOF_CHAR (setq c (js2-get-char)))
-        (js2-add-to-string c)
-        (when (and (eq c ??) (eq (js2-peek-char) ?>))
-          (setq c (js2-get-char))  ;; Skip >
-          (js2-add-to-string c)
-          (throw 'return t)))
-      (js2-xml-discard-string token)
-      nil)))
-
-;;; Highlighting
-
-(defun js2-set-face (beg end face &optional record)
-  "Fontify a region.  If RECORD is non-nil, record for later."
-  (when (cl-plusp js2-highlight-level)
-    (setq beg (min (point-max) beg)
-          beg (max (point-min) beg)
-          end (min (point-max) end)
-          end (max (point-min) end))
-    (if record
-        (push (list beg end face) js2-mode-fontifications)
-      (put-text-property beg end 'font-lock-face face))))
-
-(defsubst js2-clear-face (beg end)
-  (remove-text-properties beg end '(font-lock-face nil
-                                    help-echo nil
-                                    point-entered nil
-                                    cursor-sensor-functions nil
-                                    c-in-sws nil)))
-
-(defconst js2-ecma-global-props
-  (concat "^"
-          (regexp-opt
-           '("Infinity" "NaN" "undefined" "arguments") t)
-          "$")
-  "Value properties of the Ecma-262 Global Object.
-Shown at or above `js2-highlight-level' 2.")
-
-;; might want to add the name "arguments" to this list?
-(defconst js2-ecma-object-props
-  (concat "^"
-          (regexp-opt
-           '("prototype" "__proto__" "__parent__") t)
-          "$")
-  "Value properties of the Ecma-262 Object constructor.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-global-funcs
-  (concat
-   "^"
-   (regexp-opt
-    '("decodeURI" "decodeURIComponent" "encodeURI" "encodeURIComponent"
-      "eval" "isFinite" "isNaN" "parseFloat" "parseInt") t)
-   "$")
-  "Function properties of the Ecma-262 Global object.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-number-props
-  (concat "^"
-          (regexp-opt '("MAX_VALUE" "MIN_VALUE" "NaN"
-                        "NEGATIVE_INFINITY"
-                        "POSITIVE_INFINITY") t)
-          "$")
-  "Properties of the Ecma-262 Number constructor.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-date-props "^\\(parse\\|UTC\\)$"
-  "Properties of the Ecma-262 Date constructor.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-math-props
-  (concat "^"
-          (regexp-opt
-           '("E" "LN10" "LN2" "LOG2E" "LOG10E" "PI" "SQRT1_2" "SQRT2")
-           t)
-          "$")
-  "Properties of the Ecma-262 Math object.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-math-funcs
-  (concat "^"
-          (regexp-opt
-           '("abs" "acos" "asin" "atan" "atan2" "ceil" "cos" "exp" "floor"
-             "log" "max" "min" "pow" "random" "round" "sin" "sqrt" "tan") t)
-          "$")
-  "Function properties of the Ecma-262 Math object.
-Shown at or above `js2-highlight-level' 2.")
-
-(defconst js2-ecma-function-props
-  (concat
-   "^"
-   (regexp-opt
-    '(;; properties of the Object prototype object
-      "hasOwnProperty" "isPrototypeOf" "propertyIsEnumerable"
-      "toLocaleString" "toString" "valueOf"
-      ;; properties of the Function prototype object
-      "apply" "call"
-      ;; properties of the Array prototype object
-      "concat" "join" "pop" "push" "reverse" "shift" "slice" "sort"
-      "splice" "unshift"
-      ;; properties of the String prototype object
-      "charAt" "charCodeAt" "fromCharCode" "indexOf" "lastIndexOf"
-      "localeCompare" "match" "replace" "search" "split" "substring"
-      "toLocaleLowerCase" "toLocaleUpperCase" "toLowerCase"
-      "toUpperCase"
-      ;; properties of the Number prototype object
-      "toExponential" "toFixed" "toPrecision"
-      ;; properties of the Date prototype object
-      "getDate" "getDay" "getFullYear" "getHours" "getMilliseconds"
-      "getMinutes" "getMonth" "getSeconds" "getTime"
-      "getTimezoneOffset" "getUTCDate" "getUTCDay" "getUTCFullYear"
-      "getUTCHours" "getUTCMilliseconds" "getUTCMinutes" "getUTCMonth"
-      "getUTCSeconds" "setDate" "setFullYear" "setHours"
-      "setMilliseconds" "setMinutes" "setMonth" "setSeconds" "setTime"
-      "setUTCDate" "setUTCFullYear" "setUTCHours" "setUTCMilliseconds"
-      "setUTCMinutes" "setUTCMonth" "setUTCSeconds" "toDateString"
-      "toLocaleDateString" "toLocaleString" "toLocaleTimeString"
-      "toTimeString" "toUTCString"
-      ;; properties of the RegExp prototype object
-      "exec" "test"
-      ;; properties of the JSON prototype object
-      "parse" "stringify"
-      ;; SpiderMonkey/Rhino extensions, versions 1.5+
-      "toSource" "__defineGetter__" "__defineSetter__"
-      "__lookupGetter__" "__lookupSetter__" "__noSuchMethod__"
-      "every" "filter" "forEach" "lastIndexOf" "map" "some")
-    t)
-   "$")
-  "Built-in functions defined by Ecma-262 and SpiderMonkey extensions.
-Shown at or above `js2-highlight-level' 3.")
-
-(defun js2-parse-highlight-prop-get (parent target prop call-p)
-  (let ((target-name (and target
-                          (js2-name-node-p target)
-                          (js2-name-node-name target)))
-        (prop-name (if prop (js2-name-node-name prop)))
-        (level2 (>= js2-highlight-level 2))
-        (level3 (>= js2-highlight-level 3)))
-    (when level2
-      (let ((face
-             (if call-p
-                 (cond
-                  ((and target prop)
-                   (cond
-                    ((and level3 (string-match js2-ecma-function-props prop-name))
-                     'font-lock-builtin-face)
-                    ((and target-name prop)
-                     (cond
-                      ((string= target-name "Date")
-                       (if (string-match js2-ecma-date-props prop-name)
-                           'font-lock-builtin-face))
-                      ((string= target-name "Math")
-                       (if (string-match js2-ecma-math-funcs prop-name)
-                           'font-lock-builtin-face))))))
-                  (prop
-                   (if (string-match js2-ecma-global-funcs prop-name)
-                       'font-lock-builtin-face)))
-               (cond
-                ((and target prop)
-                 (cond
-                  ((string= target-name "Number")
-                   (if (string-match js2-ecma-number-props prop-name)
-                       'font-lock-constant-face))
-                  ((string= target-name "Math")
-                   (if (string-match js2-ecma-math-props prop-name)
-                       'font-lock-constant-face))))
-                (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-access))
-        (when face
-          (let ((pos (+ (js2-node-pos parent)  ; absolute
-                        (js2-node-pos prop)))) ; relative
-            (js2-set-face pos
-                          (+ pos (js2-node-len prop))
-                          face 'record)))))))
-
-(defun js2-parse-highlight-member-expr-node (node)
-  "Perform syntax highlighting of EcmaScript built-in properties.
-The variable `js2-highlight-level' governs this highlighting."
-  (let (face target prop name pos end parent call-p callee)
-    (cond
-     ;; case 1:  simple name, e.g. foo
-     ((js2-name-node-p node)
-      (setq name (js2-name-node-name node))
-      ;; possible for name to be nil in rare cases - saw it when
-      ;; running js2-mode on an elisp buffer.  Might as well try to
-      ;; make it so js2-mode never barfs.
-      (when name
-        (setq face (if (string-match js2-ecma-global-props name)
-                       'font-lock-constant-face))
-        (when face
-          (setq pos (js2-node-pos node)
-                end (+ pos (js2-node-len node)))
-          (js2-set-face pos end face 'record))))
-     ;; case 2:  property access or function call
-     ((or (js2-prop-get-node-p node)
-          ;; highlight function call if expr is a prop-get node
-          ;; or a plain name (i.e. unqualified function call)
-          (and (setq call-p (js2-call-node-p node))
-               (setq callee (js2-call-node-target node)) ; separate setq!
-               (or (js2-prop-get-node-p callee)
-                   (js2-name-node-p callee))))
-      (setq parent node
-            node (if call-p callee node))
-      (if (and call-p (js2-name-node-p callee))
-          (setq prop callee)
-        (setq target (js2-prop-get-node-left node)
-              prop (js2-prop-get-node-right node)))
-      (cond
-       ((js2-name-node-p prop)
-        ;; case 2(a&c):  simple or complex target, simple name, e.g. x[y].bar
-        (js2-parse-highlight-prop-get parent target prop call-p))
-       ((js2-name-node-p target)
-        ;; case 2b:  simple target, complex name, e.g. foo.x[y]
-        (js2-parse-highlight-prop-get parent target nil call-p)))))))
-
-(defun js2-parse-highlight-member-expr-fn-name (expr)
-  "Highlight the `baz' in function foo.bar.baz(args) {...}.
-This is experimental Rhino syntax.  EXPR is the foo.bar.baz member expr.
-We currently only handle the case where the last component is a prop-get
-of a simple name.  Called before EXPR has a parent node."
-  (let (pos
-        (name (and (js2-prop-get-node-p expr)
-                   (js2-prop-get-node-right expr))))
-    (when (js2-name-node-p name)
-      (js2-set-face (setq pos (+ (js2-node-pos expr)  ; parent is absolute
-                                 (js2-node-pos name)))
-                    (+ pos (js2-node-len name))
-                    'font-lock-function-name-face
-                    'record))))
-
-;; source:  http://jsdoc.sourceforge.net/
-;; Note - this syntax is for Google's enhanced jsdoc parser that
-;; allows type specifications, and needs work before entering the wild.
-
-(defconst js2-jsdoc-param-tag-regexp
-  (concat "^\\s-*\\*+\\s-*\\(@"
-          (regexp-opt '("param" "arg" "argument" "prop" "property" "typedef"))
-          "\\)"
-          "\\s-*\\({[^}]+}\\)?"         ; optional type
-          "\\s-*\\[?\\([[:alnum:]_$\.]+\\)?\\]?"  ; name
-          "\\_>")
-  "Matches jsdoc tags with optional type and optional param name.")
-
-(defconst js2-jsdoc-typed-tag-regexp
-  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
-          (regexp-opt
-           '("enum"
-             "extends"
-             "field"
-             "id"
-             "implements"
-             "lends"
-             "mods"
-             "requires"
-             "return"
-             "returns"
-             "yield"
-             "yields"
-             "type"
-             "throw"
-             "throws"))
-          "\\)\\)\\s-*\\({[^}]+}\\)?")
-  "Matches jsdoc tags with optional type.")
-
-(defconst js2-jsdoc-arg-tag-regexp
-  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
-          (regexp-opt
-           '("alias"
-             "augments"
-             "borrows"
-             "callback"
-             "bug"
-             "base"
-             "config"
-             "default"
-             "define"
-             "exception"
-             "func"
-             "function"
-             "member"
-             "memberOf"
-             "method"
-             "module"
-             "name"
-             "namespace"
-             "since"
-             "suppress"
-             "this"
-             "throws"
-             "version"))
-          "\\)\\)\\s-+\\([^ \t\n]+\\)")
-  "Matches jsdoc tags with a single argument.")
-
-(defconst js2-jsdoc-empty-tag-regexp
-  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
-          (regexp-opt
-           '("abstract"
-             "addon"
-             "author"
-             "class"
-             "const"
-             "constant"
-             "constructor"
-             "constructs"
-             "deprecated"
-             "desc"
-             "description"
-             "event"
-             "example"
-             "exec"
-             "export"
-             "fileoverview"
-             "final"
-             "func"
-             "function"
-             "hidden"
-             "ignore"
-             "implicitCast"
-             "inheritDoc"
-             "inner"
-             "interface"
-             "license"
-             "method"
-             "noalias"
-             "noshadow"
-             "notypecheck"
-             "override"
-             "owner"
-             "preserve"
-             "preserveTry"
-             "private"
-             "protected"
-             "public"
-             "static"
-             "supported"
-             "virtual"
-             ))
-          "\\)\\)\\s-*")
-  "Matches empty jsdoc tags.")
-
-(defconst js2-jsdoc-link-tag-regexp
-  "{\\(@\\(?:link\\|code\\)\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?}"
-  "Matches a jsdoc link or code tag.")
-
-(defconst js2-jsdoc-see-tag-regexp
-  "^\\s-*\\*+\\s-*\\(@see\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?"
-  "Matches a jsdoc @see tag.")
-
-(defconst js2-jsdoc-html-tag-regexp
-  "\\(</?\\)\\([[:alpha:]]+\\)\\s-*\\(/?>\\)"
-  "Matches a simple (no attributes) html start- or end-tag.")
-
-(defun js2-jsdoc-highlight-helper ()
-  (js2-set-face (match-beginning 1)
-                (match-end 1)
-                'js2-jsdoc-tag)
-  (if (match-beginning 2)
-      (if (save-excursion
-            (goto-char (match-beginning 2))
-            (= (char-after) ?{))
-          (js2-set-face (1+ (match-beginning 2))
-                        (1- (match-end 2))
-                        'js2-jsdoc-type)
-        (js2-set-face (match-beginning 2)
-                      (match-end 2)
-                      'js2-jsdoc-value)))
-  (if (match-beginning 3)
-      (js2-set-face (match-beginning 3)
-                    (match-end 3)
-                    'js2-jsdoc-value)))
-
-(defun js2-highlight-jsdoc (ast)
-  "Highlight doc comment tags."
-  (let ((comments (js2-ast-root-comments ast))
-        beg end)
-    (save-excursion
-      (dolist (node comments)
-        (when (eq (js2-comment-node-format node) 'jsdoc)
-          ;; Slice off the leading /* and trailing */ in case there
-          ;; are tags on the first line
-          (setq beg (+ 2 (js2-node-abs-pos node))
-                end (+ beg -4 (js2-node-len node)))
-          (save-restriction
-            (narrow-to-region beg end)
-            (dolist (re (list js2-jsdoc-param-tag-regexp
-                              js2-jsdoc-typed-tag-regexp
-                              js2-jsdoc-arg-tag-regexp
-                              js2-jsdoc-link-tag-regexp
-                              js2-jsdoc-see-tag-regexp
-                              js2-jsdoc-empty-tag-regexp))
-              (goto-char beg)
-              (while (re-search-forward re nil t)
-                (js2-jsdoc-highlight-helper)))
-            ;; simple highlighting for html tags
-            (goto-char beg)
-            (while (re-search-forward js2-jsdoc-html-tag-regexp nil t)
-              (js2-set-face (match-beginning 1)
-                            (match-end 1)
-                            'js2-jsdoc-html-tag-delimiter)
-              (js2-set-face (match-beginning 2)
-                            (match-end 2)
-                            'js2-jsdoc-html-tag-name)
-              (js2-set-face (match-beginning 3)
-                            (match-end 3)
-                            'js2-jsdoc-html-tag-delimiter))))))))
-
-(defun js2-highlight-assign-targets (_node left right)
-  "Highlight function properties and external variables."
-  (let (leftpos name)
-    ;; highlight vars and props assigned function values
-    (when (or (js2-function-node-p right)
-              (js2-class-node-p right))
-      (cond
-       ;; var foo = function() {...}
-       ((js2-name-node-p left)
-        (setq name left))
-       ;; foo.bar.baz = function() {...}
-       ((and (js2-prop-get-node-p left)
-             (js2-name-node-p (js2-prop-get-node-right left)))
-        (setq name (js2-prop-get-node-right left))))
-      (when name
-        (js2-set-face (setq leftpos (js2-node-abs-pos name))
-                      (+ leftpos (js2-node-len name))
-                      'font-lock-function-name-face
-                      'record)))))
-
-(defun js2-record-name-node (node)
-  "Saves NODE to `js2-recorded-identifiers' to check for undeclared variables
-later. NODE must be a name node."
-  (let ((leftpos (js2-node-abs-pos node)))
-    (push (list node js2-current-scope
-                leftpos
-                (+ leftpos (js2-node-len node)))
-          js2-recorded-identifiers)))
-
-(defun js2-highlight-undeclared-vars ()
-  "After entire parse is finished, look for undeclared variable references.
-We have to wait until entire buffer is parsed, since JavaScript permits var
-declarations to occur after they're used.
-
-Some identifiers may be assumed to be externally defined.
-These externs are not highlighted, even if there is no declaration
-for them in the source code (in the current file).
-
-The list of externs consists of the following:
-
-  - `js2-ecma262-externs' for basic names from the ECMAScript language standard.
-  - Depending on the buffer-local variables `js2-include-*-externs'
-    the corresponding `js2-*-externs' to add names for certain environments
-    like the browser, Node or Rhino.
-  - Two customizable lists `js2-global-externs' and `js2-additional-externs',
-    the latter of which should be set per-buffer.
-
-See especially `js2-additional-externs' for further details about externs."
-  (let ((default-externs
-          (append js2-ecma-262-externs
-                  (if (and js2-include-browser-externs
-                           (>= js2-language-version 200)) js2-harmony-externs)
-                  (if js2-include-rhino-externs js2-rhino-externs)
-                  (if js2-include-node-externs js2-node-externs)
-                  (if (or js2-include-browser-externs js2-include-node-externs)
-                      js2-typed-array-externs)
-                  (if js2-include-browser-externs js2-browser-externs)))
-        name)
-    (dolist (entry js2-recorded-identifiers)
-      (cl-destructuring-bind (name-node scope pos end) entry
-        (setq name (js2-name-node-name name-node))
-        (unless (or (member name js2-global-externs)
-                    (member name default-externs)
-                    (member name js2-additional-externs)
-                    (js2-get-defining-scope scope name pos))
-          (js2-report-warning "msg.undeclared.variable" name pos (- end pos)
-                              'js2-external-variable))))))
-
-(defun js2--add-or-update-symbol (symbol inition used vars)
-  "Add or update SYMBOL entry in VARS, an hash table.
-SYMBOL is a js2-name-node, INITION either nil, t, or ?P,
-respectively meaning that SYMBOL is a mere declaration, an
-assignment or a function parameter; when USED is t, the symbol
-node is assumed to be an usage and thus added to the list stored
-in the cdr of the entry.
-"
-  (let* ((nm (js2-name-node-name symbol))
-         (es (js2-node-get-enclosing-scope symbol))
-         (ds (js2-get-defining-scope es nm)))
-    (when (and ds (not (equal nm "arguments")))
-      (let* ((sym (js2-scope-get-symbol ds nm))
-             (var (gethash sym vars))
-             (err-var-p (js2-catch-node-p ds)))
-        (unless inition
-          (setq inition err-var-p))
-        (if var
-            (progn
-              (when (and inition (not (equal (car var) ?P)))
-                (setcar var inition))
-              (when (and used (not (memq symbol (cdr var))))
-                (push symbol (cdr var))))
-          ;; do not consider the declaration of catch parameter as an usage
-          (when (and err-var-p used)
-            (setq used nil))
-          (puthash sym (cons inition (if used (list symbol))) vars))))))
-
-(defun js2--collect-target-symbols (node strict)
-  "Collect the `js-name-node' symbols declared in NODE and return a list of them.
-NODE is either `js2-array-node', `js2-object-node', or `js2-name-node'.
-When STRICT, signal an error if NODE is not one of the expected types."
-  (let (targets)
-    (cond
-     ((js2-name-node-p node)
-      (push node targets))
-     ((js2-array-node-p node)
-      (dolist (elt (js2-array-node-elems node))
-        (when elt
-          (setq elt (cond ((js2-infix-node-p elt) ;; default (=)
-                           (js2-infix-node-left elt))
-                          ((js2-unary-node-p elt) ;; rest (...)
-                           (js2-unary-node-operand elt))
-                          (t elt)))
-          (setq targets (append (js2--collect-target-symbols elt strict)
-                                targets)))))
-     ((js2-object-node-p node)
-      (dolist (elt (js2-object-node-elems node))
-        (let ((subexpr (cond
-                        ((and (js2-infix-node-p elt)
-                              (= js2-ASSIGN (js2-infix-node-type elt)))
-                         ;; Destructuring with default argument.
-                         (js2-infix-node-left elt))
-                        ((and (js2-infix-node-p elt)
-                              (= js2-COLON (js2-infix-node-type elt)))
-                         ;; In regular destructuring {a: aa, b: bb},
-                         ;; the var is on the right.  In abbreviated
-                         ;; destructuring {a, b}, right == left.
-                         (js2-infix-node-right elt))
-                        ((and (js2-unary-node-p elt)
-                              (= js2-TRIPLEDOT (js2-unary-node-type elt)))
-                         ;; Destructuring with spread.
-                         (js2-unary-node-operand elt)))))
-          (when subexpr
-            (setq targets (append
-                           (js2--collect-target-symbols subexpr strict)
-                           targets))))))
-     ((js2-assign-node-p node)
-      (setq targets (append (js2--collect-target-symbols
-                             (js2-assign-node-left node) strict)
-                            targets)))
-     (strict
-      (js2-report-error "msg.no.parm" nil (js2-node-abs-pos node)
-                        (js2-node-len node))
-        nil))
-    targets))
-
-(defun js2--examine-variable (parent node var-init-node)
-  "Examine the usage of the variable NODE, a js2-name-node.
-PARENT is its direct ancestor and VAR-INIT-NODE is the node to be
-examined: return a list of three values, respectively if the
-variable is declared and/or assigned or whether it is simply a
-key of a literal object."
-  (let ((target (js2-var-init-node-target var-init-node))
-        declared assigned object-key)
-    (setq declared (memq node (js2--collect-target-symbols target nil)))
-    ;; Is there an initializer for the declared variable?
-    (when (js2-var-init-node-initializer var-init-node)
-      (setq assigned declared)
-      ;; Determine if the name is actually a literal object key that we shall
-      ;; ignore later
-      (when (and (not declared)
-                 (js2-object-prop-node-p parent)
-                 (eq node (js2-object-prop-node-left parent)))
-        (setq object-key t)))
-    ;; Maybe this is a for loop and the variable is one of its iterators?
-    (unless assigned
-      (let* ((gp (js2-node-parent parent))
-             (ggp (if gp (js2-node-parent gp))))
-        (when (and ggp (js2-for-in-node-p ggp))
-          (setq assigned (memq node
-                               (cl-loop
-                                for kid in (js2-var-decl-node-kids
-                                            (js2-for-in-node-iterator ggp))
-                                with syms = '()
-                                do
-                                (setq syms (append syms
-                                                   (js2--collect-target-symbols
-                                                    (js2-var-init-node-target kid)
-                                                    nil)))
-                                finally return syms))))))
-    (list declared assigned object-key)))
-
-(defun js2--classify-variable (parent node vars)
-  "Classify the single variable NODE, a js2-name-node."
-  (let ((function-param (and (js2-function-node-p parent)
-                             (memq node (js2-function-node-params parent)))))
-    (if (js2-prop-get-node-p parent)
-        ;; If we are within a prop-get, e.g. the "bar" in "foo.bar",
-        ;; just mark "foo" as used
-        (let ((left (js2-prop-get-node-left parent)))
-          (when (js2-name-node-p left)
-            (js2--add-or-update-symbol left nil t vars)))
-      ;; If the node is the external name of an export-binding-node, and
-      ;; it is different from the local name, ignore it
-      (when (or (not (js2-export-binding-node-p parent))
-                (not (and (eq (js2-export-binding-node-extern-name parent) node)
-                          (not (eq (js2-export-binding-node-local-name parent) node)))))
-        (let ((granparent parent)
-              var-init-node
-              assign-node
-              object-key         ; is name actually an object prop key?
-              declared           ; is it declared in narrowest scope?
-              assigned           ; does it get assigned or initialized?
-              (used (null function-param)))
-          ;; Determine the closest var-init-node and assign-node: this
-          ;; is needed because the name may be within a "destructured"
-          ;; declaration/assignment, so we cannot just take its parent
-          (while (and granparent (not (js2-scope-p granparent)))
-            (cond
-             ((js2-var-init-node-p granparent)
-              (when (null var-init-node)
-                (setq var-init-node granparent)))
-             ((js2-assign-node-p granparent)
-              (when (null assign-node)
-                (setq assign-node granparent))))
-            (setq granparent (js2-node-parent granparent)))
-
-          ;; If we are within a var-init-node, determine if the name is
-          ;; declared and initialized
-          (when var-init-node
-            (let ((result (js2--examine-variable parent node var-init-node)))
-              (setq declared (car result)
-                    assigned (cadr result)
-                    object-key (car (cddr result)))))
-
-          ;; Ignore literal object keys, which are not really variables
-          (unless object-key
-            (when function-param
-              (setq assigned ?P))
-
-            (when (null assigned)
-              (cond
-               ((js2-for-in-node-p parent)
-                (setq assigned (eq node (js2-for-in-node-iterator parent))
-                      used (not assigned)))
-               ((js2-function-node-p parent)
-                (setq assigned t
-                      used (js2-wrapper-function-p parent)))
-               ((js2-export-binding-node-p parent)
-                (if (js2-import-clause-node-p (js2-node-parent parent))
-                    (setq declared t
-                          assigned t)
-                  (setq used t)))
-               ((js2-namespace-import-node-p parent)
-                (setq assigned t
-                      used nil))
-               (assign-node
-                (setq assigned (memq node
-                                     (js2--collect-target-symbols
-                                      (js2-assign-node-left assign-node)
-                                      nil))
-                      used (not assigned)))))
-
-            (when declared
-              (setq used nil))
-
-            (js2--add-or-update-symbol node assigned used vars)))))))
-
-(defun js2--classify-variables ()
-  "Collect and classify variables declared or used within js2-mode-ast.
-Traverse the whole ast tree returning a summary of the variables
-usage as an hash-table, keyed by their corresponding symbol table
-entry.
-Each variable is described by a tuple where the car is a flag
-indicating whether the variable has been initialized and the cdr
-is a possibly empty list of name nodes where it is used. External
-symbols, i.e. those not present in the whole scopes hierarchy,
-are ignored."
-  (let ((vars (make-hash-table :test #'eq :size 100)))
-    (js2-visit-ast
-     js2-mode-ast
-     (lambda (node end-p)
-       (when (and (null end-p) (js2-name-node-p node))
-         (let ((parent (js2-node-parent node)))
-           (when parent
-             (js2--classify-variable parent node vars))))
-       t))
-    vars))
-
-(defun js2--get-name-node (node)
-  (cond
-   ((js2-name-node-p node) node)
-   ((js2-function-node-p node)
-    (js2-function-node-name node))
-   ((js2-class-node-p node)
-    (js2-class-node-name node))
-   ((js2-comp-loop-node-p node)
-    (js2-comp-loop-node-iterator node))
-   (t node)))
-
-(defun js2--highlight-unused-variable (symbol info)
-  (let ((name (js2-symbol-name symbol))
-        (inited (car info))
-        (refs (cdr info))
-        pos len)
-    (unless (and inited refs)
-      (if refs
-          (dolist (ref refs)
-            (setq pos (js2-node-abs-pos ref))
-            (setq len (js2-name-node-len ref))
-            (js2-report-warning "msg.uninitialized.variable" name pos len
-                                'js2-warning))
-        (when (or js2-warn-about-unused-function-arguments
-                  (not (eq inited ?P)))
-          (let* ((symn (js2-symbol-ast-node symbol))
-                 (namen (js2--get-name-node symn)))
-            (unless (js2-node-top-level-decl-p namen)
-              (setq pos (js2-node-abs-pos namen))
-              (setq len (js2-name-node-len namen))
-              (js2-report-warning "msg.unused.variable" name pos len
-                                  'js2-warning))))))))
-
-(defun js2-highlight-unused-variables ()
-  "Highlight unused variables."
-  (let ((vars (js2--classify-variables)))
-    (maphash #'js2--highlight-unused-variable vars)))
-
-;;;###autoload
-(define-minor-mode js2-highlight-unused-variables-mode
-  "Toggle highlight of unused variables."
-  :lighter ""
-  (if js2-highlight-unused-variables-mode
-      (add-hook 'js2-post-parse-callbacks
-                #'js2-highlight-unused-variables nil t)
-    (remove-hook 'js2-post-parse-callbacks
-                 #'js2-highlight-unused-variables t)))
-
-(defun js2-add-additional-externs (externs)
-  (setq js2-additional-externs
-        (nconc externs
-               js2-additional-externs)))
-
-(defun js2-get-jslint-comment-identifiers (comment)
-  (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 (concat "/\\* *" comment "\\(?: \\|$\\)"))))
-           append (js2-get-jslint-comment-identifiers-in
-                   (match-end 0)
-                   (js2-node-abs-end node))))
-
-(defun js2-get-jslint-comment-identifiers-in (beg end)
-  (let (res)
-    (save-excursion
-      (goto-char beg)
-      (while (re-search-forward js2-mode-identifier-re end t)
-        (let ((match (match-string 0)))
-          (unless (member match '("true" "false"))
-            (push match res)))))
-    (nreverse res)))
-
-(defun js2-apply-jslint-globals ()
-  (js2-add-additional-externs (js2-get-jslint-globals)))
-
-(defun js2-get-jslint-globals ()
-  (js2-get-jslint-comment-identifiers "global"))
-
-(defun js2-apply-jslint-declaration-externs ()
-  (js2-add-additional-externs (js2-get-jslint-declaration-externs)))
-
-(defvar js2-jslint-declaration-externs
-  `(("browser" . ,(mapcar 'symbol-name
-                          '(Audio clearInterval clearTimeout document
-                            event history Image location name
-                            navigator Option screen setInterval
-                            setTimeout XMLHttpRequest)))
-    ("node" . ,(mapcar 'symbol-name
-                       '(Buffer clearImmediate clearInterval
-                         clearTimeout console exports global module
-                         process querystring require setImmediate
-                         setInterval setTimeout __dirname
-                         __filename)))
-    ("es6" . ,(mapcar 'symbol-name
-                      '(ArrayBuffer DataView Float32Array
-                        Float64Array Int8Array Int16Array Int32Array
-                        Intl Map Promise Proxy Reflect Set Symbol
-                        System Uint8Array Uint8ClampedArray
-                        Uint16Array Uint32Array WeakMap WeakSet)))
-    ("couch" . ,(mapcar 'symbol-name
-                        '(emit getRow isArray log provides
-                          registerType require send start sum
-                          toJSON)))
-    ("devel" . ,(mapcar 'symbol-name
-                        '(alert confirm console Debug opera prompt
-                          WSH)))))
-
-(defun js2-get-jslint-declaration-externs ()
-  (apply 'append
-         (mapcar (lambda (identifier)
-                   (cdr (assoc identifier
-                               js2-jslint-declaration-externs)))
-                 (js2-get-jslint-comment-identifiers "jslint"))))
-
-;;; IMenu support
-
-;; We currently only support imenu, but eventually should support speedbar and
-;; possibly other browsing mechanisms.
-
-;; The basic strategy is to identify function assignment targets of the form
-;; `foo.bar.baz', convert them to (list fn foo bar baz <position>), and push the
-;; list into `js2-imenu-recorder'.  The lists are merged into a trie-like tree
-;; for imenu after parsing is finished.
-
-;; A `foo.bar.baz' assignment target may be expressed in many ways in
-;; JavaScript, and the general problem is undecidable.  However, several forms
-;; are readily recognizable at parse-time; the forms we attempt to recognize
-;; include:
-
-;;  function foo()  -- function declaration
-;;  foo = function()  -- function expression assigned to variable
-;;  foo.bar.baz = function()  -- function expr assigned to nested property-get
-;;  foo = {bar: function()}  -- fun prop in object literal assigned to var
-;;  foo = {bar: {baz: function()}} -- inside nested object literal
-;;  foo.bar = {baz: function()}} -- obj lit assigned to nested prop get
-;;  a.b = {c: {d: function()}} -- nested obj lit assigned to nested prop get
-;;  foo = {get bar() {...}}  -- getter/setter in obj literal
-;;  function foo() {function bar() {...}}  -- nested function
-;;  foo['a'] = function()  -- fun expr assigned to deterministic element-get
-
-;; This list boils down to a few forms that can be combined recursively.
-;; Top-level named function declarations include both the left-hand (name)
-;; and the right-hand (function value) expressions needed to produce an imenu
-;; entry.  The other "right-hand" forms we need to look for are:
-;;  - functions declared as props/getters/setters in object literals
-;;  - nested named function declarations
-;; The "left-hand" expressions that functions can be assigned to include:
-;;  - local/global variables
-;;  - nested property-get expressions like a.b.c.d
-;;  - element gets like foo[10] or foo['bar'] where the index
-;;    expression can be trivially converted to a property name.  They
-;;    effectively then become property gets.
-
-;; All the different definition types are canonicalized into the form
-;; foo.bar.baz = position-of-function-keyword
-
-;; We need to build a trie-like structure for imenu.  As an example,
-;; consider the following JavaScript code:
-
-;; a = function() {...}  // function at position 5
-;; b = function() {...}  // function at position 25
-;; foo = function() {...} // function at position 100
-;; foo.bar = function() {...} // function at position 200
-;; foo.bar.baz = function() {...} // function at position 300
-;; foo.bar.zab = function() {...} // function at position 400
-
-;; During parsing we accumulate an entry for each definition in
-;; the variable `js2-imenu-recorder', like so:
-
-;; '((fn a 5)
-;;   (fn b 25)
-;;   (fn foo 100)
-;;   (fn foo bar 200)
-;;   (fn foo bar baz 300)
-;;   (fn foo bar zab 400))
-
-;; Where 'fn' is the respective function node.
-;; After parsing these entries are merged into this alist-trie:
-
-;; '((a . 1)
-;;   (b . 2)
-;;   (foo (<definition> . 3)
-;;        (bar (<definition> . 6)
-;;             (baz . 100)
-;;             (zab . 200))))
-
-;; Note the wacky need for a <definition> name.  The token can be anything
-;; that isn't a valid JavaScript identifier, because you might make foo
-;; a function and then start setting properties on it that are also functions.
-
-(defun js2-prop-node-name (node)
-  "Return the name of a node that may be a property-get/property-name.
-If NODE is not a valid name-node, string-node or integral number-node,
-returns nil.  Otherwise returns the string name/value of the node."
-  (cond
-   ((js2-name-node-p node)
-    (js2-name-node-name node))
-   ((js2-string-node-p node)
-    (js2-string-node-value node))
-   ((and (js2-number-node-p node)
-         (string-match "^[0-9]+$" (js2-number-node-value node)))
-    (js2-number-node-value node))
-   ((eq (js2-node-type node) js2-THIS)
-    "this")
-   ((eq (js2-node-type node) js2-SUPER)
-    "super")))
-
-(defun js2-node-qname-component (node)
-  "Return the name of this node, if it contributes to a qname.
-Returns nil if the node doesn't contribute."
-  (copy-sequence
-   (or (js2-prop-node-name node)
-       (cond
-        ((and (js2-function-node-p node)
-              (js2-function-node-name node))
-         (js2-name-node-name (js2-function-node-name node)))
-        ((js2-computed-prop-name-node-p node)
-         "[computed]")))))
-
-(defun js2-record-imenu-entry (fn-node qname pos)
-  "Add an entry to `js2-imenu-recorder'.
-FN-NODE should be the current item's function node.
-
-Associate FN-NODE with its QNAME for later lookup.
-This is used in postprocessing the chain list.  For each chain, we find
-the parent function, look up its qname, then prepend a copy of it to the chain."
-  (push (cons fn-node (append qname (list pos))) js2-imenu-recorder)
-  (unless js2-imenu-function-map
-    (setq js2-imenu-function-map (make-hash-table :test 'eq)))
-  (puthash fn-node qname js2-imenu-function-map))
-
-(defun js2-record-imenu-functions (node &optional var)
-  "Record function definitions for imenu.
-NODE is a function node or an object literal.
-VAR, if non-nil, is the expression that NODE is being assigned to.
-When passed arguments of wrong type, does nothing."
-  (when js2-parse-ide-mode
-    (let ((fun-p (js2-function-node-p node))
-          qname fname-node)
-      (cond
-       ;; non-anonymous function declaration?
-       ((and fun-p
-             (not var)
-             (setq fname-node (js2-function-node-name node)))
-        (js2-record-imenu-entry node (list fname-node) (js2-node-pos node)))
-       ;; for remaining forms, compute left-side tree branch first
-       ((and var (setq qname (js2-compute-nested-prop-get var)))
-        (cond
-         ;; foo.bar.baz = function
-         (fun-p
-          (js2-record-imenu-entry node qname (js2-node-pos node)))
-         ;; foo.bar.baz = object-literal
-         ;; look for nested functions:  {a: {b: function() {...} }}
-         ((js2-object-node-p node)
-          ;; Node position here is still absolute, since the parser
-          ;; passes the assignment target and value expressions
-          ;; to us before they are added as children of the assignment node.
-          (js2-record-object-literal node qname (js2-node-pos node)))))))))
-
-(defun js2-compute-nested-prop-get (node)
-  "If NODE is of form foo.bar, foo['bar'], or any nested combination, return
-component nodes as a list.  Otherwise return nil.  Element-gets are treated
-as property-gets if the index expression is a string, or a positive integer."
-  (let (left right head)
-    (cond
-     ((or (js2-name-node-p node)
-          (js2-this-or-super-node-p node))
-      (list node))
-     ;; foo.bar.baz is parenthesized as (foo.bar).baz => right operand is a leaf
-     ((js2-prop-get-node-p node)        ; foo.bar
-      (setq left (js2-prop-get-node-left node)
-            right (js2-prop-get-node-right node))
-      (if (setq head (js2-compute-nested-prop-get left))
-          (nconc head (list right))))
-     ((js2-elem-get-node-p node)        ; foo['bar'] or foo[101]
-      (setq left (js2-elem-get-node-target node)
-            right (js2-elem-get-node-element node))
-      (if (or (js2-string-node-p right)      ; ['bar']
-              (and (js2-number-node-p right) ; [10]
-                   (string-match "^[0-9]+$"
-                                 (js2-number-node-value right))))
-          (if (setq head (js2-compute-nested-prop-get left))
-              (nconc head (list right))))))))
-
-(defun js2-record-object-literal (node qname pos)
-  "Recursively process an object literal looking for functions.
-NODE is an object literal that is the right-hand child of an assignment
-expression.  QNAME is a list of nodes representing the assignment target,
-e.g. for foo.bar.baz = {...}, QNAME is (foo-node bar-node baz-node).
-POS is the absolute position of the node.
-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'
-      (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.
-Also returns t if NODE's name is not defined in any scope, since it implies
-that it's an external variable, which must also be in the top-level scope."
-  (let* ((name (js2-prop-node-name node))
-         (this-scope (js2-node-get-enclosing-scope node))
-         defining-scope)
-    (cond
-     ((js2-this-or-super-node-p node)
-      nil)
-     ((null this-scope)
-      t)
-     ((setq defining-scope (js2-get-defining-scope this-scope name))
-      (js2-ast-root-p defining-scope))
-     (t t))))
-
-(defun js2-wrapper-function-p (node)
-  "Return t if NODE is a function expression that's immediately invoked.
-NODE must be `js2-function-node'."
-  (let ((parent (js2-node-parent node)))
-    (or
-     ;; function(){...}();
-     (and (js2-call-node-p parent)
-          (eq node (js2-call-node-target parent)))
-     (and (js2-paren-node-p parent)
-          ;; (function(){...})();
-          (or (js2-call-node-p (setq parent (js2-node-parent parent)))
-              ;; (function(){...}).call(this);
-              (and (js2-prop-get-node-p parent)
-                   (member (js2-name-node-name (js2-prop-get-node-right parent))
-                           '("call" "apply"))
-                   (js2-call-node-p (js2-node-parent parent))))))))
-
-(defun js2-browse-postprocess-chains ()
-  "Modify function-declaration name chains after parsing finishes.
-Some of the information is only available after the parse tree is complete.
-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
-        ;; 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))
-            (push chain result)
-          (when (js2-this-or-super-node-p head)
-            (setq chain (cdr chain))) ; discard this-node
-          (when (setq fn (js2-node-parent-script-or-fn current-fn))
-            (setq parent-qname (gethash fn js2-imenu-function-map 'not-found))
-            (when (eq parent-qname 'not-found)
-              ;; anonymous function expressions are not recorded
-              ;; during the parse, so we need to handle this case here
-              (setq parent-qname
-                    (if (js2-wrapper-function-p fn)
-                        (let ((grandparent (js2-node-parent-script-or-fn fn)))
-                          (if (js2-ast-root-p grandparent)
-                              nil
-                            (gethash grandparent js2-imenu-function-map 'skip)))
-                      'skip))
-              (puthash fn parent-qname js2-imenu-function-map))
-            (if (eq parent-qname 'skip)
-                ;; We don't show it, let's record that fact.
-                (remhash current-fn js2-imenu-function-map)
-              ;; Prepend parent fn qname to this chain.
-              (let ((qname (append parent-qname chain)))
-                (puthash current-fn (butlast qname) js2-imenu-function-map)
-                (push qname result)))))))
-    ;; Collect chains obtained by third-party code.
-    (let (js2-imenu-recorder)
-      (run-hooks 'js2-build-imenu-callbacks)
-      (dolist (entry js2-imenu-recorder)
-        (push (cdr entry) result)))
-    ;; Finally replace each node in each chain with its name.
-    (dolist (chain result)
-      (setq p chain)
-      (while p
-        (if (js2-node-p (setq elem (car p)))
-            (setcar p (js2-node-qname-component elem)))
-        (setq p (cdr p))))
-    result))
-
-;; Merge name chains into a trie-like tree structure of nested lists.
-;; To simplify construction of the trie, we first build it out using the rule
-;; that the trie consists of lists of pairs.  Each pair is a 2-element array:
-;; [key, num-or-list].  The second element can be a number; if so, this key
-;; is a leaf-node with only one value.  (I.e. there is only one declaration
-;; associated with the key at this level.)  Otherwise the second element is
-;; a list of pairs, with the rule applied recursively.  This symmetry permits
-;; a simple recursive formulation.
-;;
-;; js2-mode is building the data structure for imenu.  The imenu documentation
-;; claims that it's the structure above, but in practice it wants the children
-;; at the same list level as the key for that level, which is how I've drawn
-;; the "Expected final result" above.  We'll postprocess the trie to remove the
-;; list wrapper around the children at each level.
-;;
-;; A completed nested imenu-alist entry looks like this:
-;;       '(("foo"
-;;          ("<definition>" . 7)
-;;          ("bar"
-;;           ("a" . 40)
-;;           ("b" . 60))))
-;;
-;; In particular, the documentation for `imenu--index-alist' says that
-;; a nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
-;; The sub-alist entries immediately follow INDEX-NAME, the head of the list.
-
-(defun js2-treeify (lst)
-  "Convert (a b c d) to (a ((b ((c d)))))."
-  (if (null (cddr lst))  ; list length <= 2
-      lst
-    (list (car lst) (list (js2-treeify (cdr lst))))))
-
-(defun js2-build-alist-trie (chains trie)
-  "Merge declaration name chains into a trie-like alist structure for imenu.
-CHAINS is the qname chain list produced during parsing. TRIE is a
-list of elements built up so far."
-  (let (head tail pos branch kids)
-    (dolist (chain chains)
-      (setq head (car chain)
-            tail (cdr chain)
-            pos (if (numberp (car tail)) (car tail))
-            branch (js2-find-if (lambda (n)
-                                  (string= (car n) head))
-                                trie)
-            kids (cl-second branch))
-      (cond
-       ;; case 1:  this key isn't in the trie yet
-       ((null branch)
-        (if trie
-            (setcdr (last trie) (list (js2-treeify chain)))
-          (setq trie (list (js2-treeify chain)))))
-       ;; case 2:  key is present with a single number entry:  replace w/ list
-       ;;  ("a1" 10)  +  ("a1" 20) => ("a1" (("<definition>" 10)
-       ;;                                    ("<definition>" 20)))
-       ((numberp kids)
-        (setcar (cdr branch)
-                (list (list "<definition-1>" kids)
-                      (if pos
-                          (list "<definition-2>" pos)
-                        (js2-treeify tail)))))
-       ;; case 3:  key is there (with kids), and we're a number entry
-       (pos
-        (setcdr (last kids)
-                (list
-                 (list (format "<definition-%d>"
-                               (1+ (cl-loop for kid in kids
-                                            count (eq ?< (aref (car kid) 0)))))
-                       pos))))
-       ;; case 4:  key is there with kids, need to merge in our chain
-       (t
-        (js2-build-alist-trie (list tail) kids))))
-    trie))
-
-(defun js2-flatten-trie (trie)
-  "Convert TRIE to imenu-format.
-Recurses through nodes, and for each one whose second element is a list,
-appends the list's flattened elements to the current element.  Also
-changes the tails into conses.  For instance, this pre-flattened trie
-
-'(a ((b 20)
-     (c ((d 30)
-         (e 40)))))
-
-becomes
-
-'(a (b . 20)
-    (c (d . 30)
-       (e . 40)))
-
-Note that the root of the trie has no key, just a list of chains.
-This is also true for the value of any key with multiple children,
-e.g. key 'c' in the example above."
-  (cond
-   ((listp (car trie))
-    (mapcar #'js2-flatten-trie trie))
-   (t
-    (if (numberp (cl-second trie))
-        (cons (car trie) (cl-second trie))
-      ;; else pop list and append its kids
-      (apply #'append (list (car trie)) (js2-flatten-trie (cdr trie)))))))
-
-(defun js2-build-imenu-index ()
-  "Turn `js2-imenu-recorder' into an imenu data structure."
-  (when (eq js2-imenu-recorder 'empty)
-    (setq js2-imenu-recorder nil))
-  (let* ((chains (js2-browse-postprocess-chains))
-         (result (js2-build-alist-trie chains nil)))
-    (js2-flatten-trie result)))
-
-(defun js2-test-print-chains (chains)
-  "Print a list of qname chains.
-Each element of CHAINS is a list of the form (NODE [NODE *] pos);
-i.e. one or more nodes, and an integer position as the list tail."
-  (mapconcat (lambda (chain)
-               (concat "("
-                       (mapconcat (lambda (elem)
-                                    (if (js2-node-p elem)
-                                        (or (js2-node-qname-component elem)
-                                            "nil")
-                                      (number-to-string elem)))
-                                  chain
-                                  " ")
-                       ")"))
-             chains
-             "\n"))
-
-;;; Parser
-
-(defconst js2-version "1.8.5"
-  "Version of JavaScript supported.")
-
-(defun js2-record-face (face &optional token)
-  "Record a style run of FACE for TOKEN or the current token."
-  (unless token (setq token (js2-current-token)))
-  (js2-set-face (js2-token-beg token) (js2-token-end token) face 'record))
-
-(defsubst js2-node-end (n)
-  "Computes the absolute end of node N.
-Use with caution!  Assumes `js2-node-pos' is -absolute-, which
-is only true until the node is added to its parent; i.e., while parsing."
-  (+ (js2-node-pos n)
-     (js2-node-len n)))
-
-(defun js2-record-comment (token)
-  "Record a comment in `js2-scanned-comments'."
-  (let ((ct (js2-token-comment-type token))
-        (beg (js2-token-beg token))
-        (end (js2-token-end token)))
-    (push (make-js2-comment-node :len (- end beg)
-                                 :format ct)
-          js2-scanned-comments)
-    (when js2-parse-ide-mode
-      (js2-record-face (if (eq ct 'jsdoc)
-                           'font-lock-doc-face
-                         'font-lock-comment-face)
-                       token)
-      (when (memq ct '(html preprocessor))
-        ;; Tell cc-engine the bounds of the comment.
-        (js2-record-text-property beg (1- end) 'c-in-sws t)))))
-
-(defun js2-peek-token (&optional modifier)
-  "Return the next token type without consuming it.
-If `js2-ti-lookahead' is positive, return the type of next token
-from `js2-ti-tokens'.  Otherwise, call `js2-get-token'."
-  (if (not (zerop js2-ti-lookahead))
-      (js2-token-type
-       (aref js2-ti-tokens (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens)))
-    (let ((tt (js2-get-token-internal modifier)))
-      (js2-unget-token)
-      tt)))
-
-(defalias 'js2-next-token 'js2-get-token)
-
-(defun js2-match-token (match &optional dont-unget)
-  "Get next token and return t if it matches MATCH, a bytecode.
-Returns nil and consumes nothing if MATCH is not the next token."
-  (if (/= (js2-get-token) match)
-      (ignore (unless dont-unget (js2-unget-token)))
-    t))
-
-(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 (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 beg 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)))
-
-(defun js2-match-prop-name ()
-  "Consume token and return t if next token is a valid property name.
-If `js2-language-version' is >= 180, a keyword or reserved word
-is considered valid name as well."
-  (if (eq js2-NAME (js2-get-prop-name-token))
-      t
-    (js2-unget-token)
-    nil))
-
-(defun js2-must-match-prop-name (msg-id &optional pos len)
-  (if (js2-match-prop-name)
-      t
-    (js2-report-error msg-id nil pos len)
-    nil))
-
-(defun js2-peek-token-or-eol ()
-  "Return js2-EOL if the next token immediately follows a newline.
-Else returns the next token.  Used in situations where we don't
-consider certain token types valid if they are preceded by a newline.
-One example is the postfix ++ or -- operator, which has to be on the
-same line as its operand."
-  (let ((tt (js2-get-token))
-        (follows-eol (js2-token-follows-eol-p (js2-current-token))))
-    (js2-unget-token)
-    (if follows-eol
-        js2-EOL
-      tt)))
-
-(defun js2-must-match (token msg-id &optional pos len)
-  "Match next token to token code TOKEN, or record a syntax error.
-MSG-ID is the error message to report if the match fails.
-Returns t on match, nil if no match."
-  (if (js2-match-token token t)
-      t
-    (js2-report-error msg-id nil pos len)
-    (js2-unget-token)
-    nil))
-
-(defun js2-must-match-name (msg-id)
-  (if (js2-match-token js2-NAME t)
-      t
-    (if (eq (js2-current-token-type) js2-RESERVED)
-        (js2-report-error "msg.reserved.id" (js2-current-token-string))
-      (js2-report-error msg-id)
-      (js2-unget-token))
-    nil))
-
-(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)))
-
-(defun js2-check-activation-name (name _token)
-  (when (js2-inside-function)
-    ;; skip language-version 1.2 check from Rhino
-    (if (or (string= "arguments" name)
-            (and js2-compiler-activation-names  ; only used in codegen
-                 (gethash name js2-compiler-activation-names)))
-        (js2-set-requires-activation))))
-
-(defun js2-set-is-generator ()
-  (let ((fn-node js2-current-script-or-fn))
-    (when (and (js2-function-node-p fn-node)
-               (not (js2-function-node-generator-type fn-node)))
-      (setf (js2-function-node-generator-type js2-current-script-or-fn) 'LEGACY))))
-
-(defun js2-must-have-xml ()
-  (unless js2-compiler-xml-available
-    (js2-report-error "msg.XML.not.available")))
-
-(defun js2-push-scope (scope)
-  "Push SCOPE, a `js2-scope', onto the lexical scope chain."
-  (cl-assert (js2-scope-p scope))
-  (cl-assert (null (js2-scope-parent-scope scope)))
-  (cl-assert (not (eq js2-current-scope scope)))
-  (setf (js2-scope-parent-scope scope) js2-current-scope
-        js2-current-scope scope))
-
-(defsubst js2-pop-scope ()
-  (setq js2-current-scope
-        (js2-scope-parent-scope js2-current-scope)))
-
-(defun js2-enter-loop (loop-node)
-  (push loop-node js2-loop-set)
-  (push loop-node js2-loop-and-switch-set)
-  (js2-push-scope loop-node)
-  ;; Tell the current labeled statement (if any) its statement,
-  ;; and set the jump target of the first label to the loop.
-  ;; These are used in `js2-parse-continue' to verify that the
-  ;; continue target is an actual labeled loop.  (And for codegen.)
-  (when js2-labeled-stmt
-    (setf (js2-labeled-stmt-node-stmt js2-labeled-stmt) loop-node
-          (js2-label-node-loop (car (js2-labeled-stmt-node-labels
-                                     js2-labeled-stmt))) loop-node)))
-
-(defun js2-exit-loop ()
-  (pop js2-loop-set)
-  (pop js2-loop-and-switch-set)
-  (js2-pop-scope))
-
-(defsubst js2-enter-switch (switch-node)
-  (js2-push-scope switch-node)
-  (push switch-node js2-loop-and-switch-set))
-
-(defsubst js2-exit-switch ()
-  (js2-pop-scope)
-  (pop js2-loop-and-switch-set))
-
-(defsubst js2-get-directive (node)
-  "Return NODE's value if it is a directive, nil otherwise.
-
-A directive is an otherwise-meaningless expression statement
-consisting of a string literal, such as \"use strict\"."
-  (and (js2-expr-stmt-node-p node)
-       (js2-string-node-p (setq node (js2-expr-stmt-node-expr node)))
-       (js2-string-node-value node)))
-
-(defun js2-parse (&optional buf cb)
-  "Tell the js2 parser to parse a region of JavaScript.
-
-BUF is a buffer or buffer name containing the code to parse.
-Call `narrow-to-region' first to parse only part of the buffer.
-
-The returned AST root node is given some additional properties:
-  `node-count' - total number of nodes in the AST
-  `buffer' - BUF.  The buffer it refers to may change or be killed,
-             so the value is not necessarily reliable.
-
-An optional callback CB can be specified to report parsing
-progress.  If `(functionp CB)' returns t, it will be called with
-the current line number once before parsing begins, then again
-each time the lexer reaches a new line number.
-
-CB can also be a list of the form `(symbol cb ...)' to specify
-multiple callbacks with different criteria.  Each symbol is a
-criterion keyword, and the following element is the callback to
-call
-
-  :line  - called whenever the line number changes
-  :token - called for each new token consumed
-
-The list of criteria could be extended to include entering or
-leaving a statement, an expression, or a function definition."
-  (if (and cb (not (functionp cb)))
-      (error "criteria callbacks not yet implemented"))
-  (let ((inhibit-point-motion-hooks t)
-        (js2-compiler-xml-available (>= js2-language-version 160))
-        ;; This is a recursive-descent parser, so give it a big stack.
-        (max-lisp-eval-depth (max max-lisp-eval-depth 3000))
-        (max-specpdl-size (max max-specpdl-size 3000))
-        (case-fold-search nil)
-        ast)
-    (with-current-buffer (or buf (current-buffer))
-      (setq js2-scanned-comments nil
-            js2-parsed-errors nil
-            js2-parsed-warnings nil
-            js2-imenu-recorder nil
-            js2-imenu-function-map nil
-            js2-label-set nil)
-      (js2-init-scanner)
-      (setq ast (js2-do-parse))
-      (unless js2-ts-hit-eof
-        (js2-report-error "msg.got.syntax.errors" (length js2-parsed-errors)))
-      (setf (js2-ast-root-errors ast) js2-parsed-errors
-            (js2-ast-root-warnings ast) js2-parsed-warnings)
-      ;; if we didn't find any declarations, put a dummy in this list so we
-      ;; don't end up re-parsing the buffer in `js2-mode-create-imenu-index'
-      (unless js2-imenu-recorder
-        (setq js2-imenu-recorder 'empty))
-      (run-hooks 'js2-parse-finished-hook)
-      ast)))
-
-;; Corresponds to Rhino's Parser.parse() method.
-(defun js2-do-parse ()
-  "Parse current buffer starting from current point.
-Scanner should be initialized."
-  (let ((pos js2-ts-cursor)
-        (end js2-ts-cursor)  ; in case file is empty
-        root n tt
-        (in-directive-prologue t)
-        (js2-in-use-strict-directive js2-in-use-strict-directive)
-        directive)
-    ;; initialize buffer-local parsing vars
-    (setf root (make-js2-ast-root :buffer (buffer-name) :pos pos)
-          js2-current-script-or-fn root
-          js2-current-scope root
-          js2-nesting-of-function 0
-          js2-labeled-stmt nil
-          js2-recorded-identifiers nil  ; for js2-highlight
-          js2-in-use-strict-directive js2-mode-assume-strict)
-    (while (/= (setq tt (js2-get-token)) js2-EOF)
-      (if (= tt js2-FUNCTION)
-          (progn
-            (setq n (if js2-called-by-compile-function
-                        (js2-parse-function-expr)
-                      (js2-parse-function-stmt))))
-        ;; not a function - parse a statement
-        (js2-unget-token)
-        (setq n (js2-parse-statement))
-        (when in-directive-prologue
-          (setq directive (js2-get-directive n))
-          (cond
-           ((null directive)
-            (setq in-directive-prologue nil))
-           ((string= directive "use strict")
-            (setq js2-in-use-strict-directive t)))))
-      ;; add function or statement to script
-      (setq end (js2-node-end n))
-      (js2-block-node-push root n))
-    ;; add comments to root in lexical order
-    (when js2-scanned-comments
-      ;; if we find a comment beyond end of normal kids, use its end
-      (setq end (max end (js2-node-end (cl-first js2-scanned-comments))))
-      (dolist (comment js2-scanned-comments)
-        (push comment (js2-ast-root-comments root))
-        (js2-node-add-children root comment)))
-    (setf (js2-node-len root) (- end pos))
-    (setq js2-mode-ast root)  ; Make sure this is available for callbacks.
-    ;; Give extensions a chance to muck with things before highlighting starts.
-    (let ((js2-additional-externs js2-additional-externs))
-      (js2-filter-parsed-warnings)
-      (save-excursion
-        (run-hooks 'js2-post-parse-callbacks))
-      (js2-highlight-undeclared-vars))
-    root))
-
-(defun js2-filter-parsed-warnings ()
-  "Remove `js2-parsed-warnings' elements that match `js2-ignored-warnings'."
-  (when js2-ignored-warnings
-    (setq js2-parsed-warnings
-          (cl-remove-if
-           (lambda (warning)
-             (let ((msg (caar warning)))
-               (member msg js2-ignored-warnings)))
-           js2-parsed-warnings)))
-  js2-parsed-warnings)
-
-(defun js2-parse-function-closure-body (fn-node)
-  "Parse a JavaScript 1.8 function closure body."
-  (let ((js2-nesting-of-function (1+ js2-nesting-of-function)))
-    (if js2-ts-hit-eof
-        (js2-report-error "msg.no.brace.body" nil
-                          (js2-node-pos fn-node)
-                          (- js2-ts-cursor (js2-node-pos fn-node)))
-      (js2-node-add-children fn-node
-                             (setf (js2-function-node-body fn-node)
-                                   (js2-parse-expr t))))))
-
-(defun js2-parse-function-body (fn-node)
-  (js2-must-match js2-LC "msg.no.brace.body"
-                  (js2-node-pos fn-node)
-                  (- js2-ts-cursor (js2-node-pos fn-node)))
-  (let ((pos (js2-current-token-beg))         ; LC position
-        (pn (make-js2-block-node))  ; starts at LC position
-        tt
-        end
-        not-in-directive-prologue
-        node
-        directive)
-    (cl-incf js2-nesting-of-function)
-    (unwind-protect
-        (while (not (or (= (setq tt (js2-peek-token)) js2-ERROR)
-                        (= tt js2-EOF)
-                        (= tt js2-RC)))
-          (js2-block-node-push
-           pn
-           (if (/= tt js2-FUNCTION)
-               (if not-in-directive-prologue
-                   (js2-parse-statement)
-                 (setq node (js2-parse-statement)
-                       directive (js2-get-directive node))
-                 (cond
-                  ((null directive)
-                   (setq not-in-directive-prologue t))
-                  ((string= directive "use strict")
-                   ;; Back up and reparse the function, because new rules apply
-                   ;; to the function name and parameters.
-                   (when (not js2-in-use-strict-directive)
-                     (setq js2-in-use-strict-directive t)
-                     (throw 'reparse t))))
-                 node)
-             (js2-get-token)
-             (js2-parse-function-stmt))))
-      (cl-decf js2-nesting-of-function))
-    (setq end (js2-current-token-end))  ; assume no curly and leave at current token
-    (if (js2-must-match js2-RC "msg.no.brace.after.body" pos)
-        (setq end (js2-current-token-end)))
-    (setf (js2-node-pos pn) pos
-          (js2-node-len pn) (- end pos))
-    (setf (js2-function-node-body fn-node) pn)
-    (js2-node-add-children fn-node pn)
-    pn))
-
-(defun js2-define-destruct-symbols (node decl-type face &optional ignore-not-in-block)
-  "Declare and fontify destructuring parameters inside NODE.
-NODE is either `js2-array-node', `js2-object-node', or `js2-name-node'.
-
-Return a list of `js2-name-node' nodes representing the symbols
-declared; probably to check them for errors."
-  (let ((name-nodes (js2--collect-target-symbols node t)))
-    (dolist (node name-nodes)
-      (let (leftpos)
-        (js2-define-symbol decl-type (js2-name-node-name node)
-                           node ignore-not-in-block)
-        (when face
-          (js2-set-face (setq leftpos (js2-node-abs-pos node))
-                        (+ leftpos (js2-node-len node))
-                        face 'record))))
-    name-nodes))
-
-(defvar js2-illegal-strict-identifiers
-  '("eval" "arguments")
-  "Identifiers not allowed as variables in strict mode.")
-
-(defun js2-check-strict-identifier (name-node)
-  "Check that NAME-NODE makes a legal strict mode identifier."
-  (when js2-in-use-strict-directive
-    (let ((param-name (js2-name-node-name name-node)))
-      (when (member param-name js2-illegal-strict-identifiers)
-        (js2-report-error "msg.bad.id.strict" param-name
-                          (js2-node-abs-pos name-node) (js2-node-len name-node))))))
-
-(defun js2-check-strict-function-params (preceding-params params)
-  "Given PRECEDING-PARAMS in a function's parameter list, check
-for strict mode errors caused by PARAMS."
-  (when js2-in-use-strict-directive
-    (dolist (param params)
-      (let ((param-name (js2-name-node-name param)))
-        (js2-check-strict-identifier param)
-        (when (cl-some (lambda (param)
-                         (string= (js2-name-node-name param) param-name))
-                       preceding-params)
-          (js2-report-error "msg.dup.param.strict" param-name
-                            (js2-node-abs-pos param) (js2-node-len param)))))))
-
-(defun js2-parse-function-params (function-type fn-node pos)
-  "Parse the parameters of a function of FUNCTION-TYPE
-represented by FN-NODE at POS."
-  (if (js2-match-token js2-RP)
-      (setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos))
-    (let ((paren-free-arrow (and (eq function-type 'FUNCTION_ARROW)
-                                 (eq (js2-current-token-type) js2-NAME)))
-          params param
-          param-name-nodes new-param-name-nodes
-          rest-param-at)
-      (when paren-free-arrow
-        (js2-unget-token))
-      (cl-loop for tt = (js2-peek-token)
-               do
-               (cond
-                ;; destructuring param
-                ((and (not paren-free-arrow)
-                      (or (= tt js2-LB) (= tt js2-LC)))
-                 (js2-get-token)
-                 (setq param (js2-parse-destruct-primary-expr)
-                       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)))
-                ;; variable name
-                (t
-                 (when (and (>= js2-language-version 200)
-                            (not paren-free-arrow)
-                            (js2-match-token js2-TRIPLEDOT)
-                            (not rest-param-at))
-                   ;; to report errors if there are more parameters
-                   (setq rest-param-at (length params)))
-                 (js2-must-match-name "msg.no.parm")
-                 (js2-record-face 'js2-function-param)
-                 (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 (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)))
-               while
-               (and (js2-match-token js2-COMMA)
-                    (or (< js2-language-version 200)
-                        (not (= js2-RP (js2-peek-token))))))
-      (when (and (not paren-free-arrow)
-                 (js2-must-match js2-RP "msg.no.paren.after.parms"))
-        (setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos)))
-      (when rest-param-at
-        (setf (js2-function-node-rest-p fn-node) t))
-      (dolist (p params)
-        (js2-node-add-children fn-node p)
-        (push p (js2-function-node-params fn-node))))))
-
-(defun js2-check-inconsistent-return-warning (fn-node name)
-  "Possibly show inconsistent-return warning.
-Last token scanned is the close-curly for the function body."
-  (when (and js2-mode-show-strict-warnings
-             js2-strict-inconsistent-return-warning
-             (not (js2-has-consistent-return-usage
-                   (js2-function-node-body fn-node))))
-    ;; Have it extend from close-curly to bol or beginning of block.
-    (let ((pos (save-excursion
-                 (goto-char (js2-current-token-end))
-                 (max (js2-node-abs-pos (js2-function-node-body fn-node))
-                      (point-at-bol))))
-          (end (js2-current-token-end)))
-      (if (cl-plusp (js2-name-node-length name))
-          (js2-add-strict-warning "msg.no.return.value"
-                                  (js2-name-node-name name) pos end)
-        (js2-add-strict-warning "msg.anon.no.return.value" nil pos end)))))
-
-(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")
-    (let ((name (js2-create-name-node t))
-          pn member-expr)
-      (cond
-       ((js2-match-token js2-LP)
-        (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 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-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 async-p 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)))
-    (setf fn-node (make-js2-function-node :pos pos
-                                          :name name
-                                          :form function-type
-                                          :lp (if lp (- lp pos))
-                                          :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)
-      (when (and (eq function-type 'FUNCTION_STATEMENT)
-                 (cl-plusp (js2-name-node-length name)))
-        ;; Function statements define a symbol in the enclosing scope
-        (js2-define-symbol js2-FUNCTION (js2-name-node-name name) fn-node))
-      (when js2-in-use-strict-directive
-        (js2-check-strict-identifier name)))
-    (if (or (js2-inside-function) (cl-plusp js2-nesting-of-with))
-        ;; 1. Nested functions are not affected by the dynamic scope flag
-        ;;    as dynamic scope is already a parent of their scope.
-        ;; 2. Functions defined under the with statement also immune to
-        ;;    this setup, in which case dynamic scope is ignored in favor
-        ;;    of the with object.
-        (setf (js2-function-node-ignore-dynamic fn-node) t))
-    ;; dynamically bind all the per-function variables
-    (let ((js2-current-script-or-fn fn-node)
-          (js2-current-scope fn-node)
-          (js2-nesting-of-with 0)
-          (js2-end-flags 0)
-          js2-label-set
-          js2-loop-set
-          js2-loop-and-switch-set)
-      (js2-parse-function-params function-type fn-node pos)
-      (when (eq function-type 'FUNCTION_ARROW)
-        (js2-must-match js2-ARROW "msg.bad.arrow.args"))
-      (if (and (>= js2-language-version 180)
-               (/= (js2-peek-token) js2-LC))
-          (js2-parse-function-closure-body fn-node)
-        (js2-parse-function-body fn-node))
-      (js2-check-inconsistent-return-warning fn-node name)
-
-      (when name
-        (js2-node-add-children fn-node name)
-        ;; Function expressions define a name only in the body of the
-        ;; function, and only if not hidden by a parameter name
-        (when (and (eq function-type 'FUNCTION_EXPRESSION)
-                   (null (js2-scope-get-symbol js2-current-scope
-                                               (js2-name-node-name name))))
-          (js2-define-symbol js2-FUNCTION
-                             (js2-name-node-name name)
-                             fn-node))
-        (when (eq function-type 'FUNCTION_STATEMENT)
-          (js2-record-imenu-functions fn-node))))
-
-    (setf (js2-node-len fn-node) (- (js2-current-token-end) pos))
-    ;; Rhino doesn't do this, but we need it for finding undeclared vars.
-    ;; We wait until after parsing the function to set its parent scope,
-    ;; since `js2-define-symbol' needs the defining-scope check to stop
-    ;; at the function boundary when checking for redeclarations.
-    (setf (js2-scope-parent-scope fn-node) js2-current-scope)
-    fn-node))
-
-(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."
-  (let ((continue t)
-        ts-state
-        fn-node
-        ;; Preserve strict state outside this function.
-        (js2-in-use-strict-directive js2-in-use-strict-directive))
-    ;; Parse multiple times if a new strict mode directive is discovered in the
-    ;; function body, as new rules will be retroactively applied to the legality
-    ;; of function names and parameters.
-    (while continue
-      (setq ts-state (make-js2-ts-state))
-      (setq continue (catch 'reparse
-                       (setq fn-node (js2-parse-function-internal
-                                      function-type pos star-p async-p name))
-                       ;; Don't continue.
-                       nil))
-      (when continue
-        (js2-ts-seek ts-state)))
-    fn-node))
-
-(defun js2-parse-statements (&optional parent)
-  "Parse a statement list.  Last token consumed must be js2-LC.
-
-PARENT can be a `js2-block-node', in which case the statements are
-appended to PARENT.  Otherwise a new `js2-block-node' is created
-and returned.
-
-This function does not match the closing js2-RC: the caller
-matches the RC so it can provide a suitable error message if not
-matched.  This means it's up to the caller to set the length of
-the node to include the closing RC.  The node start pos is set to
-the absolute buffer start position, and the caller should fix it
-up to be relative to the parent node.  All children of this block
-node are given relative start positions and correct lengths."
-  (let ((pn (or parent (make-js2-block-node)))
-        tt)
-    (while (and (> (setq tt (js2-peek-token)) js2-EOF)
-                (/= tt js2-RC))
-      (js2-block-node-push pn (js2-parse-statement)))
-    pn))
-
-(defun js2-parse-statement ()
-  (let (pn beg end)
-    ;; coarse-grained user-interrupt check - needs work
-    (and js2-parse-interruptable-p
-         (zerop (% (cl-incf js2-parse-stmt-count)
-                   js2-statements-per-pause))
-         (input-pending-p)
-         (throw 'interrupted t))
-    (setq pn (js2-statement-helper))
-    ;; no-side-effects warning check
-    (unless (js2-node-has-side-effects pn)
-      (setq end (js2-node-end pn))
-      (save-excursion
-        (goto-char end)
-        (setq beg (max (js2-node-pos pn) (point-at-bol))))
-      (js2-add-strict-warning "msg.no.side.effects" nil beg end))
-    pn))
-
-;; These correspond to the switch cases in Parser.statementHelper
-(defconst js2-parsers
-  (let ((parsers (make-vector js2-num-tokens
-                                #'js2-parse-expr-stmt)))
-    (aset parsers js2-BREAK     #'js2-parse-break)
-    (aset parsers js2-CLASS     #'js2-parse-class-stmt)
-    (aset parsers js2-CONST     #'js2-parse-const-var)
-    (aset parsers js2-CONTINUE  #'js2-parse-continue)
-    (aset parsers js2-DEBUGGER  #'js2-parse-debugger)
-    (aset parsers js2-DEFAULT   #'js2-parse-default-xml-namespace)
-    (aset parsers js2-DO        #'js2-parse-do)
-    (aset parsers js2-EXPORT    #'js2-parse-export)
-    (aset parsers js2-FOR       #'js2-parse-for)
-    (aset parsers js2-FUNCTION  #'js2-parse-function-stmt)
-    (aset parsers js2-IF        #'js2-parse-if)
-    (aset parsers js2-IMPORT    #'js2-parse-import)
-    (aset parsers js2-LC        #'js2-parse-block)
-    (aset parsers js2-LET       #'js2-parse-let-stmt)
-    (aset parsers js2-NAME      #'js2-parse-name-or-label)
-    (aset parsers js2-RETURN    #'js2-parse-ret-yield)
-    (aset parsers js2-SEMI      #'js2-parse-semi)
-    (aset parsers js2-SWITCH    #'js2-parse-switch)
-    (aset parsers js2-THROW     #'js2-parse-throw)
-    (aset parsers js2-TRY       #'js2-parse-try)
-    (aset parsers js2-VAR       #'js2-parse-const-var)
-    (aset parsers js2-WHILE     #'js2-parse-while)
-    (aset parsers js2-WITH      #'js2-parse-with)
-    (aset parsers js2-YIELD     #'js2-parse-ret-yield)
-    parsers)
-  "A vector mapping token types to parser functions.")
-
-(defun js2-parse-warn-missing-semi (beg end)
-  (and js2-mode-show-strict-warnings
-       js2-strict-missing-semi-warning
-       (js2-add-strict-warning
-        "msg.missing.semi" nil
-        ;; back up to beginning of statement or line
-        (max beg (save-excursion
-                   (goto-char end)
-                   (point-at-bol)))
-        end)))
-
-(defconst js2-no-semi-insertion
-  (list js2-IF
-        js2-SWITCH
-        js2-WHILE
-        js2-DO
-        js2-FOR
-        js2-TRY
-        js2-WITH
-        js2-LC
-        js2-ERROR
-        js2-SEMI
-        js2-CLASS
-        js2-FUNCTION
-        js2-EXPORT)
-  "List of tokens that don't do automatic semicolon insertion.")
-
-(defconst js2-autoinsert-semi-and-warn
-  (list js2-ERROR js2-EOF js2-RC))
-
-(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
-                   (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
-         (js2-labeled-stmt-node-stmt js2-labeled-stmt)
-         (setq js2-labeled-stmt nil))
-    (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)
-                async-stmt)
-      (js2-auto-insert-semicolon pn))
-    pn))
-
-(defun js2-auto-insert-semicolon (pn)
-  (let* ((tt (js2-get-token))
-         (pos (js2-node-pos pn)))
-      (cond
-       ((= tt js2-SEMI)
-        ;; extend the node bounds to include the semicolon.
-        (setf (js2-node-len pn) (- (js2-current-token-end) pos)))
-       ((memq tt js2-autoinsert-semi-and-warn)
-        (js2-unget-token) ; Not ';', do not consume.
-        ;; Autoinsert ;
-        (js2-parse-warn-missing-semi pos (js2-node-end pn)))
-       (t
-        (if (not (js2-token-follows-eol-p (js2-current-token)))
-            ;; Report error if no EOL or autoinsert ';' otherwise
-            (js2-report-error "msg.no.semi.stmt")
-          (js2-parse-warn-missing-semi pos (js2-node-end pn)))
-        (js2-unget-token) ; Not ';', do not consume.
-        ))))
-
-(defun js2-parse-condition ()
-  "Parse a parenthesized boolean expression, e.g. in an if- or while-stmt.
-The parens are discarded and the expression node is returned.
-The `pos' field of the return value is set to an absolute position
-that must be fixed up by the caller.
-Return value is a list (EXPR LP RP), with absolute paren positions."
-  (let (pn lp rp)
-    (if (js2-must-match js2-LP "msg.no.paren.cond")
-        (setq lp (js2-current-token-beg)))
-    (setq pn (js2-parse-expr))
-    (if (js2-must-match js2-RP "msg.no.paren.after.cond")
-        (setq rp (js2-current-token-beg)))
-    ;; Report strict warning on code like "if (a = 7) ..."
-    (if (and js2-strict-cond-assign-warning
-             (js2-assign-node-p pn))
-        (js2-add-strict-warning "msg.equal.as.assign" nil
-                                (js2-node-pos pn)
-                                (+ (js2-node-pos pn)
-                                   (js2-node-len pn))))
-    (list pn lp rp)))
-
-(defun js2-parse-if ()
-  "Parser for if-statement.  Last matched token must be js2-IF."
-  (let ((pos (js2-current-token-beg))
-        cond if-true if-false else-pos end pn)
-    (setq cond (js2-parse-condition)
-          if-true (js2-parse-statement)
-          if-false (if (js2-match-token js2-ELSE)
-                       (progn
-                         (setq else-pos (- (js2-current-token-beg) pos))
-                         (js2-parse-statement)))
-          end (js2-node-end (or if-false if-true))
-          pn (make-js2-if-node :pos pos
-                               :len (- end pos)
-                               :condition (car cond)
-                               :then-part if-true
-                               :else-part if-false
-                               :else-pos else-pos
-                               :lp (js2-relpos (cl-second cond) pos)
-                               :rp (js2-relpos (cl-third cond) pos)))
-    (js2-node-add-children pn (car cond) if-true if-false)
-    pn))
-
-(defun js2-parse-import ()
-  "Parse import statement. The current token must be js2-IMPORT."
-  (unless (js2-ast-root-p js2-current-scope)
-    (js2-report-error "msg.mod.import.decl.at.top.level"))
-  (let ((beg (js2-current-token-beg)))
-    (cond ((js2-match-token js2-STRING)
-           (make-js2-import-node
-            :pos beg
-            :len (- (js2-current-token-end) beg)
-            :module-id (js2-current-token-string)))
-          (t
-           (let* ((import-clause (js2-parse-import-clause))
-                  (from-clause (and import-clause (js2-parse-from-clause)))
-                  (module-id (when from-clause (js2-from-clause-node-module-id from-clause)))
-                  (node (make-js2-import-node
-                         :pos beg
-                         :len (- (js2-current-token-end) beg)
-                         :import import-clause
-                         :from from-clause
-                         :module-id module-id)))
-             (when import-clause
-               (js2-node-add-children node import-clause))
-             (when from-clause
-               (js2-node-add-children node from-clause))
-             node)))))
-
-(defun js2-parse-import-clause ()
-  "Parse the bindings in an import statement.
-This can take many forms:
-
-ImportedDefaultBinding -> 'foo'
-NameSpaceImport -> '* as lib'
-NamedImports -> '{foo as bar, bang}'
-ImportedDefaultBinding , NameSpaceImport -> 'foo, * as lib'
-ImportedDefaultBinding , NamedImports -> 'foo, {bar, baz as bif}'
-
-Try to match namespace imports and named imports first because nothing can
-come after them. If it is an imported default binding, then it could have named
-imports or a namespace import that follows it.
-"
-  (let* ((beg (js2-current-token-beg))
-         (clause (make-js2-import-clause-node
-                  :pos beg))
-         (children (list)))
-    (cond
-     ((js2-match-token js2-MUL)
-      (let ((ns-import (js2-parse-namespace-import)))
-        (when ns-import
-          (let ((name-node (js2-namespace-import-node-name ns-import)))
-            (js2-define-symbol
-             js2-LET (js2-name-node-name name-node) name-node t))
-          (setf (js2-import-clause-node-namespace-import clause) ns-import)
-          (push ns-import children))))
-     ((js2-match-token js2-LC)
-      (let ((imports (js2-parse-export-bindings t)))
-        (setf (js2-import-clause-node-named-imports clause) imports)
-        (dolist (import imports)
-          (push import children)
-          (let ((name-node (js2-export-binding-node-local-name import)))
-            (when name-node
-              (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 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)
-        (push binding children))
-      (when (js2-match-token js2-COMMA)
-        (cond
-         ((js2-match-token js2-MUL)
-          (let ((ns-import (js2-parse-namespace-import)))
-            (let ((name-node (js2-namespace-import-node-name ns-import)))
-              (js2-define-symbol
-               js2-LET (js2-name-node-name name-node) name-node t))
-            (setf (js2-import-clause-node-namespace-import clause) ns-import)
-            (push ns-import children)))
-         ((js2-match-token js2-LC)
-          (let ((imports (js2-parse-export-bindings t)))
-            (setf (js2-import-clause-node-named-imports clause) imports)
-            (dolist (import imports)
-              (push import children)
-              (let ((name-node (js2-export-binding-node-local-name import)))
-                (when name-node
-                  (js2-define-symbol
-                   js2-LET (js2-name-node-name name-node) name-node t))))))
-         (t (js2-report-error "msg.syntax")))))
-     (t (js2-report-error "msg.mod.declaration.after.import")))
-    (setf (js2-node-len clause) (- (js2-current-token-end) beg))
-    (apply #'js2-node-add-children clause children)
-    clause))
-
-(defun js2-parse-namespace-import ()
-  "Parse a namespace import expression such as  '* as bar'.
-The current token must be js2-MUL."
-  (let ((beg (js2-current-token-beg)))
-    (cond
-      ((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)
-                                     (js2-current-token-beg))
-                             :name (js2-current-token-string)))))
-           (js2-node-add-children node (js2-namespace-import-node-name node))
-           node)))
-      (t
-       (js2-unget-token)
-       (js2-report-error "msg.syntax")
-       nil))))
-
-
-(defun js2-parse-from-clause ()
-  "Parse the from clause in an import or export statement. E.g. from 'src/lib'"
-  (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
-{foo as bar, baz as bang}. The current token must be
-js2-LC. Return a lisp list of js2-export-binding-node"
-  (let ((bindings (list)))
-    (while
-        (let ((binding (js2-maybe-parse-export-binding import-p)))
-          (when binding
-            (push binding bindings))
-          (js2-match-token js2-COMMA)))
-    (when (js2-must-match js2-RC (if import-p
-                                     "msg.mod.rc.after.import.spec.list"
-                                   "msg.mod.rc.after.export.spec.list"))
-      (reverse bindings))))
-
-(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
-js2-export-binding-node and consume all the tokens. If it does not match, it
-consumes no tokens."
-  (let ((extern-name (when (js2-match-prop-name) (js2-current-token-string)))
-        (beg (js2-current-token-beg))
-        (extern-name-len (js2-current-token-len))
-        (is-reserved-name (or (= (js2-current-token-type) js2-RESERVED)
-                              (aref js2-kwd-tokens (js2-current-token-type)))))
-    (if extern-name
-        (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 ()
-  "Parser for switch-statement.  Last matched token must be js2-SWITCH."
-  (let ((pos (js2-current-token-beg))
-        tt pn discriminant has-default case-expr case-node
-        case-pos cases stmt lp)
-    (if (js2-must-match js2-LP "msg.no.paren.switch")
-        (setq lp (js2-current-token-beg)))
-    (setq discriminant (js2-parse-expr)
-          pn (make-js2-switch-node :discriminant discriminant
-                                   :pos pos
-                                   :lp (js2-relpos lp pos)))
-    (js2-node-add-children pn discriminant)
-    (js2-enter-switch pn)
-    (unwind-protect
-        (progn
-          (if (js2-must-match js2-RP "msg.no.paren.after.switch")
-              (setf (js2-switch-node-rp pn) (- (js2-current-token-beg) pos)))
-          (js2-must-match js2-LC "msg.no.brace.switch")
-          (catch 'break
-            (while t
-              (setq tt (js2-next-token)
-                    case-pos (js2-current-token-beg))
-              (cond
-               ((= tt js2-RC)
-                (setf (js2-node-len pn) (- (js2-current-token-end) pos))
-                (throw 'break nil))  ; done
-               ((= tt js2-CASE)
-                (setq case-expr (js2-parse-expr))
-                (js2-must-match js2-COLON "msg.no.colon.case"))
-               ((= tt js2-DEFAULT)
-                (if has-default
-                    (js2-report-error "msg.double.switch.default"))
-                (setq has-default t
-                      case-expr nil)
-                (js2-must-match js2-COLON "msg.no.colon.case"))
-               (t
-                (js2-report-error "msg.bad.switch")
-                (throw 'break nil)))
-              (setq case-node (make-js2-case-node :pos case-pos
-                                                  :len (- (js2-current-token-end) case-pos)
-                                                  :expr case-expr))
-              (js2-node-add-children case-node case-expr)
-              (while (and (/= (setq tt (js2-peek-token)) js2-RC)
-                          (/= tt js2-CASE)
-                          (/= tt js2-DEFAULT)
-                          (/= tt js2-EOF))
-                (setf stmt (js2-parse-statement)
-                      (js2-node-len case-node) (- (js2-node-end stmt) case-pos))
-                (js2-block-node-push case-node stmt))
-              (push case-node cases)))
-          ;; add cases last, as pushing reverses the order to be correct
-          (dolist (kid cases)
-            (js2-node-add-children pn kid)
-            (push kid (js2-switch-node-cases pn)))
-          pn)  ; return value
-      (js2-exit-switch))))
-
-(defun js2-parse-while ()
-  "Parser for while-statement.  Last matched token must be js2-WHILE."
-  (let ((pos (js2-current-token-beg))
-        (pn (make-js2-while-node))
-        cond body)
-    (js2-enter-loop pn)
-    (unwind-protect
-        (progn
-          (setf cond (js2-parse-condition)
-                (js2-while-node-condition pn) (car cond)
-                body (js2-parse-statement)
-                (js2-while-node-body pn) body
-                (js2-node-len pn) (- (js2-node-end body) pos)
-                (js2-while-node-lp pn) (js2-relpos (cl-second cond) pos)
-                (js2-while-node-rp pn) (js2-relpos (cl-third cond) pos))
-          (js2-node-add-children pn body (car cond)))
-      (js2-exit-loop))
-    pn))
-
-(defun js2-parse-do ()
-  "Parser for do-statement.  Last matched token must be js2-DO."
-  (let ((pos (js2-current-token-beg))
-        (pn (make-js2-do-node))
-        cond body end)
-    (js2-enter-loop pn)
-    (unwind-protect
-        (progn
-          (setq body (js2-parse-statement))
-          (js2-must-match js2-WHILE "msg.no.while.do")
-          (setf (js2-do-node-while-pos pn) (- (js2-current-token-beg) pos)
-                cond (js2-parse-condition)
-                (js2-do-node-condition pn) (car cond)
-                (js2-do-node-body pn) body
-                end js2-ts-cursor
-                (js2-do-node-lp pn) (js2-relpos (cl-second cond) pos)
-                (js2-do-node-rp pn) (js2-relpos (cl-third cond) pos))
-          (js2-node-add-children pn (car cond) body))
-      (js2-exit-loop))
-    ;; Always auto-insert semicolon to follow SpiderMonkey:
-    ;; It is required by ECMAScript but is ignored by the rest of
-    ;; world; see bug 238945
-    (if (js2-match-token js2-SEMI)
-        (setq end js2-ts-cursor))
-    (setf (js2-node-len pn) (- end pos))
-    pn))
-
-(defun js2-parse-export ()
-  "Parse an export statement.
-The Last matched token must be js2-EXPORT. Currently, the 'default' and 'expr'
-expressions should only be either hoistable expressions (function or generator)
-or assignment expressions, but there is no checking to enforce that and so it
-will parse without error a small subset of
-invalid export statements."
-  (unless (js2-ast-root-p js2-current-scope)
-    (js2-report-error "msg.mod.export.decl.at.top.level"))
-  (let ((beg (js2-current-token-beg))
-        (children (list))
-        exports-list from-clause declaration default)
-    (cond
-     ((js2-match-token js2-MUL)
-      (setq from-clause (js2-parse-from-clause))
-      (when from-clause
-        (push from-clause children)))
-     ((js2-match-token js2-LC)
-      (setq exports-list (js2-parse-export-bindings))
-      (when exports-list
-        (dolist (export exports-list)
-          (push export children)))
-      (when (js2-match-contextual-kwd "from")
-        (js2-unget-token)
-        (setq from-clause (js2-parse-from-clause))))
-     ((js2-match-token js2-DEFAULT)
-      (setq default (cond ((js2-match-token js2-CLASS)
-                           (if (eq (js2-peek-token) js2-NAME)
-                               (js2-parse-class-stmt)
-                             (js2-parse-class-expr)))
-                          ((js2-match-token js2-NAME)
-                           (if (js2-match-async-function)
-                               (if (eq (js2-peek-token) js2-NAME)
-                                   (js2-parse-async-function-stmt)
-                                 (js2-parse-function-expr t))
-                             (js2-unget-token)
-                             (js2-parse-expr)))
-                          ((js2-match-token js2-FUNCTION)
-                           (if (eq (js2-peek-token) js2-NAME)
-                               (js2-parse-function-stmt)
-                             (js2-parse-function-expr)))
-                          (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 (or (js2-function-node-p declaration)
-                     (js2-class-node-p declaration)))
-        (js2-auto-insert-semicolon declaration)))
-    (when default
-      (push default children)
-      (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
-                  :len (- (js2-current-token-end) beg)
-                  :exports-list exports-list
-                  :from-clause from-clause
-                  :declaration declaration
-                  :default default)))
-      (apply #'js2-node-add-children node children)
-      node)))
-
-(defun js2-parse-for ()
-  "Parse a for, for-in or for each-in statement.
-Last matched token must be js2-FOR."
-  (let ((for-pos (js2-current-token-beg))
-        (tmp-scope (make-js2-scope))
-        pn is-for-each is-for-in-or-of is-for-of
-        in-pos each-pos tmp-pos
-        init  ; Node init is also foo in 'foo in object'.
-        cond  ; Node cond is also object in 'foo in object'.
-        incr  ; 3rd section of for-loop initializer.
-        body tt lp rp)
-    ;; See if this is a for each () instead of just a for ()
-    (when (js2-match-token js2-NAME)
-      (if (string= "each" (js2-current-token-string))
-          (progn
-            (setq is-for-each t
-                  each-pos (- (js2-current-token-beg) for-pos)) ; relative
-            (js2-record-face 'font-lock-keyword-face))
-        (js2-report-error "msg.no.paren.for")))
-    (if (js2-must-match js2-LP "msg.no.paren.for")
-        (setq lp (- (js2-current-token-beg) for-pos)))
-    (setq tt (js2-get-token))
-    ;; Capture identifiers inside parens.  We can't create the node
-    ;; (and use it as the current scope) until we know its type.
-    (js2-push-scope tmp-scope)
-    (unwind-protect
-        (progn
-          ;; parse init clause
-          (let ((js2-in-for-init t))  ; set as dynamic variable
-            (cond
-             ((= tt js2-SEMI)
-              (js2-unget-token)
-              (setq init (make-js2-empty-expr-node)))
-             ((or (= tt js2-VAR) (= tt js2-LET) (= tt js2-CONST))
-              (setq init (js2-parse-variables tt (js2-current-token-beg))))
-             (t
-              (js2-unget-token)
-              (setq init (js2-parse-expr)))))
-          (if (or (js2-match-token js2-IN)
-                  (and (>= js2-language-version 200)
-                       (js2-match-contextual-kwd "of")
-                       (setq is-for-of t)))
-              (setq is-for-in-or-of t
-                    in-pos (- (js2-current-token-beg) for-pos)
-                    ;; scope of iteration target object is not the scope we've created above.
-                    ;; stash current scope temporary.
-                    cond (let ((js2-current-scope (js2-scope-parent-scope js2-current-scope)))
-                           (js2-parse-expr)))  ; object over which we're iterating
-            ;; else ordinary for loop - parse cond and incr
-            (js2-must-match js2-SEMI "msg.no.semi.for")
-            (setq cond (if (= (js2-peek-token) js2-SEMI)
-                           (make-js2-empty-expr-node) ; no loop condition
-                         (js2-parse-expr)))
-            (js2-must-match js2-SEMI "msg.no.semi.for.cond")
-            (setq tmp-pos (js2-current-token-end)
-                  incr (if (= (js2-peek-token) js2-RP)
-                           (make-js2-empty-expr-node :pos tmp-pos)
-                         (js2-parse-expr)))))
-      (js2-pop-scope))
-    (if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
-        (setq rp (- (js2-current-token-beg) for-pos)))
-    (if (not is-for-in-or-of)
-        (setq pn (make-js2-for-node :init init
-                                    :condition cond
-                                    :update incr
-                                    :lp lp
-                                    :rp rp))
-      ;; cond could be null if 'in obj' got eaten by the init node.
-      (if (js2-infix-node-p init)
-          ;; it was (foo in bar) instead of (var foo in bar)
-          (setq cond (js2-infix-node-right init)
-                init (js2-infix-node-left init))
-        (if (and (js2-var-decl-node-p init)
-                 (> (length (js2-var-decl-node-kids init)) 1))
-            (js2-report-error "msg.mult.index")))
-      (setq pn (make-js2-for-in-node :iterator init
-                                     :object cond
-                                     :in-pos in-pos
-                                     :foreach-p is-for-each
-                                     :each-pos each-pos
-                                     :forof-p is-for-of
-                                     :lp lp
-                                     :rp rp)))
-    ;; Transplant the declarations.
-    (setf (js2-scope-symbol-table pn)
-          (js2-scope-symbol-table tmp-scope))
-    (unwind-protect
-        (progn
-          (js2-enter-loop pn)
-          ;; We have to parse the body -after- creating the loop node,
-          ;; so that the loop node appears in the js2-loop-set, allowing
-          ;; break/continue statements to find the enclosing loop.
-          (setf body (js2-parse-statement)
-                (js2-loop-node-body pn) body
-                (js2-node-pos pn) for-pos
-                (js2-node-len pn) (- (js2-node-end body) for-pos))
-          (js2-node-add-children pn init cond incr body))
-      ;; finally
-      (js2-exit-loop))
-    pn))
-
-(defun js2-parse-try ()
-  "Parse a try statement.  Last matched token must be js2-TRY."
-  (let ((try-pos (js2-current-token-beg))
-        try-end
-        try-block
-        catch-blocks
-        finally-block
-        saw-default-catch
-        peek)
-    (if (/= (js2-peek-token) js2-LC)
-        (js2-report-error "msg.no.brace.try"))
-    (setq try-block (js2-parse-statement)
-          try-end (js2-node-end try-block)
-          peek (js2-peek-token))
-    (cond
-     ((= peek js2-CATCH)
-      (while (js2-match-token js2-CATCH)
-        (let* ((catch-pos (js2-current-token-beg))
-               (catch-node (make-js2-catch-node :pos catch-pos))
-               param
-               guard-kwd
-               catch-cond
-               lp rp)
-          (if saw-default-catch
-              (js2-report-error "msg.catch.unreachable"))
-          (if (js2-must-match js2-LP "msg.no.paren.catch")
-              (setq lp (- (js2-current-token-beg) catch-pos)))
-          (js2-push-scope catch-node)
-          (let ((tt (js2-peek-token)))
-            (cond
-             ;; Destructuring pattern:
-             ;;     catch ({ message, file }) { ... }
-             ((or (= tt js2-LB) (= tt js2-LC))
-              (js2-get-token)
-              (setq param (js2-parse-destruct-primary-expr))
-              (js2-define-destruct-symbols param js2-LET nil))
-             ;; Simple name.
-             (t
-              (js2-must-match-name "msg.bad.catchcond")
-              (setq param (js2-create-name-node))
-              (js2-define-symbol js2-LET (js2-current-token-string) param)
-              (js2-check-strict-identifier param))))
-          ;; Catch condition.
-          (if (js2-match-token js2-IF)
-              (setq guard-kwd (- (js2-current-token-beg) catch-pos)
-                    catch-cond (js2-parse-expr))
-            (setq saw-default-catch t))
-          (if (js2-must-match js2-RP "msg.bad.catchcond")
-              (setq rp (- (js2-current-token-beg) catch-pos)))
-          (js2-must-match js2-LC "msg.no.brace.catchblock")
-          (js2-parse-statements catch-node)
-          (if (js2-must-match js2-RC "msg.no.brace.after.body")
-              (setq try-end (js2-current-token-end)))
-          (js2-pop-scope)
-          (setf (js2-node-len catch-node) (- try-end catch-pos)
-                (js2-catch-node-param catch-node) param
-                (js2-catch-node-guard-expr catch-node) catch-cond
-                (js2-catch-node-guard-kwd catch-node) guard-kwd
-                (js2-catch-node-lp catch-node) lp
-                (js2-catch-node-rp catch-node) rp)
-          (js2-node-add-children catch-node param catch-cond)
-          (push catch-node catch-blocks))))
-     ((/= peek js2-FINALLY)
-      (js2-must-match js2-FINALLY "msg.try.no.catchfinally"
-                      (js2-node-pos try-block)
-                      (- (setq try-end (js2-node-end try-block))
-                         (js2-node-pos try-block)))))
-    (when (js2-match-token js2-FINALLY)
-      (let ((finally-pos (js2-current-token-beg))
-            (block (js2-parse-statement)))
-        (setq try-end (js2-node-end block)
-              finally-block (make-js2-finally-node :pos finally-pos
-                                                   :len (- try-end finally-pos)
-                                                   :body block))
-        (js2-node-add-children finally-block block)))
-    (let ((pn (make-js2-try-node :pos try-pos
-                                 :len (- try-end try-pos)
-                                 :try-block try-block
-                                 :finally-block finally-block)))
-      (js2-node-add-children pn try-block finally-block)
-      ;; Push them onto the try-node, which reverses and corrects their order.
-      (dolist (cb catch-blocks)
-        (js2-node-add-children pn cb)
-        (push cb (js2-try-node-catch-clauses pn)))
-      pn)))
-
-(defun js2-parse-throw ()
-  "Parser for throw-statement.  Last matched token must be js2-THROW."
-  (let ((pos (js2-current-token-beg))
-        expr pn)
-    (if (= (js2-peek-token-or-eol) js2-EOL)
-        ;; ECMAScript does not allow new lines before throw expression,
-        ;; see bug 256617
-        (js2-report-error "msg.bad.throw.eol"))
-    (setq expr (js2-parse-expr)
-          pn (make-js2-throw-node :pos pos
-                                  :len (- (js2-node-end expr) pos)
-                                  :expr expr))
-    (js2-node-add-children pn expr)
-    pn))
-
-(defun js2-match-jump-label-name (label-name)
-  "If break/continue specified a label, return that label's labeled stmt.
-Returns the corresponding `js2-labeled-stmt-node', or if LABEL-NAME
-does not match an existing label, reports an error and returns nil."
-  (let ((bundle (cdr (assoc label-name js2-label-set))))
-    (if (null bundle)
-        (js2-report-error "msg.undef.label"))
-    bundle))
-
-(defun js2-parse-break ()
-  "Parser for break-statement.  Last matched token must be js2-BREAK."
-  (let ((pos (js2-current-token-beg))
-        (end (js2-current-token-end))
-        break-target ; statement to break from
-        break-label  ; in "break foo", name-node representing the foo
-        labels       ; matching labeled statement to break to
-        pn)
-    (when (eq (js2-peek-token-or-eol) js2-NAME)
-      (js2-get-token)
-      (setq break-label (js2-create-name-node)
-            end (js2-node-end break-label)
-            ;; matchJumpLabelName only matches if there is one
-            labels (js2-match-jump-label-name (js2-current-token-string))
-            break-target (if labels (car (js2-labeled-stmt-node-labels labels)))))
-    (unless (or break-target break-label)
-      ;; no break target specified - try for innermost enclosing loop/switch
-      (if (null js2-loop-and-switch-set)
-          (unless break-label
-            (js2-report-error "msg.bad.break" nil pos (length "break")))
-        (setq break-target (car js2-loop-and-switch-set))))
-    (setq pn (make-js2-break-node :pos pos
-                                  :len (- end pos)
-                                  :label break-label
-                                  :target break-target))
-    (js2-node-add-children pn break-label)  ; but not break-target
-    pn))
-
-(defun js2-parse-continue ()
-  "Parser for continue-statement.  Last matched token must be js2-CONTINUE."
-  (let ((pos (js2-current-token-beg))
-        (end (js2-current-token-end))
-        label   ; optional user-specified label, a `js2-name-node'
-        labels  ; current matching labeled stmt, if any
-        target  ; the `js2-loop-node' target of this continue stmt
-        pn)
-    (when (= (js2-peek-token-or-eol) js2-NAME)
-      (js2-get-token)
-      (setq label (js2-create-name-node)
-            end (js2-node-end label)
-            ;; matchJumpLabelName only matches if there is one
-            labels (js2-match-jump-label-name (js2-current-token-string))))
-    (cond
-     ((null labels)  ; no current label to go to
-      (if (null js2-loop-set)  ; no loop to continue to
-          (js2-report-error "msg.continue.outside" nil pos
-                            (length "continue"))
-        (setq target (car js2-loop-set))))  ; innermost enclosing loop
-     (t
-      (if (js2-loop-node-p (js2-labeled-stmt-node-stmt labels))
-          (setq target (js2-labeled-stmt-node-stmt labels))
-        (js2-report-error "msg.continue.nonloop" nil pos (- end pos)))))
-    (setq pn (make-js2-continue-node :pos pos
-                                     :len (- end pos)
-                                     :label label
-                                     :target target))
-    (js2-node-add-children pn label)  ; but not target - it's not our child
-    pn))
-
-(defun js2-parse-with ()
-  "Parser for with-statement.  Last matched token must be js2-WITH."
-  (when js2-in-use-strict-directive
-    (js2-report-error "msg.no.with.strict"))
-  (let ((pos (js2-current-token-beg))
-        obj body pn lp rp)
-    (if (js2-must-match js2-LP "msg.no.paren.with")
-        (setq lp (js2-current-token-beg)))
-    (setq obj (js2-parse-expr))
-    (if (js2-must-match js2-RP "msg.no.paren.after.with")
-        (setq rp (js2-current-token-beg)))
-    (let ((js2-nesting-of-with (1+ js2-nesting-of-with)))
-        (setq body (js2-parse-statement)))
-    (setq pn (make-js2-with-node :pos pos
-                                 :len (- (js2-node-end body) pos)
-                                 :object obj
-                                 :body body
-                                 :lp (js2-relpos lp pos)
-                                 :rp (js2-relpos rp pos)))
-    (js2-node-add-children pn obj body)
-    pn))
-
-(defun js2-parse-const-var ()
-  "Parser for var- or const-statement.
-Last matched token must be js2-CONST or js2-VAR."
-  (let ((tt (js2-current-token-type))
-        (pos (js2-current-token-beg))
-        expr pn)
-    (setq expr (js2-parse-variables tt (js2-current-token-beg))
-          pn (make-js2-expr-stmt-node :pos pos
-                                      :len (- (js2-node-end expr) pos)
-                                      :expr expr))
-    (js2-node-add-children pn expr)
-    pn))
-
-(defun js2-wrap-with-expr-stmt (pos expr &optional add-child)
-  (let ((pn (make-js2-expr-stmt-node :pos pos
-                                     :len (js2-node-len expr)
-                                     :type (if (js2-inside-function)
-                                               js2-EXPR_VOID
-                                             js2-EXPR_RESULT)
-                                     :expr expr)))
-    (if add-child
-        (js2-node-add-children pn expr))
-    pn))
-
-(defun js2-parse-let-stmt ()
-  "Parser for let-statement.  Last matched token must be js2-LET."
-  (let ((pos (js2-current-token-beg))
-        expr pn)
-    (if (= (js2-peek-token) js2-LP)
-        ;; let expression in statement context
-        (setq expr (js2-parse-let pos 'statement)
-              pn (js2-wrap-with-expr-stmt pos expr t))
-      ;; else we're looking at a statement like let x=6, y=7;
-      (setf expr (js2-parse-variables js2-LET pos)
-            pn (js2-wrap-with-expr-stmt pos expr t)
-            (js2-node-type pn) js2-EXPR_RESULT))
-    pn))
-
-(defun js2-parse-ret-yield ()
-  (js2-parse-return-or-yield (js2-current-token-type) nil))
-
-(defconst js2-parse-return-stmt-enders
-  (list js2-SEMI js2-RC js2-EOF js2-EOL js2-ERROR js2-RB js2-RP))
-
-(defsubst js2-now-all-set (before after mask)
-  "Return whether or not the bits in the mask have changed to all set.
-BEFORE is bits before change, AFTER is bits after change, and MASK is
-the mask for bits.  Returns t if all the bits in the mask are set in AFTER
-but not BEFORE."
-  (and (/= (logand before mask) mask)
-       (= (logand after mask) mask)))
-
-(defun js2-parse-return-or-yield (tt expr-context)
-  (let* ((pos (js2-current-token-beg))
-         (end (js2-current-token-end))
-         (before js2-end-flags)
-         (inside-function (js2-inside-function))
-         (gen-type (and inside-function (js2-function-node-generator-type
-                                         js2-current-script-or-fn)))
-         e ret name yield-star-p)
-    (unless inside-function
-      (js2-report-error (if (eq tt js2-RETURN)
-                            "msg.bad.return"
-                          "msg.bad.yield")))
-    (when (and inside-function
-               (eq gen-type 'STAR)
-               (js2-match-token js2-MUL))
-      (setq yield-star-p t))
-    ;; This is ugly, but we don't want to require a semicolon.
-    (unless (memq (js2-peek-token-or-eol) js2-parse-return-stmt-enders)
-      (setq e (if (eq gen-type 'STAR)
-                  (js2-parse-assign-expr)
-                (js2-parse-expr))
-            end (js2-node-end e)))
-    (cond
-     ((eq tt js2-RETURN)
-      (js2-set-flag js2-end-flags (if (null e)
-                                      js2-end-returns
-                                    js2-end-returns-value))
-      (setq ret (make-js2-return-node :pos pos
-                                      :len (- end pos)
-                                      :retval e))
-      (js2-node-add-children ret e)
-      ;; See if we need a strict mode warning.
-      ;; TODO:  The analysis done by `js2-has-consistent-return-usage' is
-      ;; more thorough and accurate than this before/after flag check.
-      ;; E.g. if there's a finally-block that always returns, we shouldn't
-      ;; show a warning generated by inconsistent returns in the catch blocks.
-      ;; Basically `js2-has-consistent-return-usage' needs to keep more state,
-      ;; so we know which returns/yields to highlight, and we should get rid of
-      ;; all the checking in `js2-parse-return-or-yield'.
-      (if (and js2-strict-inconsistent-return-warning
-               (js2-now-all-set before js2-end-flags
-                                (logior js2-end-returns js2-end-returns-value)))
-          (js2-add-strict-warning "msg.return.inconsistent" nil pos end)))
-     ((eq gen-type 'COMPREHENSION)
-      ;; FIXME: We should probably switch to saving and using lastYieldOffset,
-      ;; like SpiderMonkey does.
-      (js2-report-error "msg.syntax" nil pos 5))
-     (t
-      (setq ret (make-js2-yield-node :pos pos
-                                     :len (- end pos)
-                                     :value e
-                                     :star-p yield-star-p))
-      (js2-node-add-children ret e)
-      (unless expr-context
-        (setq e ret
-              ret (js2-wrap-with-expr-stmt pos e t))
-      (js2-set-requires-activation)
-      (js2-set-is-generator))))
-    ;; see if we are mixing yields and value returns.
-    (when (and inside-function
-               (js2-flag-set-p js2-end-flags js2-end-returns-value)
-               (eq (js2-function-node-generator-type js2-current-script-or-fn)
-                   'LEGACY))
-      (setq name (js2-function-name js2-current-script-or-fn))
-      (if (zerop (length name))
-          (js2-report-error "msg.anon.generator.returns" nil pos (- end pos))
-        (js2-report-error "msg.generator.returns" name pos (- end pos))))
-    ret))
-
-(defun js2-parse-debugger ()
-  (make-js2-keyword-node :type js2-DEBUGGER))
-
-(defun js2-parse-block ()
-  "Parser for a curly-delimited statement block.
-Last token matched must be `js2-LC'."
-  (let ((pos (js2-current-token-beg))
-        (pn (make-js2-scope)))
-    (js2-push-scope pn)
-    (unwind-protect
-        (progn
-          (js2-parse-statements pn)
-          (js2-must-match js2-RC "msg.no.brace.block")
-          (setf (js2-node-len pn) (- (js2-current-token-end) pos)))
-      (js2-pop-scope))
-    pn))
-
-;; For `js2-ERROR' too, to have a node for error recovery to work on.
-(defun js2-parse-semi ()
-  "Parse a statement or handle an error.
-Current token type is `js2-SEMI' or `js2-ERROR'."
-  (let ((tt (js2-current-token-type)) pos len)
-    (if (eq tt js2-SEMI)
-        (make-js2-empty-expr-node :len 1)
-      (setq pos (js2-current-token-beg)
-            len (- (js2-current-token-end) pos))
-      (js2-report-error "msg.syntax" nil pos len)
-      (make-js2-error-node :pos pos :len len))))
-
-(defun js2-parse-default-xml-namespace ()
-  "Parse a `default xml namespace = <expr>' e4x statement."
-  (let ((pos (js2-current-token-beg))
-        end len expr unary)
-    (js2-must-have-xml)
-    (js2-set-requires-activation)
-    (setq len (- js2-ts-cursor pos))
-    (unless (and (js2-match-token js2-NAME)
-                 (string= (js2-current-token-string) "xml"))
-      (js2-report-error "msg.bad.namespace" nil pos len))
-    (unless (and (js2-match-token js2-NAME)
-                 (string= (js2-current-token-string) "namespace"))
-      (js2-report-error "msg.bad.namespace" nil pos len))
-    (unless (js2-match-token js2-ASSIGN)
-      (js2-report-error "msg.bad.namespace" nil pos len))
-    (setq expr (js2-parse-expr)
-          end (js2-node-end expr)
-          unary (make-js2-unary-node :type js2-DEFAULTNAMESPACE
-                                     :pos pos
-                                     :len (- end pos)
-                                     :operand expr))
-    (js2-node-add-children unary expr)
-    (make-js2-expr-stmt-node :pos pos
-                             :len (- end pos)
-                             :expr unary)))
-
-(defun js2-record-label (label bundle)
-  ;; current token should be colon that `js2-parse-primary-expr' left untouched
-  (js2-get-token)
-  (let ((name (js2-label-node-name label))
-        labeled-stmt
-        dup)
-    (when (setq labeled-stmt (cdr (assoc name js2-label-set)))
-      ;; flag both labels if possible when used in editing mode
-      (if (and js2-parse-ide-mode
-               (setq dup (js2-get-label-by-name labeled-stmt name)))
-          (js2-report-error "msg.dup.label" nil
-                            (js2-node-abs-pos dup) (js2-node-len dup)))
-      (js2-report-error "msg.dup.label" nil
-                        (js2-node-pos label) (js2-node-len label)))
-    (js2-labeled-stmt-node-add-label bundle label)
-    (js2-node-add-children bundle label)
-    ;; Add one reference to the bundle per label in `js2-label-set'
-    (push (cons name bundle) js2-label-set)))
-
-(defun js2-parse-name-or-label ()
-  "Parser for identifier or label.  Last token matched must be js2-NAME.
-Called when we found a name in a statement context.  If it's a label, we gather
-up any following labels and the next non-label statement into a
-`js2-labeled-stmt-node' bundle and return that.  Otherwise we parse an
-expression and return it wrapped in a `js2-expr-stmt-node'."
-  (let ((pos (js2-current-token-beg))
-        expr stmt bundle
-        (continue t))
-    ;; set check for label and call down to `js2-parse-primary-expr'
-    (setq expr (js2-maybe-parse-label))
-    (if (null expr)
-        ;; Parse the non-label expression and wrap with expression stmt.
-        (js2-wrap-with-expr-stmt pos (js2-parse-expr) t)
-      ;; else parsed a label
-      (setq bundle (make-js2-labeled-stmt-node :pos pos))
-      (js2-record-label expr bundle)
-      ;; look for more labels
-      (while (and continue (= (js2-get-token) js2-NAME))
-        (if (setq expr (js2-maybe-parse-label))
-            (js2-record-label expr bundle)
-          (setq expr (js2-parse-expr)
-                stmt (js2-wrap-with-expr-stmt (js2-node-pos expr) expr t)
-                continue nil)
-          (js2-auto-insert-semicolon stmt)))
-      ;; no more labels; now parse the labeled statement
-      (unwind-protect
-            (unless stmt
-              (let ((js2-labeled-stmt bundle))  ; bind dynamically
-                (js2-unget-token)
-                (setq stmt (js2-statement-helper))))
-        ;; remove the labels for this statement from the global set
-        (dolist (label (js2-labeled-stmt-node-labels bundle))
-          (setq js2-label-set (remove label js2-label-set))))
-      (setf (js2-labeled-stmt-node-stmt bundle) stmt
-            (js2-node-len bundle) (- (js2-node-end stmt) pos))
-      (js2-node-add-children bundle stmt)
-      bundle)))
-
-(defun js2-maybe-parse-label ()
-  (cl-assert (= (js2-current-token-type) js2-NAME))
-  (let (label-pos
-        (next-tt (js2-get-token))
-        (label-end (js2-current-token-end)))
-    ;; Do not consume colon, it is used as unwind indicator
-    ;; to return to statementHelper.
-    (js2-unget-token)
-    (if (= next-tt js2-COLON)
-        (prog2
-            (setq label-pos (js2-current-token-beg))
-            (make-js2-label-node :pos label-pos
-                                 :len (- label-end label-pos)
-                                 :name (js2-current-token-string))
-          (js2-set-face label-pos
-                        label-end
-                        'font-lock-variable-name-face 'record))
-      ;; Backtrack from the name token, too.
-      (js2-unget-token)
-      nil)))
-
-(defun js2-parse-expr-stmt ()
-  "Default parser in statement context, if no recognized statement found."
-  (js2-wrap-with-expr-stmt (js2-current-token-beg)
-                           (progn
-                             (js2-unget-token)
-                             (js2-parse-expr)) t))
-
-(defun js2-parse-variables (decl-type pos)
-  "Parse a comma-separated list of variable declarations.
-Could be a 'var', 'const' or 'let' expression, possibly in a for-loop initializer.
-
-DECL-TYPE is a token value: either VAR, CONST, or LET depending on context.
-For 'var' or 'const', the keyword should be the token last scanned.
-
-POS is the position where the node should start. It's sometimes the
-var/const/let keyword, and other times the beginning of the first token
-in the first variable declaration.
-
-Returns the parsed `js2-var-decl-node' expression node."
-  (let* ((result (make-js2-var-decl-node :decl-type decl-type
-                                         :pos pos))
-         destructuring kid-pos tt init name end nbeg nend vi
-         (continue t))
-    ;; Example:
-    ;; var foo = {a: 1, b: 2}, bar = [3, 4];
-    ;; var {b: s2, a: s1} = foo, x = 6, y, [s3, s4] = bar;
-    ;; var {a, b} = baz;
-    (while continue
-      (setq destructuring nil
-            name nil
-            tt (js2-get-token)
-            kid-pos (js2-current-token-beg)
-            end (js2-current-token-end)
-            init nil)
-      (if (or (= tt js2-LB) (= tt js2-LC))
-          ;; Destructuring assignment, e.g., var [a, b] = ...
-          (setq destructuring (js2-parse-destruct-primary-expr)
-                end (js2-node-end destructuring))
-        ;; Simple variable name
-        (js2-unget-token)
-        (when (js2-must-match-name "msg.bad.var")
-          (setq name (js2-create-name-node)
-                nbeg (js2-current-token-beg)
-                nend (js2-current-token-end)
-                end nend)
-          (js2-define-symbol decl-type (js2-current-token-string) name js2-in-for-init)
-          (js2-check-strict-identifier name)))
-      (when (js2-match-token js2-ASSIGN)
-        (setq init (js2-parse-assign-expr)
-              end (js2-node-end init))
-        (js2-record-imenu-functions init name))
-      (when name
-        (js2-set-face nbeg nend (if (js2-function-node-p init)
-                                    'font-lock-function-name-face
-                                  'font-lock-variable-name-face)
-                      'record))
-      (setq vi (make-js2-var-init-node :pos kid-pos
-                                       :len (- end kid-pos)
-                                       :type decl-type))
-      (if destructuring
-          (progn
-            (if (and (null init) (not js2-in-for-init))
-                (js2-report-error "msg.destruct.assign.no.init"))
-            (js2-define-destruct-symbols destructuring
-                                         decl-type
-                                         'font-lock-variable-name-face)
-            (setf (js2-var-init-node-target vi) destructuring))
-        (setf (js2-var-init-node-target vi) name))
-      (setf (js2-var-init-node-initializer vi) init)
-      (js2-node-add-children vi name destructuring init)
-      (js2-block-node-push result vi)
-      (unless (js2-match-token js2-COMMA)
-        (setq continue nil)))
-    (setf (js2-node-len result) (- end pos))
-    result))
-
-(defun js2-parse-let (pos &optional stmt-p)
-  "Parse a let expression or statement.
-A let-expression is of the form `let (vars) expr'.
-A let-statement is of the form `let (vars) {statements}'.
-The third form of let is a variable declaration list, handled
-by `js2-parse-variables'."
-  (let ((pn (make-js2-let-node :pos pos))
-        beg vars body)
-    (if (js2-must-match js2-LP "msg.no.paren.after.let")
-        (setf (js2-let-node-lp pn) (- (js2-current-token-beg) pos)))
-    (js2-push-scope pn)
-    (unwind-protect
-        (progn
-          (setq vars (js2-parse-variables js2-LET (js2-current-token-beg)))
-          (if (js2-must-match js2-RP "msg.no.paren.let")
-              (setf (js2-let-node-rp pn) (- (js2-current-token-beg) pos)))
-          (if (and stmt-p (js2-match-token js2-LC))
-              ;; let statement
-              (progn
-                (setf beg (js2-current-token-beg)  ; position stmt at LC
-                      body (js2-parse-statements))
-                (js2-must-match js2-RC "msg.no.curly.let")
-                (setf (js2-node-len body) (- (js2-current-token-end) beg)
-                      (js2-node-len pn) (- (js2-current-token-end) pos)
-                      (js2-let-node-body pn) body
-                      (js2-node-type pn) js2-LET))
-            ;; let expression
-            (setf body (js2-parse-expr)
-                  (js2-node-len pn) (- (js2-node-end body) pos)
-                  (js2-let-node-body pn) body))
-          (setf (js2-let-node-vars pn) vars)
-          (js2-node-add-children pn vars body))
-      (js2-pop-scope))
-    pn))
-
-(defun js2-define-new-symbol (decl-type name node &optional scope)
-  (js2-scope-put-symbol (or scope js2-current-scope)
-                        name
-                        (make-js2-symbol decl-type name node)))
-
-(defun js2-define-symbol (decl-type name &optional node ignore-not-in-block)
-  "Define a symbol in the current scope.
-If NODE is non-nil, it is the AST node associated with the symbol."
-  (let* ((defining-scope (js2-get-defining-scope js2-current-scope name))
-         (symbol (if defining-scope
-                     (js2-scope-get-symbol defining-scope name)))
-         (sdt (if symbol (js2-symbol-decl-type symbol) -1))
-         (pos (if node (js2-node-abs-pos node)))
-         (len (if node (js2-node-len node))))
-    (cond
-     ((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")
-        ((= sdt js2-LET) "msg.let.redecl")
-        ((= sdt js2-VAR) "msg.var.redecl")
-        ((= sdt js2-FUNCTION) "msg.function.redecl")
-        (t "msg.parm.redecl"))
-       name pos len))
-     ((or (= decl-type js2-LET)
-          (= decl-type js2-CONST))
-      (if (and (= decl-type js2-LET)
-               (not ignore-not-in-block)
-               (or (= (js2-node-type js2-current-scope) js2-IF)
-                   (js2-loop-node-p js2-current-scope)))
-          (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))
-      (if symbol
-          (if (and js2-strict-var-redeclaration-warning (= sdt js2-VAR))
-              (js2-add-strict-warning "msg.var.redecl" name)
-            (if (and js2-strict-var-hides-function-arg-warning (= sdt js2-LP))
-                (js2-add-strict-warning "msg.var.hides.arg" name)))
-        (js2-define-new-symbol decl-type name node
-                               js2-current-script-or-fn)))
-     ((= decl-type js2-LP)
-      (if symbol
-          ;; must be duplicate parameter. Second parameter hides the
-          ;; first, so go ahead and add the second pararameter
-          (js2-report-warning "msg.dup.parms" name))
-      (js2-define-new-symbol decl-type name node))
-     (t (js2-code-bug)))))
-
-(defun js2-parse-paren-expr-or-generator-comp ()
-  (let ((px-pos (js2-current-token-beg)))
-    (cond
-     ((and (>= js2-language-version 200)
-           (js2-match-token js2-FOR))
-      (js2-parse-generator-comp px-pos))
-     ((and (>= js2-language-version 200)
-           (js2-match-token js2-RP))
-      ;; Not valid expression syntax, but this is valid in an arrow
-      ;; function with no params: () => body.
-      (if (eq (js2-peek-token) js2-ARROW)
-          ;; Return whatever, it will hopefully be rewinded and
-          ;; reparsed when we reach the =>.
-          (make-js2-keyword-node :type js2-NULL)
-        (js2-report-error "msg.syntax")
-        (make-js2-error-node)))
-     (t
-      (let* ((js2-in-for-init nil)
-             (expr (js2-parse-expr))
-             (pn (make-js2-paren-node :pos px-pos
-                                      :expr expr)))
-        (js2-node-add-children pn (js2-paren-node-expr pn))
-        (js2-must-match js2-RP "msg.no.paren")
-        (setf (js2-node-len pn) (- (js2-current-token-end) px-pos))
-        pn)))))
-
-(defun js2-parse-expr (&optional oneshot)
-  (let* ((pn (js2-parse-assign-expr))
-         (pos (js2-node-pos pn))
-         left
-         right
-         op-pos)
-    (while (and (not oneshot)
-                (js2-match-token js2-COMMA))
-      (setq op-pos (- (js2-current-token-beg) pos))  ; relative
-      (setq right (js2-parse-assign-expr)
-            left pn
-            pn (make-js2-infix-node :type js2-COMMA
-                                    :pos pos
-                                    :len (- js2-ts-cursor pos)
-                                    :op-pos op-pos
-                                    :left left
-                                    :right right))
-      (js2-node-add-children pn left right))
-    pn))
-
-(defun js2-parse-assign-expr ()
-  (let ((tt (js2-get-token))
-        (pos (js2-current-token-beg))
-        pn left right op-pos
-        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)
-            recorded-identifiers js2-recorded-identifiers
-            parsed-errors js2-parsed-errors)
-      ;; not yield - parse assignment expression
-      (setq pn (js2-parse-cond-expr)
-            tt (js2-get-token))
-      (cond
-       ((and (<= js2-first-assign tt)
-             (<= tt js2-last-assign))
-        ;; tt express assignment (=, |=, ^=, ..., %=)
-        (setq op-pos (- (js2-current-token-beg) pos)  ; relative
-              left pn)
-        ;; The assigned node could be a js2-prop-get-node (foo.bar = 0), we only
-        ;; care about assignment to strict variable names.
-        (when (js2-name-node-p left)
-          (js2-check-strict-identifier left))
-        (setq right (js2-parse-assign-expr)
-              pn (make-js2-assign-node :type tt
-                                       :pos pos
-                                       :len (- (js2-node-end right) pos)
-                                       :op-pos op-pos
-                                       :left left
-                                       :right right))
-        (when js2-parse-ide-mode
-          (js2-highlight-assign-targets pn left right)
-          (js2-record-imenu-functions right left))
-        ;; do this last so ide checks above can use absolute positions
-        (js2-node-add-children pn left right))
-       ((and (>= js2-language-version 200)
-             (or
-              (= tt js2-ARROW)
-              (and async-p
-                   (= (js2-peek-token) js2-ARROW))))
-        (js2-ts-seek ts-state)
-        (when async-p
-          (js2-record-face 'font-lock-keyword-face)
-          (js2-get-token))
-        (setq js2-recorded-identifiers recorded-identifiers
-              js2-parsed-errors parsed-errors)
-        (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) nil async-p)))
-       (t
-        (js2-unget-token)))
-      pn)))
-
-(defun js2-parse-cond-expr ()
-  (let ((pos (js2-current-token-beg))
-        (pn (js2-parse-or-expr))
-        test-expr
-        if-true
-        if-false
-        q-pos
-        c-pos)
-    (when (js2-match-token js2-HOOK)
-      (setq q-pos (- (js2-current-token-beg) pos)
-            if-true (let (js2-in-for-init) (js2-parse-assign-expr)))
-      (js2-must-match js2-COLON "msg.no.colon.cond")
-      (setq c-pos (- (js2-current-token-beg) pos)
-            if-false (js2-parse-assign-expr)
-            test-expr pn
-            pn (make-js2-cond-node :pos pos
-                                   :len (- (js2-node-end if-false) pos)
-                                   :test-expr test-expr
-                                   :true-expr if-true
-                                   :false-expr if-false
-                                   :q-pos q-pos
-                                   :c-pos c-pos))
-      (js2-node-add-children pn test-expr if-true if-false))
-    pn))
-
-(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.
-PARSER is a function to call to parse the right operand,
-or a `js2-node' struct if it has already been parsed.
-FIXME: The latter option is unused?"
-  (let* ((pos (js2-node-pos left))
-         (op-pos (- (js2-current-token-beg) pos))
-         (right (if (js2-node-p parser)
-                    parser
-                  (unless no-get (js2-get-token))
-                  (funcall parser)))
-         (pn (make-js2-infix-node :type type
-                                  :pos pos
-                                  :len (- (js2-node-end right) pos)
-                                  :op-pos op-pos
-                                  :left left
-                                  :right right)))
-    (js2-node-add-children pn left right)
-    pn))
-
-(defun js2-parse-or-expr ()
-  (let ((pn (js2-parse-and-expr)))
-    (when (js2-match-token js2-OR)
-      (setq pn (js2-make-binary js2-OR
-                                pn
-                                'js2-parse-or-expr)))
-    pn))
-
-(defun js2-parse-and-expr ()
-  (let ((pn (js2-parse-bit-or-expr)))
-    (when (js2-match-token js2-AND)
-      (setq pn (js2-make-binary js2-AND
-                                pn
-                                'js2-parse-and-expr)))
-    pn))
-
-(defun js2-parse-bit-or-expr ()
-  (let ((pn (js2-parse-bit-xor-expr)))
-    (while (js2-match-token js2-BITOR)
-      (setq pn (js2-make-binary js2-BITOR
-                                pn
-                                'js2-parse-bit-xor-expr)))
-    pn))
-
-(defun js2-parse-bit-xor-expr ()
-  (let ((pn (js2-parse-bit-and-expr)))
-    (while (js2-match-token js2-BITXOR)
-      (setq pn (js2-make-binary js2-BITXOR
-                                pn
-                                'js2-parse-bit-and-expr)))
-    pn))
-
-(defun js2-parse-bit-and-expr ()
-  (let ((pn (js2-parse-eq-expr)))
-    (while (js2-match-token js2-BITAND)
-      (setq pn (js2-make-binary js2-BITAND
-                                pn
-                                'js2-parse-eq-expr)))
-    pn))
-
-(defconst js2-parse-eq-ops
-  (list js2-EQ js2-NE js2-SHEQ js2-SHNE))
-
-(defun js2-parse-eq-expr ()
-  (let ((pn (js2-parse-rel-expr))
-        tt)
-    (while (memq (setq tt (js2-get-token)) js2-parse-eq-ops)
-      (setq pn (js2-make-binary tt
-                                pn
-                                'js2-parse-rel-expr)))
-    (js2-unget-token)
-    pn))
-
-(defconst js2-parse-rel-ops
-  (list js2-IN js2-INSTANCEOF js2-LE js2-LT js2-GE js2-GT))
-
-(defun js2-parse-rel-expr ()
-  (let ((pn (js2-parse-shift-expr))
-        (continue t)
-        tt)
-    (while continue
-      (setq tt (js2-get-token))
-      (cond
-       ((and js2-in-for-init (= tt js2-IN))
-        (js2-unget-token)
-        (setq continue nil))
-       ((memq tt js2-parse-rel-ops)
-        (setq pn (js2-make-binary tt pn 'js2-parse-shift-expr)))
-       (t
-        (js2-unget-token)
-        (setq continue nil))))
-    pn))
-
-(defconst js2-parse-shift-ops
-  (list js2-LSH js2-URSH js2-RSH))
-
-(defun js2-parse-shift-expr ()
-  (let ((pn (js2-parse-add-expr))
-        tt
-        (continue t))
-    (while continue
-      (setq tt (js2-get-token))
-      (if (memq tt js2-parse-shift-ops)
-          (setq pn (js2-make-binary tt pn 'js2-parse-add-expr))
-        (js2-unget-token)
-        (setq continue nil)))
-    pn))
-
-(defun js2-parse-add-expr ()
-  (let ((pn (js2-parse-mul-expr))
-        tt
-        (continue t))
-    (while continue
-      (setq tt (js2-get-token))
-      (if (or (= tt js2-ADD) (= tt js2-SUB))
-          (setq pn (js2-make-binary tt pn 'js2-parse-mul-expr))
-        (js2-unget-token)
-        (setq continue nil)))
-    pn))
-
-(defconst js2-parse-mul-ops
-  (list js2-MUL js2-DIV js2-MOD))
-
-(defun js2-parse-mul-expr ()
-  (let ((pn (js2-parse-expon-expr))
-        tt
-        (continue t))
-    (while continue
-      (setq tt (js2-get-token))
-      (if (memq tt js2-parse-mul-ops)
-          (setq pn (js2-make-binary tt pn 'js2-parse-expon-expr))
-        (js2-unget-token)
-        (setq continue nil)))
-    pn))
-
-(defun js2-parse-expon-expr ()
-  (let ((pn (js2-parse-unary-expr)))
-    (when (>= js2-language-version 200)
-      (while (js2-match-token js2-EXPON)
-        (when (and (js2-unary-node-p pn)
-                   (not (memq (js2-node-type pn) '(js2-INC js2-DEC))))
-          (js2-report-error "msg.syntax" nil
-                            (js2-node-abs-pos pn) (js2-node-len pn)))
-        ;; Make it right-associative.
-        (setq pn (js2-make-binary js2-EXPON pn 'js2-parse-expon-expr))))
-    pn))
-
-(defun js2-make-unary (beg type parser &rest args)
-  "Make a unary node starting at BEG of type TYPE.
-If BEG is nil, `(js2-current-token-beg)' is used for the node
-start position.  PARSER is either a node (for postfix operators)
-or a function to call to parse the operand (for prefix
-operators)."
-  (let* ((pos (or beg (js2-current-token-beg)))
-         (postfix (js2-node-p parser))
-         (expr (if postfix
-                   parser
-                 (apply parser args)))
-         end
-         pn)
-    (if postfix  ; e.g. i++
-        (setq pos (js2-node-pos expr)
-              end (js2-current-token-end))
-      (setq end (js2-node-end expr)))
-    (setq pn (make-js2-unary-node :type type
-                                  :pos pos
-                                  :len (- end pos)
-                                  :operand expr))
-    (js2-node-add-children pn expr)
-    pn))
-
-(defconst js2-incrementable-node-types
-  (list js2-NAME js2-GETPROP js2-GETELEM js2-GET_REF js2-CALL)
-  "Node types that can be the operand of a ++ or -- operator.")
-
-(defun js2-check-bad-inc-dec (tt beg end unary)
-  (unless (memq (js2-node-type (js2-unary-node-operand unary))
-                js2-incrementable-node-types)
-    (js2-report-error (if (= tt js2-INC)
-                          "msg.bad.incr"
-                        "msg.bad.decr")
-                      nil beg (- end beg))))
-
-(defun js2-parse-unary-expr ()
-  (let ((tt (js2-current-token-type))
-        (beg (js2-current-token-beg)))
-    (cond
-     ((or (= tt js2-VOID)
-          (= tt js2-NOT)
-          (= tt js2-BITNOT)
-          (= tt js2-TYPEOF))
-      (js2-get-token)
-      (js2-make-unary beg tt 'js2-parse-unary-expr))
-     ((= tt js2-ADD)
-      (js2-get-token)
-      ;; Convert to special POS token in decompiler and parse tree
-      (js2-make-unary beg js2-POS 'js2-parse-unary-expr))
-     ((= tt js2-SUB)
-      (js2-get-token)
-      ;; Convert to special NEG token in decompiler and parse tree
-      (js2-make-unary beg js2-NEG 'js2-parse-unary-expr))
-     ((or (= tt js2-INC)
-          (= tt js2-DEC))
-      (js2-get-token)
-      (let ((beg2 (js2-current-token-beg))
-            (end (js2-current-token-end))
-            (expr (js2-make-unary beg tt 'js2-parse-member-expr t)))
-        (js2-check-bad-inc-dec tt beg2 end expr)
-        expr))
-     ((= tt js2-DELPROP)
-      (js2-get-token)
-      (js2-make-unary beg js2-DELPROP 'js2-parse-unary-expr))
-     ((js2-parse-await-maybe tt))
-     ((= tt js2-ERROR)
-      (js2-get-token)
-      (make-js2-error-node))  ; try to continue
-     ((and (= tt js2-LT)
-           js2-compiler-xml-available)
-      ;; XML stream encountered in expression.
-      (js2-parse-member-expr-tail t (js2-parse-xml-initializer)))
-     (t
-      (let ((pn (js2-parse-member-expr t))
-            ;; Don't look across a newline boundary for a postfix incop.
-            (tt (js2-peek-token-or-eol))
-            expr)
-        (when (or (= tt js2-INC) (= tt js2-DEC))
-          (js2-get-token)
-          (setf expr pn
-                pn (js2-make-unary (js2-node-pos expr) tt expr))
-          (js2-node-set-prop pn 'postfix t)
-          (js2-check-bad-inc-dec tt (js2-current-token-beg) (js2-current-token-end) pn))
-        pn)))))
-
-(defun js2-parse-xml-initializer ()
-  "Parse an E4X XML initializer.
-I'm parsing it the way Rhino parses it, but without the tree-rewriting.
-Then I'll postprocess the result, depending on whether we're in IDE
-mode or codegen mode, and generate the appropriate rewritten AST.
-IDE mode uses a rich AST that models the XML structure.  Codegen mode
-just concatenates everything and makes a new XML or XMLList out of it."
-  (let ((tt (js2-get-first-xml-token))
-        pn-xml pn expr kids expr-pos
-        (continue t)
-        (first-token t))
-    (when (not (or (= tt js2-XML) (= tt js2-XMLEND)))
-      (js2-report-error "msg.syntax"))
-    (setq pn-xml (make-js2-xml-node))
-    (while continue
-      (if first-token
-          (setq first-token nil)
-        (setq tt (js2-get-next-xml-token)))
-      (cond
-       ;; js2-XML means we found a {expr} in the XML stream.
-       ;; The token string is the XML up to the left-curly.
-       ((= tt js2-XML)
-        (push (make-js2-string-node :pos (js2-current-token-beg)
-                                    :len (- js2-ts-cursor (js2-current-token-beg)))
-              kids)
-        (js2-must-match js2-LC "msg.syntax")
-        (setq expr-pos js2-ts-cursor
-              expr (if (eq (js2-peek-token) js2-RC)
-                       (make-js2-empty-expr-node :pos expr-pos)
-                     (js2-parse-expr)))
-        (js2-must-match js2-RC "msg.syntax")
-        (setq pn (make-js2-xml-js-expr-node :pos (js2-node-pos expr)
-                                            :len (js2-node-len expr)
-                                            :expr expr))
-        (js2-node-add-children pn expr)
-        (push pn kids))
-       ;; a js2-XMLEND token means we hit the final close-tag.
-       ((= tt js2-XMLEND)
-        (push (make-js2-string-node :pos (js2-current-token-beg)
-                                    :len (- js2-ts-cursor (js2-current-token-beg)))
-              kids)
-        (dolist (kid (nreverse kids))
-          (js2-block-node-push pn-xml kid))
-        (setf (js2-node-len pn-xml) (- js2-ts-cursor
-                                       (js2-node-pos pn-xml))
-              continue nil))
-       (t
-        (js2-report-error "msg.syntax")
-        (setq continue nil))))
-    pn-xml))
-
-
-(defun js2-parse-argument-list ()
-  "Parse an argument list and return it as a Lisp list of nodes.
-Returns the list in reverse order.  Consumes the right-paren token."
-  (let (result)
-    (unless (js2-match-token js2-RP)
-      (cl-loop do
-               (let ((tt (js2-get-token))
-                     (beg (js2-current-token-beg)))
-                 (if (and (= tt js2-TRIPLEDOT)
-                          (>= js2-language-version 200))
-                     (push (js2-make-unary beg tt 'js2-parse-assign-expr) result)
-                   (js2-unget-token)
-                   (push (js2-parse-assign-expr) result)))
-               while
-               (and (js2-match-token js2-COMMA)
-                    (or (< js2-language-version 200)
-                        (not (= js2-RP (js2-peek-token))))))
-      (js2-must-match js2-RP "msg.no.paren.arg")
-      result)))
-
-(defun js2-parse-member-expr (&optional allow-call-syntax)
-  (let ((tt (js2-current-token-type))
-        pn pos target args beg end init)
-    (if (/= tt js2-NEW)
-        (setq pn (js2-parse-primary-expr))
-      ;; parse a 'new' expression
-      (js2-get-token)
-      (setq pos (js2-current-token-beg)
-            beg pos
-            target (js2-parse-member-expr)
-            end (js2-node-end target)
-            pn (make-js2-new-node :pos pos
-                                  :target target
-                                  :len (- end pos)))
-      (js2-highlight-function-call (js2-current-token))
-      (js2-node-add-children pn target)
-      (when (js2-match-token js2-LP)
-        ;; Add the arguments to pn, if any are supplied.
-        (setf beg pos  ; start of "new" keyword
-              pos (js2-current-token-beg)
-              args (nreverse (js2-parse-argument-list))
-              (js2-new-node-args pn) args
-              end (js2-current-token-end)
-              (js2-new-node-lp pn) (- pos beg)
-              (js2-new-node-rp pn) (- end 1 beg))
-        (apply #'js2-node-add-children pn args))
-      (when (and js2-allow-rhino-new-expr-initializer
-                 (js2-match-token js2-LC))
-        (setf init (js2-parse-object-literal)
-              end (js2-node-end init)
-              (js2-new-node-initializer pn) init)
-        (js2-node-add-children pn init))
-        (setf (js2-node-len pn) (- end beg)))  ; end outer if
-    (js2-parse-member-expr-tail allow-call-syntax pn)))
-
-(defun js2-parse-member-expr-tail (allow-call-syntax pn)
-  "Parse a chain of property/array accesses or function calls.
-Includes parsing for E4X operators like `..' and `.@'.
-If ALLOW-CALL-SYNTAX is nil, stops when we encounter a left-paren.
-Returns an expression tree that includes PN, the parent node."
-  (let (tt
-        (continue t))
-    (while continue
-      (setq tt (js2-get-token))
-      (cond
-       ((or (= tt js2-DOT) (= tt js2-DOTDOT))
-        (setq pn (js2-parse-property-access tt pn)))
-       ((= tt js2-DOTQUERY)
-        (setq pn (js2-parse-dot-query pn)))
-       ((= tt js2-LB)
-        (setq pn (js2-parse-element-get pn)))
-       ((= tt js2-LP)
-        (js2-unget-token)
-        (if allow-call-syntax
-            (setq pn (js2-parse-function-call pn))
-          (setq continue nil)))
-       ((= tt js2-TEMPLATE_HEAD)
-        (setq pn (js2-parse-tagged-template pn (js2-parse-template-literal))))
-       ((= tt js2-NO_SUBS_TEMPLATE)
-        (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)))
-    pn))
-
-(defun js2-parse-tagged-template (tag-node tpl-node)
-  "Parse tagged template expression."
-  (let* ((pos (js2-node-pos tag-node))
-         (pn (make-js2-tagged-template-node :pos pos
-                                            :len (- (js2-current-token-end) pos)
-                                            :tag tag-node
-                                            :template tpl-node)))
-    (js2-node-add-children pn tag-node tpl-node)
-    pn))
-
-(defun js2-parse-dot-query (pn)
-  "Parse a dot-query expression, e.g. foo.bar.(@name == 2)
-Last token parsed must be `js2-DOTQUERY'."
-  (let ((pos (js2-node-pos pn))
-        op-pos expr end)
-    (js2-must-have-xml)
-    (js2-set-requires-activation)
-    (setq op-pos (js2-current-token-beg)
-          expr (js2-parse-expr)
-          end (js2-node-end expr)
-          pn (make-js2-xml-dot-query-node :left pn
-                                          :pos pos
-                                          :op-pos op-pos
-                                          :right expr))
-    (js2-node-add-children pn
-                           (js2-xml-dot-query-node-left pn)
-                           (js2-xml-dot-query-node-right pn))
-    (if (js2-must-match js2-RP "msg.no.paren")
-        (setf (js2-xml-dot-query-node-rp pn) (js2-current-token-beg)
-              end (js2-current-token-end)))
-    (setf (js2-node-len pn) (- end pos))
-    pn))
-
-(defun js2-parse-element-get (pn)
-  "Parse an element-get expression, e.g. foo[bar].
-Last token parsed must be `js2-RB'."
-  (let ((lb (js2-current-token-beg))
-        (pos (js2-node-pos pn))
-        rb expr)
-    (setq expr (js2-parse-expr))
-    (if (js2-must-match js2-RB "msg.no.bracket.index")
-        (setq rb (js2-current-token-beg)))
-    (setq pn (make-js2-elem-get-node :target pn
-                                     :pos pos
-                                     :element expr
-                                     :lb (js2-relpos lb pos)
-                                     :rb (js2-relpos rb pos)
-                                     :len (- (js2-current-token-end) pos)))
-    (js2-node-add-children pn
-                           (js2-elem-get-node-target pn)
-                           (js2-elem-get-node-element pn))
-    pn))
-
-(defun js2-highlight-function-call (token)
-  (when (eq (js2-token-type token) js2-NAME)
-    (js2-record-face 'js2-function-call token)))
-
-(defun js2-parse-function-call (pn)
-  (js2-highlight-function-call (js2-current-token))
-  (js2-get-token)
-  (let (args
-        (pos (js2-node-pos pn)))
-    (setq pn (make-js2-call-node :pos pos
-                                 :target pn
-                                 :lp (- (js2-current-token-beg) pos)))
-    (js2-node-add-children pn (js2-call-node-target pn))
-    ;; Add the arguments to pn, if any are supplied.
-    (setf args (nreverse (js2-parse-argument-list))
-          (js2-call-node-rp pn) (- (js2-current-token-beg) pos)
-          (js2-call-node-args pn) args)
-    (apply #'js2-node-add-children pn args)
-    (setf (js2-node-len pn) (- js2-ts-cursor pos))
-    pn))
-
-(defun js2-parse-property-access (tt pn)
-  "Parse a property access, XML descendants access, or XML attr access."
-  (let ((member-type-flags 0)
-        (dot-pos (js2-current-token-beg))
-        (dot-len (if (= tt js2-DOTDOT) 2 1))
-        name
-        ref  ; right side of . or .. operator
-        result)
-    (when (= tt js2-DOTDOT)
-      (js2-must-have-xml)
-      (setq member-type-flags js2-descendants-flag))
-    (if (not js2-compiler-xml-available)
-        (progn
-          (js2-must-match-prop-name "msg.no.name.after.dot")
-          (setq name (js2-create-name-node t js2-GETPROP)
-                result (make-js2-prop-get-node :left pn
-                                               :pos (js2-current-token-beg)
-                                               :right name
-                                               :len (js2-current-token-len)))
-          (js2-node-add-children result pn name)
-          result)
-      ;; otherwise look for XML operators
-      (setf result (if (= tt js2-DOT)
-                       (make-js2-prop-get-node)
-                     (make-js2-infix-node :type js2-DOTDOT))
-            (js2-node-pos result) (js2-node-pos pn)
-            (js2-infix-node-op-pos result) dot-pos
-            (js2-infix-node-left result) pn  ; do this after setting position
-            tt (js2-get-prop-name-token))
-      (cond
-       ;; handles: name, ns::name, ns::*, ns::[expr]
-       ((= tt js2-NAME)
-        (setq ref (js2-parse-property-name -1 nil member-type-flags)))
-       ;; handles: *, *::name, *::*, *::[expr]
-       ((= tt js2-MUL)
-        (setq ref (js2-parse-property-name nil "*" member-type-flags)))
-       ;; handles: '@attr', '@ns::attr', '@ns::*', '@ns::[expr]', etc.
-       ((= tt js2-XMLATTR)
-        (setq result (js2-parse-attribute-access)))
-       (t
-        (js2-report-error "msg.no.name.after.dot" nil dot-pos dot-len)))
-      (if ref
-          (setf (js2-node-len result) (- (js2-node-end ref)
-                                         (js2-node-pos result))
-                (js2-infix-node-right result) ref))
-      (if (js2-infix-node-p result)
-          (js2-node-add-children result
-                                 (js2-infix-node-left result)
-                                 (js2-infix-node-right result)))
-      result)))
-
-(defun js2-parse-attribute-access ()
-  "Parse an E4X XML attribute expression.
-This includes expressions of the forms:
-
-  @attr      @ns::attr     @ns::*
-  @*         @*::attr      @*::*
-  @[expr]    @*::[expr]    @ns::[expr]
-
-Called if we peeked an '@' token."
-  (let ((tt (js2-get-prop-name-token))
-        (at-pos (js2-current-token-beg)))
-    (cond
-     ;; handles: @name, @ns::name, @ns::*, @ns::[expr]
-     ((= tt js2-NAME)
-      (js2-parse-property-name at-pos nil 0))
-     ;; handles: @*, @*::name, @*::*, @*::[expr]
-     ((= tt js2-MUL)
-      (js2-parse-property-name (js2-current-token-beg) "*" 0))
-     ;; handles @[expr]
-     ((= tt js2-LB)
-      (js2-parse-xml-elem-ref at-pos))
-     (t
-      (js2-report-error "msg.no.name.after.xmlAttr")
-      ;; Avoid cascaded errors that happen if we make an error node here.
-      (js2-parse-property-name (js2-current-token-beg) "" 0)))))
-
-(defun js2-parse-property-name (at-pos s member-type-flags)
-  "Check if :: follows name in which case it becomes qualified name.
-
-AT-POS is a natural number if we just read an '@' token, else nil.
-S is the name or string that was matched:  an identifier, 'throw' or '*'.
-MEMBER-TYPE-FLAGS is a bit set tracking whether we're a '.' or '..' child.
-
-Returns a `js2-xml-ref-node' if it's an attribute access, a child of a '..'
-operator, or the name is followed by ::.  For a plain name, returns a
-`js2-name-node'.  Returns a `js2-error-node' for malformed XML expressions."
-  (let ((pos (or at-pos (js2-current-token-beg)))
-        colon-pos
-        (name (js2-create-name-node t (js2-current-token-type) s))
-        ns tt pn)
-    (catch 'return
-      (when (js2-match-token js2-COLONCOLON)
-        (setq ns name
-              colon-pos (js2-current-token-beg)
-              tt (js2-get-prop-name-token))
-        (cond
-         ;; handles name::name
-         ((= tt js2-NAME)
-          (setq name (js2-create-name-node)))
-         ;; handles name::*
-         ((= tt js2-MUL)
-          (setq name (js2-create-name-node nil nil "*")))
-         ;; handles name::[expr]
-         ((= tt js2-LB)
-          (throw 'return (js2-parse-xml-elem-ref at-pos ns colon-pos)))
-         (t
-          (js2-report-error "msg.no.name.after.coloncolon"))))
-      (if (and (null ns) (zerop member-type-flags))
-          name
-        (prog1
-            (setq pn
-                  (make-js2-xml-prop-ref-node :pos pos
-                                              :len (- (js2-node-end name) pos)
-                                              :at-pos at-pos
-                                              :colon-pos colon-pos
-                                              :propname name))
-          (js2-node-add-children pn name))))))
-
-(defun js2-parse-xml-elem-ref (at-pos &optional namespace colon-pos)
-  "Parse the [expr] portion of an xml element reference.
-For instance, @[expr], @*::[expr], or ns::[expr]."
-  (let* ((lb (js2-current-token-beg))
-         (pos (or at-pos lb))
-         rb
-         (expr (js2-parse-expr))
-         (end (js2-node-end expr))
-         pn)
-    (if (js2-must-match js2-RB "msg.no.bracket.index")
-        (setq rb (js2-current-token-beg)
-              end (js2-current-token-end)))
-    (prog1
-        (setq pn
-              (make-js2-xml-elem-ref-node :pos pos
-                                          :len (- end pos)
-                                          :namespace namespace
-                                          :colon-pos colon-pos
-                                          :at-pos at-pos
-                                          :expr expr
-                                          :lb (js2-relpos lb pos)
-                                          :rb (js2-relpos rb pos)))
-      (js2-node-add-children pn namespace expr))))
-
-(defun js2-parse-destruct-primary-expr ()
-  (let ((js2-is-in-destructuring t))
-    (js2-parse-primary-expr)))
-
-(defun js2-parse-primary-expr ()
-  "Parse a literal (leaf) expression of some sort.
-Includes complex literals such as functions, object-literals,
-array-literals, array comprehensions and regular expressions."
-  (let (tt node)
-    (setq tt (js2-current-token-type))
-    (cond
-     ((= tt js2-CLASS)
-      (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)
-      (js2-parse-object-literal))
-     ((= tt js2-LET)
-      (js2-parse-let (js2-current-token-beg)))
-     ((= tt js2-LP)
-      (js2-parse-paren-expr-or-generator-comp))
-     ((= tt js2-XMLATTR)
-      (js2-must-have-xml)
-      (js2-parse-attribute-access))
-     ((= tt js2-NAME)
-      (js2-parse-name tt))
-     ((= 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-legacy-octal-p node))
-        (js2-report-error "msg.no.octal.strict"))
-      node)
-     ((or (= tt js2-STRING) (= tt js2-NO_SUBS_TEMPLATE))
-      (make-js2-string-node :type tt))
-     ((= tt js2-TEMPLATE_HEAD)
-      (js2-parse-template-literal))
-     ((or (= tt js2-DIV) (= tt js2-ASSIGN_DIV))
-      ;; Got / or /= which in this context means a regexp literal
-      (let* ((px-pos (js2-current-token-beg))
-             (flags (js2-read-regexp tt px-pos))
-             (end (js2-current-token-end)))
-        (prog1
-            (make-js2-regexp-node :pos px-pos
-                                  :len (- end px-pos)
-                                  :value (js2-current-token-string)
-                                  :flags flags)
-          (js2-set-face px-pos end 'font-lock-string-face 'record))))
-     ((or (= tt js2-NULL)
-          (= tt js2-THIS)
-          (= tt js2-SUPER)
-          (= tt js2-FALSE)
-          (= tt js2-TRUE))
-      (make-js2-keyword-node :type tt))
-     ((= tt js2-TRIPLEDOT)
-      ;; Likewise, only valid in an arrow function with a rest param.
-      (if (and (js2-match-token js2-NAME)
-               (js2-match-token js2-RP)
-               (eq (js2-peek-token) js2-ARROW))
-          (progn
-            (js2-unget-token)  ; Put back the right paren.
-            ;; See the previous case.
-            (make-js2-keyword-node :type js2-NULL))
-        (js2-report-error "msg.syntax")
-        (make-js2-error-node)))
-     ((= tt js2-RESERVED)
-      (js2-report-error "msg.reserved.id")
-      (make-js2-name-node))
-     ((= tt js2-ERROR)
-      ;; the scanner or one of its subroutines reported the error.
-      (make-js2-error-node))
-     ((= tt js2-EOF)
-      (let* ((px-pos (point-at-bol))
-             (len (- js2-ts-cursor px-pos)))
-        (js2-report-error "msg.unexpected.eof" nil px-pos len))
-      (make-js2-error-node :pos (1- js2-ts-cursor)))
-     (t
-      (js2-report-error "msg.syntax")
-      (make-js2-error-node)))))
-
-(defun js2-parse-template-literal ()
-  (let ((beg (js2-current-token-beg))
-        (kids (list (make-js2-string-node :type js2-TEMPLATE_HEAD)))
-        (tt js2-TEMPLATE_HEAD))
-    (while (eq tt js2-TEMPLATE_HEAD)
-      (push (js2-parse-expr) kids)
-      (js2-must-match js2-RC "msg.syntax")
-      (setq tt (js2-get-token 'TEMPLATE_TAIL))
-      (push (make-js2-string-node :type tt) kids))
-    (setq kids (nreverse kids))
-    (let ((tpl (make-js2-template-node :pos beg
-                                       :len (- (js2-current-token-end) beg)
-                                       :kids kids)))
-      (apply #'js2-node-add-children tpl kids)
-      tpl)))
-
-(defun js2-parse-name (_tt)
-  (let ((name (js2-current-token-string))
-        node)
-    (setq node (if js2-compiler-xml-available
-                   (js2-parse-property-name nil name 0)
-                 (js2-create-name-node 'check-activation nil name)))
-    (if (and js2-highlight-external-variables
-             ;; FIXME: What's TRT for `js2-xml-ref-node'?
-             (js2-name-node-p node))
-        (js2-record-name-node node))
-    node))
-
-(defun js2-parse-warn-trailing-comma (msg pos elems comma-pos)
-  (js2-add-strict-warning
-   msg nil
-   ;; back up from comma to beginning of line or array/objlit
-   (max (if elems
-            (js2-node-pos (car elems))
-          pos)
-        (save-excursion
-          (goto-char comma-pos)
-          (back-to-indentation)
-          (point)))
-   comma-pos))
-
-(defun js2-parse-array-comp-or-literal ()
-  (let ((pos (js2-current-token-beg)))
-    (if (and (>= js2-language-version 200)
-             (js2-match-token js2-FOR))
-        (js2-parse-array-comp pos)
-      (js2-parse-array-literal pos))))
-
-(defun js2-parse-array-literal (pos)
-  (let ((after-lb-or-comma t)
-        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
-       ;; end of array
-       ((or (= tt js2-RB)
-            (= tt js2-EOF))  ; prevent infinite loop
-        (if (= tt js2-EOF)
-            (js2-report-error "msg.no.bracket.arg" nil pos))
-        (when (and after-comma (< js2-language-version 170))
-          (js2-parse-warn-trailing-comma "msg.array.trailing.comma"
-                                         pos (remove nil elems) after-comma))
-        (setq continue nil
-              pn (make-js2-array-node :pos pos
-                                      :len (- js2-ts-cursor pos)
-                                      :elems (nreverse elems)))
-        (apply #'js2-node-add-children pn (js2-array-node-elems pn)))
-       ;; 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
-             (not (cdr elems)))      ; but no 2nd element
-        (js2-unget-token)
-        (setf continue nil
-              pn (js2-parse-legacy-array-comp (car elems) pos)))
-       ;; another element
-       (t
-        (unless after-lb-or-comma
-          (js2-report-error "msg.no.bracket.arg"))
-        (if (and (= tt js2-TRIPLEDOT)
-                 (>= js2-language-version 200))
-            ;; rest/spread operator
-            (progn
-              (push (js2-make-unary nil 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
-              after-comma nil))))
-    (unless js2-is-in-destructuring
-      (js2-pop-scope))
-    pn))
-
-(defun js2-parse-legacy-array-comp (expr pos)
-  "Parse a legacy array comprehension (JavaScript 1.7).
-EXPR is the first expression after the opening left-bracket.
-POS is the beginning of the LB token preceding EXPR.
-We should have just parsed the 'for' keyword before calling this function."
-  (let ((current-scope js2-current-scope)
-        loops first filter result)
-    (unwind-protect
-        (progn
-          (while (js2-match-token js2-FOR)
-            (let ((loop (make-js2-comp-loop-node)))
-              (js2-push-scope loop)
-              (push loop loops)
-              (js2-parse-comp-loop loop)))
-          ;; First loop takes expr scope's parent.
-          (setf (js2-scope-parent-scope (setq first (car (last loops))))
-                (js2-scope-parent-scope current-scope))
-          ;; Set expr scope's parent to the last loop.
-          (setf (js2-scope-parent-scope current-scope) (car loops))
-          (if (/= (js2-get-token) js2-IF)
-              (js2-unget-token)
-            (setq filter (js2-parse-condition))))
-      (dotimes (_ (1- (length loops)))
-        (js2-pop-scope)))
-    (js2-must-match js2-RB "msg.no.bracket.arg" pos)
-    (setq result (make-js2-comp-node :pos pos
-                                     :len (- js2-ts-cursor pos)
-                                     :result expr
-                                     :loops (nreverse loops)
-                                     :filters (and filter (list (car filter)))
-                                     :form 'LEGACY_ARRAY))
-    ;; Set comp loop's parent to the last loop.
-    ;; TODO: Get rid of the bogus expr scope.
-    (setf (js2-scope-parent-scope result) first)
-    (apply #'js2-node-add-children result expr (car filter)
-           (js2-comp-node-loops result))
-    result))
-
-(defun js2-parse-array-comp (pos)
-  "Parse an ES6 array comprehension.
-POS is the beginning of the LB token.
-We should have just parsed the 'for' keyword before calling this function."
-  (let ((pn (js2-parse-comprehension pos 'ARRAY)))
-    (js2-must-match js2-RB "msg.no.bracket.arg" pos)
-    pn))
-
-(defun js2-parse-generator-comp (pos)
-  (let* ((js2-nesting-of-function (1+ js2-nesting-of-function))
-         (js2-current-script-or-fn
-          (make-js2-function-node :generator-type 'COMPREHENSION))
-         (pn (js2-parse-comprehension pos 'STAR_GENERATOR)))
-    (js2-must-match js2-RP "msg.no.paren" pos)
-    pn))
-
-(defun js2-parse-comprehension (pos form)
-  (let (loops filters expr result last)
-    (unwind-protect
-        (progn
-          (js2-unget-token)
-          (while (js2-match-token js2-FOR)
-            (let ((loop (make-js2-comp-loop-node)))
-              (js2-push-scope loop)
-              (push loop loops)
-              (js2-parse-comp-loop loop)))
-          (while (js2-match-token js2-IF)
-            (push (car (js2-parse-condition)) filters))
-          (setq expr (js2-parse-assign-expr))
-          (setq last (car loops)))
-      (dolist (_ loops)
-        (js2-pop-scope)))
-    (setq result (make-js2-comp-node :pos pos
-                                     :len (- js2-ts-cursor pos)
-                                     :result expr
-                                     :loops (nreverse loops)
-                                     :filters (nreverse filters)
-                                     :form form))
-    (apply #'js2-node-add-children result (js2-comp-node-loops result))
-    (apply #'js2-node-add-children result expr (js2-comp-node-filters result))
-    (setf (js2-scope-parent-scope result) last)
-    result))
-
-(defun js2-parse-comp-loop (pn &optional only-of-p)
-  "Parse a 'for [each] (foo [in|of] bar)' expression in an Array comprehension.
-The current token should be the initial FOR.
-If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed."
-  (let ((pos (js2-comp-loop-node-pos pn))
-        tt iter obj foreach-p forof-p in-pos each-pos lp rp)
-    (when (and (not only-of-p) (js2-match-token js2-NAME))
-      (if (string= (js2-current-token-string) "each")
-          (progn
-            (setq foreach-p t
-                  each-pos (- (js2-current-token-beg) pos)) ; relative
-            (js2-record-face 'font-lock-keyword-face))
-        (js2-report-error "msg.no.paren.for")))
-    (if (js2-must-match js2-LP "msg.no.paren.for")
-        (setq lp (- (js2-current-token-beg) pos)))
-    (setq tt (js2-peek-token))
-    (cond
-     ((or (= tt js2-LB)
-          (= tt js2-LC))
-      (js2-get-token)
-      (setq iter (js2-parse-destruct-primary-expr))
-      (js2-define-destruct-symbols iter js2-LET
-                                   'font-lock-variable-name-face t))
-     ((js2-match-token js2-NAME)
-      (setq iter (js2-create-name-node)))
-     (t
-      (js2-report-error "msg.bad.var")))
-    ;; Define as a let since we want the scope of the variable to
-    ;; be restricted to the array comprehension
-    (if (js2-name-node-p iter)
-        (js2-define-symbol js2-LET (js2-name-node-name iter) pn t))
-    (if (or (and (not only-of-p) (js2-match-token js2-IN))
-            (and (>= js2-language-version 200)
-                 (js2-match-contextual-kwd "of")
-                 (setq forof-p t)))
-        (setq in-pos (- (js2-current-token-beg) pos))
-      (js2-report-error "msg.in.after.for.name"))
-    (setq obj (js2-parse-expr))
-    (if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
-        (setq rp (- (js2-current-token-beg) pos)))
-    (setf (js2-node-pos pn) pos
-          (js2-node-len pn) (- js2-ts-cursor pos)
-          (js2-comp-loop-node-iterator pn) iter
-          (js2-comp-loop-node-object pn) obj
-          (js2-comp-loop-node-in-pos pn) in-pos
-          (js2-comp-loop-node-each-pos pn) each-pos
-          (js2-comp-loop-node-foreach-p pn) foreach-p
-          (js2-comp-loop-node-forof-p pn) forof-p
-          (js2-comp-loop-node-lp pn) lp
-          (js2-comp-loop-node-rp pn) rp)
-    (js2-node-add-children pn iter obj)
-    pn))
-
-(defun js2-parse-class-stmt ()
-  (let ((pos (js2-current-token-beg))
-        (_ (js2-must-match-name "msg.unnamed.class.stmt"))
-        (name (js2-create-name-node t)))
-    (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)
-      node)))
-
-(defun js2-parse-class-expr ()
-  (let ((pos (js2-current-token-beg))
-        name)
-    (when (js2-match-token js2-NAME)
-      (setq name (js2-create-name-node t)))
-    (js2-parse-class pos 'CLASS_EXPRESSION name)))
-
-(defun js2-parse-class (pos form name)
-  ;; class X [extends ...] {
-  (let (pn elems extends)
-    (if (js2-match-token js2-EXTENDS)
-        (if (= (js2-peek-token) js2-LC)
-            (js2-report-error "msg.missing.extends")
-          ;; TODO(sdh): this should be left-hand-side-expr, not assign-expr
-          (setq extends (js2-parse-assign-expr))
-          (if (not extends)
-              (js2-report-error "msg.bad.extends"))))
-    (js2-must-match js2-LC "msg.no.brace.class")
-    (setq elems (js2-parse-object-literal-elems t)
-          pn (make-js2-class-node :pos pos
-                                  :len (- js2-ts-cursor pos)
-                                  :form form
-                                  :name name
-                                  :extends extends
-                                  :elems elems))
-    (apply #'js2-node-add-children
-           pn name extends (js2-class-node-elems pn))
-    pn))
-
-(defun js2-parse-object-literal ()
-  (let* ((pos (js2-current-token-beg))
-         (elems (js2-parse-object-literal-elems))
-         (result (make-js2-object-node :pos pos
-                                       :len (- js2-ts-cursor pos)
-                                       :elems elems)))
-    (apply #'js2-node-add-children result (js2-object-node-elems result))
-    result))
-
-(defun js2-property-key-string (property-node)
-  "Return the key of PROPERTY-NODE (a `js2-object-prop-node' or
-`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)."
-  (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))
-        (static nil)
-        (continue t)
-        tt elems elem
-        elem-key-string previous-elem-key-string
-        after-comma previous-token)
-    (while continue
-      (setq tt (js2-get-prop-name-token)
-            static nil
-            elem nil
-            previous-token nil)
-      ;; Handle 'static' keyword only if we're in a class
-      (when (and class-p (= js2-NAME tt)
-                 (string= "static" (js2-current-token-string)))
-        (js2-record-face 'font-lock-keyword-face)
-        (setq static t
-              tt (js2-get-prop-name-token)))
-      ;; Handle generator * before the property name for in-line functions
-      (when (and (>= js2-language-version 200)
-                 (= js2-MUL tt))
-        (setq previous-token (js2-current-token)
-              tt (js2-get-prop-name-token)))
-      ;; Handle getter, setter and async methods
-      (let ((prop (js2-current-token-string)))
-        (when (and (>= js2-language-version 200)
-                   (= js2-NAME tt)
-                   (member prop '("get" "set" "async"))
-                   (member (js2-peek-token 'KEYWORD_IS_NAME)
-                           (list js2-NAME js2-STRING js2-NUMBER js2-LB)))
-          (setq previous-token (js2-current-token)
-                tt (js2-get-prop-name-token))))
-      (cond
-       ;; 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 nil 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 previous-token class-p))
-        (if (and (null elem)
-                 (not js2-recover-from-parse-errors))
-            (setq continue nil)))
-       ;; Break out of loop, and handle trailing commas.
-       ((or (= tt js2-RC)
-            (= tt js2-EOF))
-        (js2-unget-token)
-        (setq continue nil)
-        (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
-          (setq continue nil))))         ; end switch
-      ;; Handle static for classes' codegen.
-      (if static
-          (if elem (js2-node-set-prop elem 'STATIC t)
-            (js2-report-error "msg.unexpected.static")))
-      ;; Handle commas, depending on class-p.
-      (let ((tok (js2-get-prop-name-token)))
-        (if (eq tok js2-COMMA)
-            (if class-p
-                (js2-report-error "msg.class.unexpected.comma")
-              (setq after-comma (js2-current-token-end)))
-          (js2-unget-token)
-          (unless class-p (setq continue nil))))
-      (when elem
-        (when (and js2-in-use-strict-directive
-                   (setq elem-key-string (js2-property-key-string elem))
-                   (cl-some
-                    (lambda (previous-elem)
-                      (and (setq previous-elem-key-string
-                                 (js2-property-key-string previous-elem))
-                           ;; 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
-                            (js2-node-abs-pos (js2-infix-node-left elem))
-                            (js2-node-len (js2-infix-node-left elem))))
-        ;; Append any parsed element.
-        (push elem elems)))       ; end loop
-    (js2-must-match js2-RC "msg.no.brace.prop")
-    (nreverse elems)))
-
-(defun js2-parse-named-prop (tt previous-token &optional class-p)
-  "Parse a name, string, or getter/setter object property.
-When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted."
-  (let ((key (js2-parse-prop-name tt))
-        (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))))
-        pos)
-    (when (member prop '("get" "set" "async"))
-      (setq pos (js2-token-beg previous-token))
-      (js2-set-face (js2-token-beg previous-token)
-                    (js2-token-end previous-token)
-                    'font-lock-keyword-face 'record))  ; get/set/async
-    (cond
-     ;; method definition: {f() {...}}
-     ((and (= (js2-peek-token) js2-LP)
-           (>= js2-language-version 200))
-      (when (or (js2-name-node-p key) (js2-string-node-p key))
-        ;; highlight function name properties
-        (js2-record-face 'font-lock-function-name-face))
-      (js2-parse-method-prop pos key property-type))
-     ;; class field or binding element with initializer
-     ((and (= (js2-peek-token) js2-ASSIGN)
-           (>= js2-language-version 200))
-      (if (not (or class-p
-                   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))
-            (end (js2-current-token-end))
-            (expr (js2-parse-plain-property key class-p)))
-        (when (and (= tt js2-NAME)
-                   (not js2-is-in-destructuring)
-                   js2-highlight-external-variables
-                   (js2-node-get-prop expr 'SHORTHAND))
-          (js2-record-name-node key))
-        (js2-set-face beg end
-                      (if (js2-function-node-p
-                           (js2-object-prop-node-right expr))
-                          'font-lock-function-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 &optional class-p)
-  "Parse a non-getter/setter property in an object literal.
-PROP is the node representing the property: a number, name,
-string or expression."
-  (let* (tt
-         (pos (js2-node-pos prop))
-         colon expr result)
-    (cond
-     ;; Abbreviated property, as in {foo, bar} or class {a; b}
-     ((and (>= js2-language-version 200)
-           (if class-p
-               (and (setq tt (js2-peek-token-or-eol))
-                    (member tt (list js2-EOL js2-RC js2-SEMI)))
-             (and (setq tt (js2-peek-token))
-                  (member tt (list js2-COMMA js2-RC))
-                  (js2-name-node-p prop))))
-      (setq result (make-js2-object-prop-node
-                    :pos pos
-                    :len (js2-node-len prop)
-                    :left prop
-                    :right prop
-                    :op-pos (- (js2-current-token-beg) pos)))
-      (js2-node-add-children result prop)
-      (js2-node-set-prop result 'SHORTHAND t)
-      result)
-     ;; Normal property
-     (t
-      (setq tt (js2-get-token))
-      (if (= tt js2-COLON)
-          (setq colon (- (js2-current-token-beg) pos)
-                expr (js2-parse-assign-expr))
-        (js2-report-error "msg.no.colon.prop")
-        (setq expr (make-js2-error-node)))
-      (setq result (make-js2-object-prop-node
-                   :pos pos
-                   ;; don't include last consumed token in length
-                   :len (- (+ (js2-node-pos expr)
-                              (js2-node-len expr))
-                           pos)
-                   :left prop
-                   :right expr
-                   :op-pos colon))
-      (js2-node-add-children result prop expr)
-      result))))
-
-(defun js2-parse-method-prop (pos prop type-string)
-  "Parse method property in an object literal or a class body.
-JavaScript syntax is:
-
-  { foo(...) {...}, get foo() {...}, set foo(x) {...}, *foo(...) {...},
-    async foo(...) {...} }
-
-and expression closure style is also supported
-
-  { get foo() x, set foo(x) _x = x }
-
-POS is the start position of the `get' or `set' keyword, if any.
-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 (or (cdr (assoc type-string '(("get" . GET)
-                                             ("set" . SET)
-                                             ("async" . ASYNC))))
-                   'FUNCTION))
-         result end
-         (pos (or 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-method-node :pos pos
-                                       :len (- end pos)
-                                       :left prop
-                                       :right fn))
-    (js2-node-add-children result prop fn)
-    result))
-
-(defun js2-create-name-node (&optional check-activation-p token string)
-  "Create a name node using the current token and, optionally, STRING.
-And, if CHECK-ACTIVATION-P is non-nil, use the value of TOKEN."
-  (let* ((beg (js2-current-token-beg))
-         (tt (js2-current-token-type))
-         (s (or string
-                (if (= js2-NAME tt)
-                    (js2-current-token-string)
-                  (js2-tt-name tt))))
-         name)
-    (setq name (make-js2-name-node :pos beg
-                                   :name s
-                                   :len (length s)))
-    (if check-activation-p
-        (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
-;; `js-mode'.  To continue support for the older versions, some code
-;; that was here previously was moved to `js2-old-indent.el'.
-
-;; Whichever indenter is used, it's often "wrong", however, and needs
-;; to be overridden.  The right long-term solution is probably to
-;; emulate (or integrate with) cc-engine, but it's a nontrivial amount
-;; of coding.  Even when a parse tree from `js2-parse' is present,
-;; which is not true at the moment the user is typing, computing
-;; indentation is still thousands of lines of code to handle every
-;; possible syntactic edge case.
-
-;; In the meantime, the compromise solution is that we offer a "bounce
-;; indenter", configured with `js2-bounce-indent-p', which cycles the
-;; current line indent among various likely guess points.  This approach
-;; is far from perfect, but should at least make it slightly easier to
-;; move the line towards its desired indentation when manually
-;; overriding Karl's heuristic nesting guesser.
-
-(defun js2-backward-sws ()
-  "Move backward through whitespace and comments."
-  (interactive)
-  (while (forward-comment -1)))
-
-(defun js2-forward-sws ()
-  "Move forward through whitespace and comments."
-  (interactive)
-  (while (forward-comment 1)))
-
-(defun js2-arglist-close ()
-  "Return non-nil if we're on a line beginning with a close-paren/brace."
-  (save-excursion
-    (goto-char (point-at-bol))
-    (js2-forward-sws)
-    (looking-at "[])}]")))
-
-(defun js2-indent-looks-like-label-p ()
-  (goto-char (point-at-bol))
-  (js2-forward-sws)
-  (looking-at (concat js2-mode-identifier-re ":")))
-
-(defun js2-indent-in-objlit-p (parse-status)
-  "Return non-nil if this looks like an object-literal entry."
-  (let ((start (nth 1 parse-status)))
-    (and
-     start
-     (save-excursion
-       (and (zerop (forward-line -1))
-            (not (< (point) start))     ; crossed a {} boundary
-            (js2-indent-looks-like-label-p)))
-     (save-excursion
-       (js2-indent-looks-like-label-p)))))
-
-;; If prev line looks like foobar({ then we're passing an object
-;; literal to a function call, and people pretty much always want to
-;; de-dent back to the previous line, so move the 'basic-offset'
-;; position to the front.
-(defun js2-indent-objlit-arg-p (parse-status)
-  (save-excursion
-    (back-to-indentation)
-    (js2-backward-sws)
-    (and (eq (1- (point)) (nth 1 parse-status))
-         (eq (char-before) ?{)
-         (progn
-           (forward-char -1)
-           (skip-chars-backward " \t")
-           (eq (char-before) ?\()))))
-
-(defun js2-indent-case-block-p ()
-  (save-excursion
-    (back-to-indentation)
-    (js2-backward-sws)
-    (goto-char (point-at-bol))
-    (skip-chars-forward " \t")
-    (looking-at "case\\s-.+:")))
-
-(defun js2-bounce-indent (normal-col parse-status &optional backward)
-  "Cycle among alternate computed indentation positions.
-PARSE-STATUS is the result of `parse-partial-sexp' from the beginning
-of the buffer to the current point.  NORMAL-COL is the indentation
-column computed by the heuristic guesser based on current paren,
-bracket, brace and statement nesting.  If BACKWARDS, cycle positions
-in reverse."
-  (let ((cur-indent (current-indentation))
-        (old-buffer-undo-list buffer-undo-list)
-        ;; Emacs 21 only has `count-lines', not `line-number-at-pos'
-        (current-line (save-excursion
-                        (forward-line 0)  ; move to bol
-                        (1+ (count-lines (point-min) (point)))))
-        positions pos main-pos anchor arglist-cont same-indent
-        basic-offset computed-pos)
-    ;; temporarily don't record undo info, if user requested this
-    (when js2-mode-indent-inhibit-undo
-      (setq buffer-undo-list t))
-    (unwind-protect
-        (progn
-          ;; First likely point:  indent from beginning of previous code line
-          (push (setq basic-offset
-                      (+ (save-excursion
-                           (back-to-indentation)
-                           (js2-backward-sws)
-                           (back-to-indentation)
-                           (current-column))
-                         js2-basic-offset))
-                positions)
-
-          ;; (First + epsilon) likely point:  indent 2x from beginning of
-          ;; previous code line.  Google does it this way.
-          (push (setq basic-offset
-                      (+ (save-excursion
-                           (back-to-indentation)
-                           (js2-backward-sws)
-                           (back-to-indentation)
-                           (current-column))
-                         (* 2 js2-basic-offset)))
-                positions)
-
-          ;; Second likely point:  indent from assign-expr RHS.  This
-          ;; is just a crude guess based on finding " = " on the previous
-          ;; line containing actual code.
-          (setq pos (save-excursion
-                      (forward-line -1)
-                      (goto-char (point-at-bol))
-                      (when (re-search-forward "\\s-+\\(=\\)\\s-+"
-                                               (point-at-eol) t)
-                        (goto-char (match-end 1))
-                        (skip-chars-forward " \t\r\n")
-                        (current-column))))
-          (when pos
-            (cl-incf pos js2-basic-offset)
-            (push pos positions))
-
-          ;; Third likely point:  same indent as previous line of code.
-          ;; Make it the first likely point if we're not on an
-          ;; arglist-close line and previous line ends in a comma, or
-          ;; both this line and prev line look like object-literal
-          ;; elements.
-          (setq pos (save-excursion
-                      (goto-char (point-at-bol))
-                      (js2-backward-sws)
-                      (back-to-indentation)
-                      (prog1
-                          (current-column)
-                        ;; while we're here, look for trailing comma
-                        (if (save-excursion
-                              (goto-char (point-at-eol))
-                              (js2-backward-sws)
-                              (eq (char-before) ?,))
-                            (setq arglist-cont (1- (point)))))))
-          (when pos
-            (if (and (or arglist-cont
-                         (js2-indent-in-objlit-p parse-status))
-                     (not (js2-arglist-close)))
-                (setq same-indent pos))
-            (push pos positions))
-
-          ;; Fourth likely point:  first preceding code with less indentation.
-          ;; than the immediately preceding code line.
-          (setq pos (save-excursion
-                      (back-to-indentation)
-                      (js2-backward-sws)
-                      (back-to-indentation)
-                      (setq anchor (current-column))
-                      (while (and (zerop (forward-line -1))
-                                  (>= (progn
-                                        (back-to-indentation)
-                                        (current-column))
-                                      anchor)))
-                      (setq pos (current-column))))
-          (push pos positions)
-
-          ;; nesting-heuristic position, main by default
-          (push (setq main-pos normal-col) positions)
-
-          ;; delete duplicates and sort positions list
-          (setq positions (sort (delete-dups positions) '<))
-
-          ;; comma-list continuation lines:  prev line indent takes precedence
-          (if same-indent
-              (setq main-pos same-indent))
-
-          ;; common special cases where we want to indent in from previous line
-          (if (or (js2-indent-case-block-p)
-                  (js2-indent-objlit-arg-p parse-status))
-              (setq main-pos basic-offset))
-
-          ;; if bouncing backward, reverse positions list
-          (if backward
-              (setq positions (reverse positions)))
-
-          ;; record whether we're already sitting on one of the alternatives
-          (setq pos (member cur-indent positions))
-
-          (cond
-           ;; case 0:  we're one one of the alternatives and this is the
-           ;; first time they've pressed TAB on this line (best-guess).
-           ((and js2-mode-indent-ignore-first-tab
-                 pos
-                 ;; first time pressing TAB on this line?
-                 (not (eq js2-mode-last-indented-line current-line)))
-            ;; do nothing
-            (setq computed-pos nil))
-           ;; case 1:  only one computed position => use it
-           ((null (cdr positions))
-            (setq computed-pos 0))
-           ;; case 2:  not on any of the computed spots => use main spot
-           ((not pos)
-            (setq computed-pos (js2-position main-pos positions)))
-           ;; case 3:  on last position:  cycle to first position
-           ((null (cdr pos))
-            (setq computed-pos 0))
-           ;; case 4:  on intermediate position:  cycle to next position
-           (t
-            (setq computed-pos (js2-position (cl-second pos) positions))))
-
-          ;; see if any hooks want to indent; otherwise we do it
-          (cl-loop with result = nil
-                   for hook in js2-indent-hook
-                   while (null result)
-                   do
-                   (setq result (funcall hook positions computed-pos))
-                   finally do
-                   (unless (or result (null computed-pos))
-                     (indent-line-to (nth computed-pos positions)))))
-
-      ;; finally
-      (if js2-mode-indent-inhibit-undo
-          (setq buffer-undo-list old-buffer-undo-list))
-      ;; see commentary for `js2-mode-last-indented-line'
-      (setq js2-mode-last-indented-line current-line))))
-
-(defun js2-1-line-comment-continuation-p ()
-  "Return t if we're in a 1-line comment continuation.
-If so, we don't ever want to use bounce-indent."
-  (save-excursion
-    (and (progn
-           (forward-line 0)
-           (looking-at "\\s-*//"))
-         (progn
-           (forward-line -1)
-           (forward-line 0)
-           (when (looking-at "\\s-*$")
-             (js2-backward-sws)
-             (forward-line 0))
-           (looking-at "\\s-*//")))))
-
-(defun js2-indent-bounce (&optional backward)
-  "Indent the current line, bouncing between several positions."
-  (interactive)
-  (let (parse-status offset indent-col
-        ;; 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))
-    (setq parse-status (save-excursion
-                         (syntax-ppss (point-at-bol)))
-          offset (- (point) (save-excursion
-                              (back-to-indentation)
-                              (point))))
-    ;; Don't touch multiline strings.
-    (unless (nth 3 parse-status)
-      (setq indent-col (js2-proper-indentation parse-status))
-      (cond
-       ;; It doesn't work well on first line of buffer.
-       ((and (not (nth 4 parse-status))
-             (not (js2-same-line (point-min)))
-             (not (js2-1-line-comment-continuation-p)))
-        (js2-bounce-indent indent-col parse-status backward))
-       ;; just indent to the guesser's likely spot
-       (t (indent-line-to indent-col)))
-      (when (cl-plusp offset)
-        (forward-char offset)))))
-
-(defun js2-indent-bounce-backward ()
-  "Indent the current line, bouncing between positions in reverse."
-  (interactive)
-  (js2-indent-bounce t))
-
-(defun js2-indent-region (start end)
-  "Indent the region, but don't use bounce indenting."
-  (let ((js2-bounce-indent-p nil)
-        (indent-region-function nil)
-        (after-change-functions (remq 'js2-mode-edit
-                                      after-change-functions)))
-    (indent-region start end nil) ; nil for byte-compiler
-    (js2-mode-edit start end (- end start))))
-
-(defvar js2-minor-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "C-c C-`") #'js2-next-error)
-    map)
-  "Keymap used when `js2-minor-mode' is active.")
-
-;;;###autoload
-(define-minor-mode js2-minor-mode
-  "Minor mode for running js2 as a background linter.
-This allows you to use a different major mode for JavaScript editing,
-such as `js-mode', while retaining the asynchronous error/warning
-highlighting features of `js2-mode'."
-  :group 'js2-mode
-  :lighter " js-lint"
-  (if (derived-mode-p 'js2-mode)
-      (setq js2-minor-mode nil)
-    (if js2-minor-mode
-        (js2-minor-mode-enter)
-      (js2-minor-mode-exit))))
-
-(defun js2-minor-mode-enter ()
-  "Initialization for `js2-minor-mode'."
-  (set (make-local-variable 'max-lisp-eval-depth)
-       (max max-lisp-eval-depth 3000))
-  (setq next-error-function #'js2-next-error)
-  ;; Experiment:  make reparse-delay longer for longer files.
-  (if (cl-plusp js2-dynamic-idle-timer-adjust)
-      (setq js2-idle-timer-delay
-            (* js2-idle-timer-delay
-               (/ (point-max) js2-dynamic-idle-timer-adjust))))
-  (setq js2-mode-buffer-dirty-p t
-        js2-mode-parsing nil)
-  (set (make-local-variable 'js2-highlight-level) 0) ; no syntax highlighting
-  (add-hook 'after-change-functions #'js2-minor-mode-edit nil t)
-  (add-hook 'change-major-mode-hook #'js2-minor-mode-exit nil t)
-  (when js2-include-jslint-globals
-    (add-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals nil t))
-  (when js2-include-jslint-declaration-externs
-    (add-hook 'js2-post-parse-callbacks 'js2-apply-jslint-declaration-externs nil t))
-  (run-hooks 'js2-init-hook)
-  (js2-reparse))
-
-(defun js2-minor-mode-exit ()
-  "Turn off `js2-minor-mode'."
-  (setq next-error-function nil)
-  (remove-hook 'after-change-functions #'js2-mode-edit t)
-  (remove-hook 'change-major-mode-hook #'js2-minor-mode-exit t)
-  (when js2-mode-node-overlay
-    (delete-overlay js2-mode-node-overlay)
-    (setq js2-mode-node-overlay nil))
-  (js2-remove-overlays)
-  (remove-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals t)
-  (remove-hook 'js2-post-parse-callbacks 'js2-apply-jslint-declaration-externs t)
-  (setq js2-mode-ast nil))
-
-(defvar js2-source-buffer nil "Linked source buffer for diagnostics view")
-(make-variable-buffer-local 'js2-source-buffer)
-
-(cl-defun js2-display-error-list ()
-  "Display a navigable buffer listing parse errors/warnings."
-  (interactive)
-  (unless (js2-have-errors-p)
-    (message "No errors")
-    (cl-return-from js2-display-error-list))
-  (cl-labels ((annotate-list
-               (lst type)
-               "Add diagnostic TYPE and line number to errs list"
-               (mapcar (lambda (err)
-                         (list err type (line-number-at-pos (nth 1 err))))
-                       lst)))
-    (let* ((srcbuf (current-buffer))
-           (errbuf (get-buffer-create "*js-lint*"))
-           (errors (annotate-list
-                    (when js2-mode-ast (js2-ast-root-errors js2-mode-ast))
-                    'js2-error))  ; must be a valid face name
-           (warnings (annotate-list
-                      (when js2-mode-ast (js2-ast-root-warnings js2-mode-ast))
-                      'js2-warning))  ; must be a valid face name
-           (all-errs (sort (append errors warnings)
-                           (lambda (e1 e2) (< (cl-cadar e1) (cl-cadar e2))))))
-      (with-current-buffer errbuf
-        (let ((inhibit-read-only t))
-          (erase-buffer)
-          (dolist (err all-errs)
-            (cl-destructuring-bind ((msg-key beg _end &rest) type line) err
-              (insert-text-button
-               (format "line %d: %s" line (js2-get-msg msg-key))
-               'face type
-               'follow-link "\C-m"
-               'action 'js2-error-buffer-jump
-               'js2-msg (js2-get-msg msg-key)
-               'js2-pos beg)
-              (insert "\n"))))
-        (js2-error-buffer-mode)
-        (setq js2-source-buffer srcbuf)
-        (pop-to-buffer errbuf)
-        (goto-char (point-min))
-        (unless (eobp)
-          (js2-error-buffer-view))))))
-
-(defvar js2-error-buffer-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "n" #'js2-error-buffer-next)
-    (define-key map "p" #'js2-error-buffer-prev)
-    (define-key map (kbd "RET") #'js2-error-buffer-jump)
-    (define-key map "o" #'js2-error-buffer-view)
-    (define-key map "q" #'js2-error-buffer-quit)
-    map)
-  "Keymap used for js2 diagnostics buffers.")
-
-(defun js2-error-buffer-mode ()
-  "Major mode for js2 diagnostics buffers.
-Selecting an error will jump it to the corresponding source-buffer error.
-\\{js2-error-buffer-mode-map}"
-  (interactive)
-  (setq major-mode 'js2-error-buffer-mode
-        mode-name "JS Lint Diagnostics")
-  (use-local-map js2-error-buffer-mode-map)
-  (setq truncate-lines t)
-  (set-buffer-modified-p nil)
-  (setq buffer-read-only t)
-  (run-hooks 'js2-error-buffer-mode-hook))
-
-(defun js2-error-buffer-next ()
-  "Move to next error and view it."
-  (interactive)
-  (when (zerop (forward-line 1))
-    (js2-error-buffer-view)))
-
-(defun js2-error-buffer-prev ()
-  "Move to previous error and view it."
-  (interactive)
-  (when (zerop (forward-line -1))
-    (js2-error-buffer-view)))
-
-(defun js2-error-buffer-quit ()
-  "Kill the current buffer."
-  (interactive)
-  (kill-buffer))
-
-(defun js2-error-buffer-jump (&rest ignored)
-  "Jump cursor to current error in source buffer."
-  (interactive)
-  (when (js2-error-buffer-view)
-    (pop-to-buffer js2-source-buffer)))
-
-(defun js2-error-buffer-view ()
-  "Scroll source buffer to show error at current line."
-  (interactive)
-  (cond
-   ((not (eq major-mode 'js2-error-buffer-mode))
-    (message "Not in a js2 errors buffer"))
-   ((not (buffer-live-p js2-source-buffer))
-    (message "Source buffer has been killed"))
-   ((not (wholenump (get-text-property (point) 'js2-pos)))
-    (message "There does not seem to be an error here"))
-   (t
-    (let ((pos (get-text-property (point) 'js2-pos))
-          (msg (get-text-property (point) 'js2-msg)))
-      (save-selected-window
-        (pop-to-buffer js2-source-buffer)
-        (goto-char pos)
-        (message msg))))))
-
-;;;###autoload
-(define-derived-mode js2-mode js-mode "Javascript-IDE"
-  "Major mode for editing JavaScript code."
-  (set (make-local-variable 'max-lisp-eval-depth)
-       (max max-lisp-eval-depth 3000))
-  (set (make-local-variable 'indent-line-function) #'js2-indent-line)
-  (set (make-local-variable 'indent-region-function) #'js2-indent-region)
-  (set (make-local-variable 'syntax-propertize-function) nil)
-  (set (make-local-variable 'comment-line-break-function) #'js2-line-break)
-  (set (make-local-variable 'beginning-of-defun-function) #'js2-beginning-of-defun)
-  (set (make-local-variable 'end-of-defun-function) #'js2-end-of-defun)
-  ;; We un-confuse `parse-partial-sexp' by setting syntax-table properties
-  ;; for characters inside regexp literals.
-  (set (make-local-variable 'parse-sexp-lookup-properties) t)
-  ;; this is necessary to make `show-paren-function' work properly
-  (set (make-local-variable 'parse-sexp-ignore-comments) t)
-  ;; needed for M-x rgrep, among other things
-  (put 'js2-mode 'find-tag-default-function #'js2-mode-find-tag)
-
-  (setq font-lock-defaults '(nil t))
-
-  ;; Experiment:  make reparse-delay longer for longer files.
-  (when (cl-plusp js2-dynamic-idle-timer-adjust)
-    (setq js2-idle-timer-delay
-          (* js2-idle-timer-delay
-             (/ (point-max) js2-dynamic-idle-timer-adjust))))
-
-  (add-hook 'change-major-mode-hook #'js2-mode-exit nil t)
-  (add-hook 'after-change-functions #'js2-mode-edit nil t)
-  (setq imenu-create-index-function #'js2-mode-create-imenu-index)
-  (setq next-error-function #'js2-next-error)
-  (imenu-add-to-menubar (concat "IM-" mode-name))
-  (add-to-invisibility-spec '(js2-outline . t))
-  (set (make-local-variable 'line-move-ignore-invisible) t)
-  (set (make-local-variable 'forward-sexp-function) #'js2-mode-forward-sexp)
-  (when (fboundp 'cursor-sensor-mode) (cursor-sensor-mode 1))
-
-  (setq js2-mode-functions-hidden nil
-        js2-mode-comments-hidden nil
-        js2-mode-buffer-dirty-p t
-        js2-mode-parsing nil)
-
-  (when js2-include-jslint-globals
-    (add-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals nil t))
-  (when js2-include-jslint-declaration-externs
-    (add-hook 'js2-post-parse-callbacks 'js2-apply-jslint-declaration-externs nil t))
-
-  (run-hooks 'js2-init-hook)
-
-  (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."
-  (interactive)
-  (when js2-mode-node-overlay
-    (delete-overlay js2-mode-node-overlay)
-    (setq js2-mode-node-overlay nil))
-  (js2-remove-overlays)
-  (setq js2-mode-ast nil)
-  (remove-hook 'change-major-mode-hook #'js2-mode-exit t)
-  (remove-from-invisibility-spec '(js2-outline . t))
-  (js2-mode-show-all)
-  (with-silent-modifications
-    (js2-clear-face (point-min) (point-max))))
-
-(defun js2-mode-reset-timer ()
-  "Cancel any existing parse timer and schedule a new one."
-  (if js2-mode-parse-timer
-      (cancel-timer js2-mode-parse-timer))
-  (setq js2-mode-parsing nil)
-  (let ((timer (timer-create)))
-    (setq js2-mode-parse-timer timer)
-    (timer-set-function timer 'js2-mode-idle-reparse (list (current-buffer)))
-    (timer-set-idle-time timer js2-idle-timer-delay)
-    ;; http://debbugs.gnu.org/cgi/bugreport.cgi?bug=12326
-    (timer-activate-when-idle timer nil)))
-
-(defun js2-mode-idle-reparse (buffer)
-  "Run `js2-reparse' if BUFFER is the current buffer, or schedule
-it to be reparsed when the buffer is selected."
-  (cond ((eq buffer (current-buffer))
-         (js2-reparse))
-        ((buffer-live-p buffer)
-         ;; reparse when the buffer is selected again
-         (with-current-buffer buffer
-           (add-hook 'window-configuration-change-hook
-                     #'js2-mode-idle-reparse-inner
-                     nil t)))))
-
-(defun js2-mode-idle-reparse-inner ()
-  (remove-hook 'window-configuration-change-hook
-               #'js2-mode-idle-reparse-inner
-               t)
-  (js2-reparse))
-
-(defun js2-mode-edit (_beg _end _len)
-  "Schedule a new parse after buffer is edited.
-Buffer edit spans from BEG to END and is of length LEN."
-  (setq js2-mode-buffer-dirty-p t)
-  (js2-mode-hide-overlay)
-  (js2-mode-reset-timer))
-
-(defun js2-minor-mode-edit (_beg _end _len)
-  "Callback for buffer edits in `js2-mode'.
-Schedules a new parse after buffer is edited.
-Buffer edit spans from BEG to END and is of length LEN."
-  (setq js2-mode-buffer-dirty-p t)
-  (js2-mode-hide-overlay)
-  (js2-mode-reset-timer))
-
-(defun js2-reparse (&optional force)
-  "Re-parse current buffer after user finishes some data entry.
-If we get any user input while parsing, including cursor motion,
-we discard the parse and reschedule it.  If FORCE is nil, then the
-buffer will only rebuild its `js2-mode-ast' if the buffer is dirty."
-  (let (time
-        interrupted-p
-        (js2-compiler-strict-mode js2-mode-show-strict-warnings))
-    (unless js2-mode-parsing
-      (setq js2-mode-parsing t)
-      (unwind-protect
-          (when (or js2-mode-buffer-dirty-p force)
-            (js2-remove-overlays)
-            (setq js2-mode-buffer-dirty-p nil
-                  js2-mode-fontifications nil
-                  js2-mode-deferred-properties nil)
-            (if js2-mode-verbose-parse-p
-                (message "parsing..."))
-            (setq time
-                  (js2-time
-                   (setq interrupted-p
-                         (catch 'interrupted
-                           (js2-parse)
-                           (with-silent-modifications
-                             ;; if parsing is interrupted, comments and regex
-                             ;; literals stay ignored by `parse-partial-sexp'
-                             (remove-text-properties (point-min) (point-max)
-                                                     '(syntax-table))
-                             (js2-mode-apply-deferred-properties)
-                             (js2-mode-remove-suppressed-warnings)
-                             (js2-mode-show-warnings)
-                             (js2-mode-show-errors)
-                             (if (>= js2-highlight-level 1)
-                                 (js2-highlight-jsdoc js2-mode-ast)))
-                           nil))))
-            (if interrupted-p
-                (progn
-                  ;; unfinished parse => try again
-                  (setq js2-mode-buffer-dirty-p t)
-                  (js2-mode-reset-timer))
-              (if js2-mode-verbose-parse-p
-                  (message "Parse time: %s" time))))
-        (setq js2-mode-parsing nil)
-        (unless interrupted-p
-          (setq js2-mode-parse-timer nil))))))
-
-;; We bound it to [mouse-1] previously.  But the signature of
-;; mouse-set-point changed around 24.4, so it's kind of hard to keep
-;; it working in 24.1-24.3.  Since the command is not hugely
-;; important, we removed the binding (#356).  Maybe we'll bring it
-;; back when supporting <24.4 is not a goal anymore.
-(defun js2-mode-show-node (event &optional promote-to-region)
-  "Debugging aid:  highlight selected AST node on mouse click."
-  (interactive "e\np")
-  (mouse-set-point event promote-to-region)
-  (when js2-mode-show-overlay
-    (let ((node (js2-node-at-point))
-          beg end)
-      (if (null node)
-          (message "No node found at location %s" (point))
-        (setq beg (js2-node-abs-pos node)
-              end (+ beg (js2-node-len node)))
-        (if js2-mode-node-overlay
-            (move-overlay js2-mode-node-overlay beg end)
-          (setq js2-mode-node-overlay (make-overlay beg end))
-          (overlay-put js2-mode-node-overlay 'font-lock-face 'highlight))
-        (with-silent-modifications
-          (if (fboundp 'cursor-sensor-mode)
-              (put-text-property beg end 'cursor-sensor-functions
-                                 '(js2-mode-hide-overlay))
-            (put-text-property beg end 'point-left #'js2-mode-hide-overlay)))
-        (message "%s, parent: %s"
-                 (js2-node-short-name node)
-                 (if (js2-node-parent node)
-                     (js2-node-short-name (js2-node-parent node))
-                   "nil"))))))
-
-(defun js2-mode-hide-overlay (&optional arg1 arg2 _arg3)
-  "Remove the debugging overlay when point moves.
-ARG1, ARG2 and ARG3 have different values depending on whether this function
-was found on `point-left' or in `cursor-sensor-functions'."
-  (when js2-mode-node-overlay
-    (let ((beg (overlay-start js2-mode-node-overlay))
-          (end (overlay-end js2-mode-node-overlay))
-          (p2 (if (windowp arg1)
-                  ;; Called from cursor-sensor-functions.
-                  (window-point arg1)
-                ;; Called from point-left.
-                arg2)))
-      ;; Sometimes we're called spuriously.
-      (unless (and p2
-                   (>= p2 beg)
-                   (<= p2 end))
-        (with-silent-modifications
-          (remove-text-properties beg end
-                                  '(point-left nil cursor-sensor-functions)))
-        (delete-overlay js2-mode-node-overlay)
-        (setq js2-mode-node-overlay nil)))))
-
-(defun js2-mode-reset ()
-  "Debugging helper:  reset everything."
-  (interactive)
-  (js2-mode-exit)
-  (js2-mode))
-
-(defun js2-mode-show-warn-or-err (e face)
-  "Highlight a warning or error E with FACE.
-E is a list of ((MSG-KEY MSG-ARG) BEG LEN OVERRIDE-FACE).
-The last element is optional.  When present, use instead of FACE."
-  (let* ((key (cl-first e))
-         (beg (cl-second e))
-         (end (+ beg (cl-third e)))
-         ;; Don't inadvertently go out of bounds.
-         (beg (max (point-min) (min beg (point-max))))
-         (end (max (point-min) (min end (point-max))))
-         (ovl (make-overlay beg end)))
-    ;; FIXME: Why a mix of overlays and text-properties?
-    (overlay-put ovl 'font-lock-face (or (cl-fourth e) face))
-    (overlay-put ovl 'js2-error t)
-    (put-text-property beg end 'help-echo (js2-get-msg key))
-    (if (fboundp 'cursor-sensor-mode)
-        (put-text-property beg end 'cursor-sensor-functions '(js2-echo-error))
-      (put-text-property beg end 'point-entered #'js2-echo-error))))
-
-(defun js2-remove-overlays ()
-  "Remove overlays from buffer that have a `js2-error' property."
-  (let ((beg (point-min))
-        (end (point-max)))
-    (save-excursion
-      (dolist (o (overlays-in beg end))
-        (when (overlay-get o 'js2-error)
-          (delete-overlay o))))))
-
-(defun js2-mode-apply-deferred-properties ()
-  "Apply fontifications and other text properties recorded during parsing."
-  (when (cl-plusp js2-highlight-level)
-    ;; We defer clearing faces as long as possible to eliminate flashing.
-    (js2-clear-face (point-min) (point-max))
-    ;; Have to reverse the recorded fontifications list so that errors
-    ;; and warnings overwrite the normal fontifications.
-    (dolist (f (nreverse js2-mode-fontifications))
-      (put-text-property (cl-first f) (cl-second f) 'font-lock-face (cl-third f)))
-    (setq js2-mode-fontifications nil))
-  (dolist (p js2-mode-deferred-properties)
-    (apply #'put-text-property p))
-  (setq js2-mode-deferred-properties nil))
-
-(defun js2-mode-show-errors ()
-  "Highlight syntax errors."
-  (when js2-mode-show-parse-errors
-    (dolist (e (js2-ast-root-errors js2-mode-ast))
-      (js2-mode-show-warn-or-err e 'js2-error))))
-
-(defun js2-mode-remove-suppressed-warnings ()
-  "Take suppressed warnings out of the AST warnings list.
-This ensures that the counts and `next-error' are correct."
-  (setf (js2-ast-root-warnings js2-mode-ast)
-        (js2-delete-if
-         (lambda (e)
-           (let ((key (caar e)))
-             (or
-              (and (not js2-strict-trailing-comma-warning)
-                   (string-match "trailing\\.comma" key))
-              (and (not js2-strict-cond-assign-warning)
-                   (string= key "msg.equal.as.assign"))
-              (and js2-missing-semi-one-line-override
-                   (string= key "msg.missing.semi")
-                   (let* ((beg (cl-second e))
-                          (node (js2-node-at-point beg))
-                          (fn (js2-mode-find-parent-fn node))
-                          (body (and fn (js2-function-node-body fn)))
-                          (lc (and body (js2-node-abs-pos body)))
-                          (rc (and lc (+ lc (js2-node-len body)))))
-                     (and fn
-                          (or (null body)
-                              (save-excursion
-                                (goto-char beg)
-                                (and (js2-same-line lc)
-                                     (js2-same-line rc))))))))))
-         (js2-ast-root-warnings js2-mode-ast))))
-
-(defun js2-mode-show-warnings ()
-  "Highlight strict-mode warnings."
-  (when js2-mode-show-strict-warnings
-    (dolist (e (js2-ast-root-warnings js2-mode-ast))
-      (js2-mode-show-warn-or-err e 'js2-warning))))
-
-(defun js2-echo-error (arg1 arg2 &optional _arg3)
-  "Called by point-motion hooks.
-ARG1, ARG2 and ARG3 have different values depending on whether this function
-was found on `point-entered' or in `cursor-sensor-functions'."
-  (let* ((new-point (if (windowp arg1)
-                        ;; Called from cursor-sensor-functions.
-                        (window-point arg1)
-                      ;; Called from point-left.
-                      arg2))
-         (msg (get-text-property new-point 'help-echo)))
-    (when (and (stringp msg)
-               (not (active-minibuffer-window))
-               (not (current-message)))
-      (message msg))))
-
-(defun js2-line-break (&optional _soft)
-  "Break line at point and indent, continuing comment if within one.
-If inside a string, and `js2-concat-multiline-strings' is not
-nil, turn it into concatenation."
-  (interactive)
-  (let ((parse-status (syntax-ppss)))
-    (cond
-     ;; Check if we're inside a string.
-     ((nth 3 parse-status)
-      (if js2-concat-multiline-strings
-          (js2-mode-split-string parse-status)
-        (insert "\n")))
-     ;; Check if inside a block comment.
-     ((nth 4 parse-status)
-      (js2-mode-extend-comment (nth 8 parse-status)))
-     (t
-      (newline-and-indent)))))
-
-(defun js2-mode-split-string (parse-status)
-  "Turn a newline in mid-string into a string concatenation.
-PARSE-STATUS is as documented in `parse-partial-sexp'."
-  (let* ((quote-char (nth 3 parse-status))
-         (at-eol (eq js2-concat-multiline-strings 'eol)))
-    (insert quote-char)
-    (insert (if at-eol " +\n" "\n"))
-    (unless at-eol
-      (insert "+ "))
-    (js2-indent-line)
-    (insert quote-char)
-    (when (eolp)
-      (insert quote-char)
-      (backward-char 1))))
-
-(defun js2-mode-extend-comment (start-pos)
-  "Indent the line and, when inside a comment block, add comment prefix."
-  (let (star single col first-line needs-close)
-    (save-excursion
-      (back-to-indentation)
-      (when (< (point) start-pos)
-        (goto-char start-pos))
-      (cond
-       ((looking-at "\\*[^/]")
-        (setq star t
-              col (current-column)))
-       ((looking-at "/\\*")
-        (setq star t
-              first-line t
-              col (1+ (current-column))))
-       ((looking-at "//")
-        (setq single t
-              col (current-column)))))
-    ;; Heuristic for whether we need to close the comment:
-    ;; if we've got a parse error here, assume it's an unterminated
-    ;; comment.
-    (setq needs-close
-          (or
-           (get-char-property (1- (point)) 'js2-error)
-           ;; The heuristic above doesn't work well when we're
-           ;; creating a comment and there's another one downstream,
-           ;; as our parser thinks this one ends at the end of the
-           ;; next one.  (You can have a /* inside a js block comment.)
-           ;; So just close it if the next non-ws char isn't a *.
-           (and first-line
-                (eolp)
-                (save-excursion
-                  (skip-chars-forward " \t\r\n")
-                  (not (eq (char-after) ?*))))))
-    (delete-horizontal-space)
-    (insert "\n")
-    (cond
-     (star
-      (indent-to col)
-      (insert "* ")
-      (if (and first-line needs-close)
-          (save-excursion
-            (insert "\n")
-            (indent-to col)
-            (insert "*/"))))
-     (single
-      (indent-to col)
-      (insert "// ")))
-    ;; Don't need to extend the comment after all.
-    (js2-indent-line)))
-
-(defun js2-beginning-of-line ()
-  "Toggle point between bol and first non-whitespace char in line.
-Also moves past comment delimiters when inside comments."
-  (interactive)
-  (let (node)
-    (cond
-     ((bolp)
-      (back-to-indentation))
-     ((looking-at "//")
-      (skip-chars-forward "/ \t"))
-     ((and (eq (char-after) ?*)
-           (setq node (js2-comment-at-point))
-           (memq (js2-comment-node-format node) '(jsdoc block))
-           (save-excursion
-             (skip-chars-backward " \t")
-             (bolp)))
-      (skip-chars-forward "\* \t"))
-     (t
-      (goto-char (point-at-bol))))))
-
-(defun js2-end-of-line ()
-  "Toggle point between eol and last non-whitespace char in line."
-  (interactive)
-  (if (eolp)
-      (skip-chars-backward " \t")
-    (goto-char (point-at-eol))))
-
-(defun js2-mode-wait-for-parse (callback)
-  "Invoke CALLBACK when parsing is finished.
-If parsing is already finished, calls CALLBACK immediately."
-  (if (not js2-mode-buffer-dirty-p)
-      (funcall callback)
-    (push callback js2-mode-pending-parse-callbacks)
-    (add-hook 'js2-parse-finished-hook #'js2-mode-parse-finished)))
-
-(defun js2-mode-parse-finished ()
-  "Invoke callbacks in `js2-mode-pending-parse-callbacks'."
-  ;; We can't let errors propagate up, since it prevents the
-  ;; `js2-parse' method from completing normally and returning
-  ;; the ast, which makes things mysteriously not work right.
-  (unwind-protect
-      (dolist (cb js2-mode-pending-parse-callbacks)
-        (condition-case err
-            (funcall cb)
-          (error (message "%s" err))))
-    (setq js2-mode-pending-parse-callbacks nil)))
-
-(defun js2-mode-flag-region (from to flag)
-  "Hide or show text from FROM to TO, according to FLAG.
-If FLAG is nil then text is shown, while if FLAG is t the text is hidden.
-Returns the created overlay if FLAG is non-nil."
-  (remove-overlays from to 'invisible 'js2-outline)
-  (when flag
-    (let ((o (make-overlay from to)))
-      (overlay-put o 'invisible 'js2-outline)
-      (overlay-put o 'isearch-open-invisible
-                   'js2-isearch-open-invisible)
-      o)))
-
-;; Function to be set as an outline-isearch-open-invisible' property
-;; to the overlay that makes the outline invisible (see
-;; `js2-mode-flag-region').
-(defun js2-isearch-open-invisible (_overlay)
-  ;; We rely on the fact that isearch places point on the matched text.
-  (js2-mode-show-element))
-
-(defun js2-mode-invisible-overlay-bounds (&optional pos)
-  "Return cons cell of bounds of folding overlay at POS.
-Returns nil if not found."
-  (let ((overlays (overlays-at (or pos (point))))
-        o)
-    (while (and overlays
-                (not o))
-      (if (overlay-get (car overlays) 'invisible)
-          (setq o (car overlays))
-        (setq overlays (cdr overlays))))
-    (if o
-        (cons (overlay-start o) (overlay-end o)))))
-
-(defun js2-mode-function-at-point (&optional pos)
-  "Return the innermost function node enclosing current point.
-Returns nil if point is not in a function."
-  (let ((node (js2-node-at-point pos)))
-    (while (and node (not (js2-function-node-p node)))
-      (setq node (js2-node-parent node)))
-    (if (js2-function-node-p node)
-        node)))
-
-(defun js2-mode-toggle-element ()
-  "Hide or show the foldable element at the point."
-  (interactive)
-  (let (comment fn pos)
-    (save-excursion
-      (cond
-       ;; /* ... */ comment?
-       ((js2-block-comment-p (setq comment (js2-comment-at-point)))
-        (if (js2-mode-invisible-overlay-bounds
-             (setq pos (+ 3 (js2-node-abs-pos comment))))
-            (progn
-              (goto-char pos)
-              (js2-mode-show-element))
-          (js2-mode-hide-element)))
-       ;; //-comment?
-       ((save-excursion
-          (back-to-indentation)
-          (looking-at js2-mode-//-comment-re))
-        (js2-mode-toggle-//-comment))
-       ;; function?
-       ((setq fn (js2-mode-function-at-point))
-        (setq pos (and (js2-function-node-body fn)
-                       (js2-node-abs-pos (js2-function-node-body fn))))
-        (goto-char (1+ pos))
-        (if (js2-mode-invisible-overlay-bounds)
-            (js2-mode-show-element)
-          (js2-mode-hide-element)))
-       (t
-        (message "Nothing at point to hide or show"))))))
-
-(defun js2-mode-hide-element ()
-  "Fold/hide contents of a block, showing ellipses.
-Show the hidden text with \\[js2-mode-show-element]."
-  (interactive)
-  (if js2-mode-buffer-dirty-p
-      (js2-mode-wait-for-parse #'js2-mode-hide-element))
-  (let (node body beg end)
-    (cond
-     ((js2-mode-invisible-overlay-bounds)
-      (message "already hidden"))
-     (t
-      (setq node (js2-node-at-point))
-      (cond
-       ((js2-block-comment-p node)
-        (js2-mode-hide-comment node))
-       (t
-        (while (and node (not (js2-function-node-p node)))
-          (setq node (js2-node-parent node)))
-        (if (and node
-                 (setq body (js2-function-node-body node)))
-            (progn
-              (setq beg (js2-node-abs-pos body)
-                    end (+ beg (js2-node-len body)))
-              (js2-mode-flag-region (1+ beg) (1- end) 'hide))
-          (message "No collapsable element found at point"))))))))
-
-(defun js2-mode-show-element ()
-  "Show the hidden element at current point."
-  (interactive)
-  (let ((bounds (js2-mode-invisible-overlay-bounds)))
-    (if bounds
-        (js2-mode-flag-region (car bounds) (cdr bounds) nil)
-      (message "Nothing to un-hide"))))
-
-(defun js2-mode-show-all ()
-  "Show all of the text in the buffer."
-  (interactive)
-  (js2-mode-flag-region (point-min) (point-max) nil))
-
-(defun js2-mode-toggle-hide-functions ()
-  (interactive)
-  (if js2-mode-functions-hidden
-      (js2-mode-show-functions)
-    (js2-mode-hide-functions)))
-
-(defun js2-mode-hide-functions ()
-  "Hides all non-nested function bodies in the buffer.
-Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element]
-to open an individual entry."
-  (interactive)
-  (if js2-mode-buffer-dirty-p
-      (js2-mode-wait-for-parse #'js2-mode-hide-functions))
-  (if (null js2-mode-ast)
-      (message "Oops - parsing failed")
-    (setq js2-mode-functions-hidden t)
-    (js2-visit-ast js2-mode-ast #'js2-mode-function-hider)))
-
-(defun js2-mode-function-hider (n endp)
-  (when (not endp)
-    (let ((tt (js2-node-type n))
-          body beg end)
-      (cond
-       ((and (= tt js2-FUNCTION)
-             (setq body (js2-function-node-body n)))
-        (setq beg (js2-node-abs-pos body)
-              end (+ beg (js2-node-len body)))
-        (js2-mode-flag-region (1+ beg) (1- end) 'hide)
-        nil)   ; don't process children of function
-       (t
-        t))))) ; keep processing other AST nodes
-
-(defun js2-mode-show-functions ()
-  "Un-hide any folded function bodies in the buffer."
-  (interactive)
-  (setq js2-mode-functions-hidden nil)
-  (save-excursion
-    (goto-char (point-min))
-    (while (/= (goto-char (next-overlay-change (point)))
-               (point-max))
-      (dolist (o (overlays-at (point)))
-        (when (and (overlay-get o 'invisible)
-                   (not (overlay-get o 'comment)))
-          (js2-mode-flag-region (overlay-start o) (overlay-end o) nil))))))
-
-(defun js2-mode-hide-comment (n)
-  (let* ((head (if (eq (js2-comment-node-format n) 'jsdoc)
-                   3  ; /**
-                 2))  ; /*
-         (beg (+ (js2-node-abs-pos n) head))
-         (end (- (+ beg (js2-node-len n)) head 2))
-         (o (js2-mode-flag-region beg end 'hide)))
-    (overlay-put o 'comment t)))
-
-(defun js2-mode-toggle-hide-comments ()
-  "Folds all block comments in the buffer.
-Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element]
-to open an individual entry."
-  (interactive)
-  (if js2-mode-comments-hidden
-      (js2-mode-show-comments)
-    (js2-mode-hide-comments)))
-
-(defun js2-mode-hide-comments ()
-  (interactive)
-  (if js2-mode-buffer-dirty-p
-      (js2-mode-wait-for-parse #'js2-mode-hide-comments))
-  (if (null js2-mode-ast)
-      (message "Oops - parsing failed")
-    (setq js2-mode-comments-hidden t)
-    (dolist (n (js2-ast-root-comments js2-mode-ast))
-      (when (js2-block-comment-p n)
-        (js2-mode-hide-comment n)))
-    (js2-mode-hide-//-comments)))
-
-(defun js2-mode-extend-//-comment (direction)
-  "Find start or end of a block of similar //-comment lines.
-DIRECTION is -1 to look back, 1 to look forward.
-INDENT is the indentation level to match.
-Returns the end-of-line position of the furthest adjacent
-//-comment line with the same indentation as the current line.
-If there is no such matching line, returns current end of line."
-  (let ((pos (point-at-eol))
-        (indent (current-indentation)))
-    (save-excursion
-      (while (and (zerop (forward-line direction))
-                  (looking-at js2-mode-//-comment-re)
-                  (eq indent (length (match-string 1))))
-        (setq pos (point-at-eol)))
-      pos)))
-
-(defun js2-mode-hide-//-comments ()
-  "Fold adjacent 1-line comments, showing only snippet of first one."
-  (let (beg end)
-    (save-excursion
-      (goto-char (point-min))
-      (while (re-search-forward js2-mode-//-comment-re nil t)
-        (setq beg (point)
-              end (js2-mode-extend-//-comment 1))
-        (unless (eq beg end)
-          (overlay-put (js2-mode-flag-region beg end 'hide)
-                       'comment t))
-        (goto-char end)
-        (forward-char 1)))))
-
-(defun js2-mode-toggle-//-comment ()
-  "Fold or un-fold any multi-line //-comment at point.
-Caller should have determined that this line starts with a //-comment."
-  (let* ((beg (point-at-eol))
-         (end beg))
-    (save-excursion
-      (goto-char end)
-      (if (js2-mode-invisible-overlay-bounds)
-          (js2-mode-show-element)
-        ;; else hide the comment
-        (setq beg (js2-mode-extend-//-comment -1)
-              end (js2-mode-extend-//-comment 1))
-        (unless (eq beg end)
-          (overlay-put (js2-mode-flag-region beg end 'hide)
-                       'comment t))))))
-
-(defun js2-mode-show-comments ()
-  "Un-hide any hidden comments, leaving other hidden elements alone."
-  (interactive)
-  (setq js2-mode-comments-hidden nil)
-  (save-excursion
-    (goto-char (point-min))
-    (while (/= (goto-char (next-overlay-change (point)))
-               (point-max))
-      (dolist (o (overlays-at (point)))
-        (when (overlay-get o 'comment)
-          (js2-mode-flag-region (overlay-start o) (overlay-end o) nil))))))
-
-(defun js2-mode-display-warnings-and-errors ()
-  "Turn on display of warnings and errors."
-  (interactive)
-  (setq js2-mode-show-parse-errors t
-        js2-mode-show-strict-warnings t)
-  (js2-reparse 'force))
-
-(defun js2-mode-hide-warnings-and-errors ()
-  "Turn off display of warnings and errors."
-  (interactive)
-  (setq js2-mode-show-parse-errors nil
-        js2-mode-show-strict-warnings nil)
-  (js2-reparse 'force))
-
-(defun js2-mode-toggle-warnings-and-errors ()
-  "Toggle the display of warnings and errors.
-Some users don't like having warnings/errors reported while they type."
-  (interactive)
-  (setq js2-mode-show-parse-errors (not js2-mode-show-parse-errors)
-        js2-mode-show-strict-warnings (not js2-mode-show-strict-warnings))
-  (if (called-interactively-p 'any)
-      (message "warnings and errors %s"
-               (if js2-mode-show-parse-errors
-                   "enabled"
-                 "disabled")))
-  (js2-reparse 'force))
-
-(defun js2-mode-customize ()
-  (interactive)
-  (customize-group 'js2-mode))
-
-(defun js2-mode-forward-sexp (&optional arg)
-  "Move forward across one statement or balanced expression.
-With ARG, do it that many times.  Negative arg -N means
-move backward across N balanced expressions."
-  (interactive "p")
-  (setq arg (or arg 1))
-  (save-restriction
-    (widen) ;; `blink-matching-open' calls `narrow-to-region'
-    (js2-reparse)
-    (let (forward-sexp-function
-          node (start (point)) pos lp rp child)
-      (cond
-       ((js2-string-node-p (js2-node-at-point))
-        (forward-sexp arg))
-       ;; backward-sexp
-       ;; could probably make this better for some cases:
-       ;;  - if in statement block (e.g. function body), go to parent
-       ;;  - infix exprs like (foo in bar) - maybe go to beginning
-       ;;    of infix expr if in the right-side expression?
-       ((and arg (cl-minusp arg))
-        (dotimes (_ (- arg))
-          (js2-backward-sws)
-          (forward-char -1)   ; Enter the node we backed up to.
-          (when (setq node (js2-node-at-point (point) t))
-            (setq pos (js2-node-abs-pos node))
-            (let ((parens (js2-mode-forward-sexp-parens node pos)))
-              (setq lp (car parens)
-                    rp (cdr parens)))
-            (when (and lp (> start lp))
-              (if (and rp (<= start rp))
-                  ;; Between parens, check if there's a child node we can jump.
-                  (when (setq child (js2-node-closest-child node (point) lp t))
-                    (setq pos (js2-node-abs-pos child)))
-                ;; Before both parens.
-                (setq pos lp)))
-            (let ((state (parse-partial-sexp start pos)))
-              (goto-char (if (not (zerop (car state)))
-                             ;; Stumble at the unbalanced paren if < 0, or
-                             ;; jump a bit further if > 0.
-                             (scan-sexps start -1)
-                           pos))))
-          (unless pos (goto-char (point-min)))))
-       (t
-        ;; forward-sexp
-        (dotimes (_ arg)
-          (js2-forward-sws)
-          (when (setq node (js2-node-at-point (point) t))
-            (setq pos (js2-node-abs-pos node))
-            (let ((parens (js2-mode-forward-sexp-parens node pos)))
-              (setq lp (car parens)
-                    rp (cdr parens)))
-            (or
-             (when (and rp (<= start rp))
-               (if (> start lp)
-                   (when (setq child (js2-node-closest-child node (point) rp))
-                     (setq pos (js2-node-abs-end child)))
-                 (setq pos (1+ rp))))
-             ;; No parens or child nodes, looks for the end of the current node.
-             (cl-incf pos (js2-node-len
-                           (if (js2-expr-stmt-node-p (js2-node-parent node))
-                               ;; Stop after the semicolon.
-                               (js2-node-parent node)
-                             node))))
-            (let ((state (save-excursion (parse-partial-sexp start pos))))
-              (goto-char (if (not (zerop (car state)))
-                             (scan-sexps start 1)
-                           pos))))
-          (unless pos (goto-char (point-max)))))))))
-
-(defun js2-mode-forward-sexp-parens (node abs-pos)
-  "Return a cons cell with positions of main parens in NODE."
-  (cond
-   ((or (js2-array-node-p node)
-        (js2-object-node-p node)
-        (js2-comp-node-p node)
-        (memq (aref node 0) '(cl-struct-js2-block-node cl-struct-js2-scope)))
-    (cons abs-pos (+ abs-pos (js2-node-len node) -1)))
-   ((js2-paren-expr-node-p node)
-    (let ((lp (js2-node-lp node))
-          (rp (js2-node-rp node)))
-      (cons (when lp (+ abs-pos lp))
-            (when rp (+ abs-pos rp)))))))
-
-(defun js2-node-closest-child (parent point limit &optional before)
-  (let* ((parent-pos (js2-node-abs-pos parent))
-         (rpoint (- point parent-pos))
-         (rlimit (- limit parent-pos))
-         (min (min rpoint rlimit))
-         (max (max rpoint rlimit))
-         found)
-    (catch 'done
-      (js2-visit-ast
-       parent
-       (lambda (node _end-p)
-         (if (eq node parent)
-             t
-           (let ((pos (js2-node-pos node)) ;; Both relative values.
-                 (end (+ (js2-node-pos node) (js2-node-len node))))
-             (when (and (>= pos min) (<= end max)
-                        (if before (< pos rpoint) (> end rpoint)))
-               (setq found node))
-             (when (> end rpoint)
-               (throw 'done nil)))
-           nil))))
-    found))
-
-(defun js2-errors ()
-  "Return a list of errors found."
-  (and js2-mode-ast
-       (js2-ast-root-errors js2-mode-ast)))
-
-(defun js2-warnings ()
-  "Return a list of warnings found."
-  (and js2-mode-ast
-       (js2-ast-root-warnings js2-mode-ast)))
-
-(defun js2-have-errors-p ()
-  "Return non-nil if any parse errors or warnings were found."
-  (or (js2-errors) (js2-warnings)))
-
-(defun js2-errors-and-warnings ()
-  "Return a copy of the concatenated errors and warnings lists.
-They are appended:  first the errors, then the warnings.
-Entries are of the form (MSG BEG END)."
-  (when js2-mode-ast
-    (append (js2-ast-root-errors js2-mode-ast)
-            (copy-sequence (js2-ast-root-warnings js2-mode-ast)))))
-
-(defun js2-next-error (&optional arg reset)
-  "Move to next parse error.
-Typically invoked via \\[next-error].
-ARG is the number of errors, forward or backward, to move.
-RESET means start over from the beginning."
-  (interactive "p")
-  (if (not (or (js2-errors) (js2-warnings)))
-      (message "No errors")
-    (when reset
-      (goto-char (point-min)))
-    (let* ((errs (js2-errors-and-warnings))
-           (continue t)
-           (start (point))
-           (count (or arg 1))
-           (backward (cl-minusp count))
-           (sorter (if backward '> '<))
-           (stopper (if backward '< '>))
-           (count (abs count))
-           all-errs err)
-      ;; Sort by start position.
-      (setq errs (sort errs (lambda (e1 e2)
-                              (funcall sorter (cl-second e1) (cl-second e2))))
-            all-errs errs)
-      ;; Find nth error with pos > start.
-      (while (and errs continue)
-        (when (funcall stopper (cl-cadar errs) start)
-          (setq err (car errs))
-          (if (zerop (cl-decf count))
-              (setq continue nil)))
-        (setq errs (cdr errs)))
-      ;; Clear for `js2-echo-error'.
-      (message nil)
-      (if err
-          (goto-char (cl-second err))
-        ;; Wrap around to first error.
-        (goto-char (cl-second (car all-errs)))
-        ;; If we were already on it, echo msg again.
-        (if (= (point) start)
-            (js2-echo-error (point) (point)))))))
-
-(defun js2-down-mouse-3 ()
-  "Make right-click move the point to the click location.
-This makes right-click context menu operations a bit more intuitive.
-The point will not move if the region is active, however, to avoid
-destroying the region selection."
-  (interactive)
-  (when (and js2-move-point-on-right-click
-             (not mark-active))
-    (let ((e last-input-event))
-      (ignore-errors
-        (goto-char (cl-cadadr e))))))
-
-(defun js2-mode-create-imenu-index ()
-  "Returns an alist for `imenu--index-alist'. Returns nil on first
-scan if buffer size > `imenu-auto-rescan-maxout'."
-  (when (and (not js2-mode-ast)
-             (<= (buffer-size) imenu-auto-rescan-maxout))
-      (js2-reparse))
-  (when js2-mode-ast
-    ;; if we have an ast but no recorder, they're requesting a rescan
-    (unless js2-imenu-recorder
-      (js2-reparse 'force))
-    (prog1
-        (js2-build-imenu-index)
-      (setq js2-imenu-recorder nil
-            js2-imenu-function-map nil))))
-
-(defun js2-mode-find-tag ()
-  "Replacement for `find-tag-default'.
-`find-tag-default' returns a ridiculous answer inside comments."
-  (let (beg end)
-    (save-excursion
-      (if (looking-at "\\_>")
-          (setq beg (progn (forward-symbol -1) (point))
-                end (progn (forward-symbol 1) (point)))
-        (setq beg (progn (forward-symbol 1) (point))
-              end (progn (forward-symbol -1) (point))))
-      (replace-regexp-in-string
-       "[\"']" ""
-       (buffer-substring-no-properties beg end)))))
-
-(defun js2-mode-forward-sibling ()
-  "Move to the end of the sibling following point in parent.
-Returns non-nil if successful, or nil if there was no following sibling."
-  (let* ((node (js2-node-at-point))
-         (parent (js2-mode-find-enclosing-fn node))
-         sib)
-    (when (setq sib (js2-node-find-child-after (point) parent))
-      (goto-char (+ (js2-node-abs-pos sib)
-                    (js2-node-len sib))))))
-
-(defun js2-mode-backward-sibling ()
-  "Move to the beginning of the sibling node preceding point in parent.
-Parent is defined as the enclosing script or function."
-  (let* ((node (js2-node-at-point))
-         (parent (js2-mode-find-enclosing-fn node))
-         sib)
-    (when (setq sib (js2-node-find-child-before (point) parent))
-      (goto-char (js2-node-abs-pos sib)))))
-
-(defun js2-beginning-of-defun (&optional arg)
-  "Go to line on which current function starts, and return t on success.
-If we're not in a function or already at the beginning of one, go
-to beginning of previous script-level element.
-With ARG N, do that N times. If N is negative, move forward."
-  (setq arg (or arg 1))
-  (if (cl-plusp arg)
-      (let ((parent (js2-node-parent-script-or-fn (js2-node-at-point))))
-        (when (cond
-               ((js2-function-node-p parent)
-                (goto-char (js2-node-abs-pos parent)))
-               (t
-                (js2-mode-backward-sibling)))
-          (if (> arg 1)
-              (js2-beginning-of-defun (1- arg))
-            t)))
-    (when (js2-end-of-defun)
-      (js2-beginning-of-defun (if (>= arg -1) 1 (1+ arg))))))
-
-(defun js2-end-of-defun ()
-  "Go to the char after the last position of the current function
-or script-level element."
-  (let* ((node (js2-node-at-point))
-         (parent (or (and (js2-function-node-p node) node)
-                     (js2-node-parent-script-or-fn node)))
-         script)
-    (unless (js2-function-node-p parent)
-      ;; Use current script-level node, or, if none, the next one.
-      (setq script (or parent node)
-            parent (js2-node-find-child-before (point) script))
-      (when (or (null parent)
-                (>= (point) (+ (js2-node-abs-pos parent)
-                               (js2-node-len parent))))
-        (setq parent (js2-node-find-child-after (point) script))))
-    (when parent
-      (goto-char (+ (js2-node-abs-pos parent)
-                    (js2-node-len parent))))))
-
-(defun js2-mark-defun (&optional allow-extend)
-  "Put mark at end of this function, point at beginning.
-The function marked is the one that contains point.
-
-Interactively, if this command is repeated,
-or (in Transient Mark mode) if the mark is active,
-it marks the next defun after the ones already marked."
-  (interactive "p")
-  (let (extended)
-    (when (and allow-extend
-               (or (and (eq last-command this-command) (mark t))
-                   (and transient-mark-mode mark-active)))
-      (let ((sib (save-excursion
-                   (goto-char (mark))
-                   (if (js2-mode-forward-sibling)
-                       (point)))))
-        (if sib
-            (progn
-              (set-mark sib)
-              (setq extended t))
-          ;; no more siblings - try extending to enclosing node
-          (goto-char (mark t)))))
-   (when (not extended)
-     (let ((node (js2-node-at-point (point) t)) ; skip comments
-           ast fn stmt parent beg end)
-       (when (js2-ast-root-p node)
-         (setq ast node
-               node (or (js2-node-find-child-after (point) node)
-                        (js2-node-find-child-before (point) node))))
-       ;; only mark whole buffer if we can't find any children
-       (if (null node)
-           (setq node ast))
-       (if (js2-function-node-p node)
-           (setq parent node)
-         (setq fn (js2-mode-find-enclosing-fn node)
-               stmt (if (or (null fn)
-                            (js2-ast-root-p fn))
-                        (js2-mode-find-first-stmt node))
-               parent (or stmt fn)))
-       (setq beg (js2-node-abs-pos parent)
-             end (+ beg (js2-node-len parent)))
-       (push-mark beg)
-       (goto-char end)
-       (exchange-point-and-mark)))))
-
-(defun js2-narrow-to-defun ()
-  "Narrow to the function enclosing point."
-  (interactive)
-  (let* ((node (js2-node-at-point (point) t))  ; skip comments
-         (fn (if (js2-script-node-p node)
-                 node
-               (js2-mode-find-enclosing-fn node)))
-         (beg (js2-node-abs-pos fn)))
-    (unless (js2-ast-root-p fn)
-      (narrow-to-region beg (+ beg (js2-node-len fn))))))
-
-(defun js2-jump-to-definition (&optional arg)
-  "Jump to the definition of an object's property, variable or function."
-  (interactive "P")
-  (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)
-                    (reverse (let ((temp (js2-compute-nested-prop-get parent)))
-                               (cl-loop for n in temp
-                                        with result = '()
-                                        do (push n result)
-                                        until (equal node n)
-                                        finally return result)))))
-         node-init)
-    (unless (and (js2-name-node-p node)
-                 (not (js2-var-init-node-p parent))
-                 (not (js2-function-node-p parent)))
-      (error "Node is not a supported jump node"))
-    (push (or (and names (pop names))
-              (unless (and (js2-object-prop-node-p parent)
-                           (eq node (js2-object-prop-node-left parent))
-                           (not (js2-node-get-prop parent 'SHORTHAND)))
-                node)
-              (error "Node is not a supported jump node")) names)
-    (setq node-init (js2-search-scope node names))
-
-    ;; todo: display list of results in buffer
-    ;; todo: group found references by buffer
-    (unless node-init
-      (switch-to-buffer
-       (catch 'found
-         (unless arg
-           (mapc (lambda (b)
-                   (with-current-buffer b
-                     (when (derived-mode-p 'js2-mode)
-                       (setq node-init (js2-search-scope js2-mode-ast names))
-                       (if node-init
-                           (throw 'found b)))))
-                 (buffer-list)))
-         nil)))
-    (setq node-init (if (listp node-init) (car node-init) node-init))
-    (unless node-init
-      (pop-tag-mark)
-      (error "No jump location found"))
-    (goto-char (js2-node-abs-pos node-init))))
-
-(defun js2-search-object (node name-node)
-  "Check if object NODE contains element with NAME-NODE."
-  (cl-assert (js2-object-node-p node))
-  ;; Only support name-node and nodes for the time being
-  (cl-loop for elem in (js2-object-node-elems node)
-           for left = (js2-object-prop-node-left elem)
-           if (or (and (js2-name-node-p left)
-                       (equal (js2-name-node-name name-node)
-                              (js2-name-node-name left)))
-                  (and (js2-string-node-p left)
-                       (string= (js2-name-node-name name-node)
-                                (js2-string-node-value left))))
-           return elem))
-
-(defun js2-search-object-for-prop (object prop-names)
-  "Return node in OBJECT that matches PROP-NAMES or nil.
-PROP-NAMES is a list of values representing a path to a value in OBJECT.
-i.e. ('name' 'value') = {name : { value: 3}}"
-  (let (node
-        (temp-object object)
-        (temp t) ;temporay node
-        (names prop-names))
-    (while (and temp names (js2-object-node-p temp-object))
-      (setq temp (js2-search-object temp-object (pop names)))
-      (and (setq node temp)
-         (setq temp-object (js2-object-prop-node-right temp))))
-    (unless names node)))
-
-(defun js2-search-scope (node names)
-  "Searches NODE scope for jump location matching NAMES.
-NAMES is a list of property values to search for. For functions
-and variables NAMES will contain one element."
-  (let (node-init
-        (val (js2-name-node-name (car names))))
-    (setq node-init (js2-get-symbol-declaration node val))
-
-    (when (> (length names) 1)
-
-      ;; Check var declarations
-      (when (and node-init (string= val (js2-name-node-name node-init)))
-        (let ((parent (js2-node-parent node-init))
-              (temp-names names))
-          (pop temp-names) ;; First element is var name
-          (setq node-init (when (js2-var-init-node-p parent)
-                            (js2-search-object-for-prop
-                             (js2-var-init-node-initializer parent)
-                             temp-names)))))
-
-      ;; Check all assign nodes
-      (js2-visit-ast
-       js2-mode-ast
-       (lambda (node endp)
-         (unless endp
-           (if (js2-assign-node-p node)
-               (let ((left (js2-assign-node-left node))
-                     (right (js2-assign-node-right node))
-                     (temp-names names))
-                 (when (js2-prop-get-node-p left)
-                   (let* ((prop-list (js2-compute-nested-prop-get left))
-                          (found (cl-loop for prop in prop-list
-                                          until (not (string= (js2-name-node-name
-                                                               (pop temp-names))
-                                                              (js2-name-node-name prop)))
-                                          if (not temp-names) return prop))
-                          (found-node (or found
-                                          (when (js2-object-node-p right)
-                                            (js2-search-object-for-prop right
-                                                                        temp-names)))))
-                     (if found-node (push found-node node-init))))))
-           t))))
-    node-init))
-
-(defun js2-get-symbol-declaration (node name)
-  "Find scope for NAME from NODE."
-  (let ((scope (js2-get-defining-scope
-          (or (js2-node-get-enclosing-scope node)
-             node) name)))
-    (if scope (js2-symbol-ast-node (js2-scope-get-symbol scope name)))))
-
-(provide 'js2-mode)
-
-;;; js2-mode.el ends here