diff options
| author | mattkae <mattkae@protonmail.com> | 2022-06-07 08:23:47 -0400 | 
|---|---|---|
| committer | mattkae <mattkae@protonmail.com> | 2022-06-07 08:23:47 -0400 | 
| commit | bd18a38c2898548a3664a9ddab9f79c84f2caf4a (patch) | |
| tree | 95b9933376770381bd8859782ae763be81c2d72b /elpa/js2-mode-20220402.2211/js2-mode.el | |
| parent | b07628dddf418d4f47b858e6c35fd3520fbaeed2 (diff) | |
| parent | ef160dea332af4b4fe5e2717b962936c67e5fe9e (diff) | |
Merge conflict
Diffstat (limited to 'elpa/js2-mode-20220402.2211/js2-mode.el')
| -rw-r--r-- | elpa/js2-mode-20220402.2211/js2-mode.el | 13090 | 
1 files changed, 0 insertions, 13090 deletions
diff --git a/elpa/js2-mode-20220402.2211/js2-mode.el b/elpa/js2-mode-20220402.2211/js2-mode.el deleted file mode 100644 index 65241ad..0000000 --- a/elpa/js2-mode-20220402.2211/js2-mode.el +++ /dev/null @@ -1,13090 +0,0 @@ -;;; js2-mode.el --- Improved JavaScript editing mode -*- lexical-binding: t -*- - -;; Copyright (C) 2009, 2011-2021  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: 20211229 -;; 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)) - -;; Use Emacs 27 and want to write JSX?  Then use `js2-minor-mode' as described -;; above.  Use Emacs 26 or earlier?  Then use `js2-jsx-mode': - -;;   (add-to-list 'auto-mode-alist '("\\.jsx?\\'" . js2-jsx-mode)) -;;   (add-to-list 'interpreter-mode-alist '("node" . js2-jsx-mode)) - -;; Note that linting of JSX code may fail in both modes. - -;; 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 - -            ;; Window or WorkerGlobalScope with support in Chromium and Firefox: -            ;; https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope -            ;; properties -            caches indexedDB isSecureContext origin -            ;; methods -            atob btoa clearInterval clearTimeout -            createImageBitmap fetch queueMicrotask setInterval -            setTimeout - -            ;; from jslint "browser" -            history location - -            ;; HTML element constructors -            Audio Image Option - -            ;; 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 usage 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-ASSIGN_AND 102)    ; &&= -(defvar js2-ASSIGN_OR 103)     ; ||= -(defvar js2-ASSIGN_NULLISH 104) ; ??= - -(defvar js2-first-assign js2-ASSIGN) -(defvar js2-last-assign js2-ASSIGN_NULLISH) - -(defvar js2-COLON 105) -(defvar js2-OR 106)            ; logical or (||) -(defvar js2-AND 107)           ; logical and (&&) -(defvar js2-INC 108)           ; increment/decrement (++ --) -(defvar js2-DEC 109) -(defvar js2-DOT 110)           ; member operator (.) -(defvar js2-FUNCTION 111)      ; function keyword -(defvar js2-EXPORT 112)        ; export keyword -(defvar js2-IMPORT 113)        ; import keyword -(defvar js2-IF 114)            ; if keyword -(defvar js2-ELSE 115)          ; else keyword -(defvar js2-SWITCH 116)        ; switch keyword -(defvar js2-CASE 117)          ; case keyword -(defvar js2-DEFAULT 118)       ; default keyword -(defvar js2-WHILE 119)         ; while keyword -(defvar js2-DO 120)            ; do keyword -(defvar js2-FOR 121)           ; for keyword -(defvar js2-BREAK 122)         ; break keyword -(defvar js2-CONTINUE 123)      ; continue keyword -(defvar js2-VAR 124)           ; var keyword -(defvar js2-WITH 125)          ; with keyword -(defvar js2-CATCH 126)         ; catch keyword -(defvar js2-FINALLY 127)       ; finally keyword -(defvar js2-VOID 128)          ; void keyword -(defvar js2-RESERVED 129)      ; reserved keywords - -(defvar js2-EMPTY 130) - -;; Types used for the parse tree - never returned by scanner. - -(defvar js2-BLOCK 131)         ; statement block -(defvar js2-LABEL 132)         ; label -(defvar js2-TARGET 133) -(defvar js2-LOOP 134) -(defvar js2-EXPR_VOID 135)     ; expression statement in functions -(defvar js2-EXPR_RESULT 136)   ; expression statement in scripts -(defvar js2-JSR 137) -(defvar js2-SCRIPT 138)        ; top-level node for entire script -(defvar js2-TYPEOFNAME 139)    ; for typeof(simple-name) -(defvar js2-USE_STACK 140) -(defvar js2-SETPROP_OP 141)    ; x.y op= something -(defvar js2-SETELEM_OP 142)    ; x[y] op= something -(defvar js2-LOCAL_BLOCK 143) -(defvar js2-SET_REF_OP 144)    ; *reference op= something - -;; For XML support: -(defvar js2-DOTDOT 145)        ; member operator (..) -(defvar js2-COLONCOLON 146)    ; namespace::name -(defvar js2-XML 147)           ; XML type -(defvar js2-DOTQUERY 148)      ; .() -- e.g., x.emps.emp.(name == "terry") -(defvar js2-XMLATTR 149)       ; @ -(defvar js2-XMLEND 150) - -;; Optimizer-only tokens -(defvar js2-TO_OBJECT 151) -(defvar js2-TO_DOUBLE 152) - -(defvar js2-GET 153)           ; JS 1.5 get pseudo keyword -(defvar js2-SET 154)           ; JS 1.5 set pseudo keyword -(defvar js2-LET 155)           ; JS 1.7 let pseudo keyword -(defvar js2-CONST 156) -(defvar js2-SETCONST 157) -(defvar js2-SETCONSTVAR 158) -(defvar js2-ARRAYCOMP 159) -(defvar js2-LETEXPR 160) -(defvar js2-WITHEXPR 161) -(defvar js2-DEBUGGER 162) - -(defvar js2-COMMENT 163) -(defvar js2-TRIPLEDOT 164)     ; for rest parameter -(defvar js2-ARROW 165)         ; function arrow (=>) -(defvar js2-CLASS 166) -(defvar js2-EXTENDS 167) -(defvar js2-SUPER 168) -(defvar js2-TEMPLATE_HEAD 169)    ; part of template literal before substitution -(defvar js2-NO_SUBS_TEMPLATE 170) ; template literal without substitutions -(defvar js2-TAGGED_TEMPLATE 171)  ; tagged template literal - -(defvar js2-AWAIT 172)  ; await (pseudo keyword) - -(defvar js2-HOOK 173)          ; conditional (?:) -(defvar js2-OPTIONAL-CHAINING 174) ; optional chaining (?.prop obj?.[expr] func?.()) -(defvar js2-EXPON 175) -(defvar js2-NULLISH-COALESCING 176) ; nullish coalescing (obj.value ?? obj.defaultValue ?? 0)) - -(defvar js2-PRIVATE_NAME 177)      ; this.#bar(); - -(defconst js2-num-tokens (1+ js2-PRIVATE_NAME)) - -(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-change-syntax-p t -  "Non-nil to set the syntax-table text property on certain literals.") - -(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.bad.optional.chaining" -         "missing property name or [ or ( after optional chaining operator") - -(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;") - -(js2-msg "msg.no.trailing.numeric.literal" -         "underscore cannot appear after last digit") - -(js2-msg "msg.no.consecutive.numeric.literal" -         "underscores cannot be next to each other") - -(js2-msg "msg.no.numeric.separator.after.leading.zero" -         "underscore cannot appear after leading zero") - -;; 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 -                                                        await-pos -                                                        foreach-p forof-p forawait-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 -  await-pos  ; buffer position of 'await' keyword, if forawait-p -  foreach-p ; t if it's a for-each loop -  forawait-p ; t if it's a for-await 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)) -        (forawait (js2-for-in-node-forawait-p n)) -        (forof (js2-for-in-node-forof-p n))) -    (insert pad "for ") -    (if foreach -        (insert "each ")) -    (if forawait -        (insert "await ")) -    (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 ") -    (when (js2-catch-node-lp n) -      (insert "(") -      (js2-print-ast (js2-catch-node-param n) 0) -      (when guard-kwd -        (insert " if ") -        (js2-print-ast guard-expr 0)) -      (insert ") ")) -    (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-NULLISH-COALESCING "??") -               (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 "%=") -               (cons js2-ASSIGN_AND "&&=") -               (cons js2-ASSIGN_OR "||=") -               (cons js2-ASSIGN_NULLISH "??=")))) -    (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-ASSIGN_AND -                      js2-ASSIGN_OR -                      js2-ASSIGN_NULLISH -                      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) -            (= tt js2-NULLISH-COALESCING)) -        (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." -  (save-excursion -    (and buf (set-buffer buf)) -    (goto-char (point-min)) -    (when (looking-at "#!/") -      (forward-line 1)) -    (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) -          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-handle-numeric-separator () -  "Detect and handle numeric separator ?_." -  (let ((buffer (nreverse js2-ts-string-buffer)) -        (res nil)) -    (while (> (length buffer) 0) -      (let ((current-c (car buffer)) -            (next-c (cadr buffer))) -        (if (eq current-c ?_) -            (if (or (= (length buffer) 1) (memq next-c '(?. ?e ?E))) -                (js2-report-scan-error "msg.no.trailing.numeric.literal") -              (when (= (cadr buffer) ?_) -                (js2-report-scan-error "msg.no.consecutive.numeric.literal"))) -          (push (car buffer) res))) -      (setq buffer (cdr buffer))) -    (setq js2-ts-string-buffer res))) - -(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 -        identifier-private -        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 (or (js2-identifier-start-p c) -                                            (and -                                             (eq c ?#) -                                             (setq identifier-private t)))) -             (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 -                  (if identifier-private -                      js2-PRIVATE_NAME -                    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 -              ((eq c ?_) (js2-report-scan-error "msg.no.numeric.separator.after.leading.zero")) -              ((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 (or (<= 0 (js2-x-digit-to-int c 0)) (= c ?_)) -                 (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 (or (and (<= ?0 c) (>= ?7 c)) (= c ?_)) -                 (js2-add-to-string c) -                 (setq c (js2-get-char))))) -            (t -             (while (or (and (<= ?0 c) (<= c ?9)) (= c ?_)) -               ;; 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 (or (js2-digit-p c) (= 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 (or (js2-digit-p c) (= c ?_))))) -           (js2-unget-char) -           (js2-handle-numeric-separator) -           (let ((string js2-ts-string-buffer)) -             (while (> (length string) 0) -               (when (and (eq (car string) ?_)) -                 (if (= (length string) 1) -                     (js2-report-scan-error "msg.no.trailing.numeric.literal"))) -               (setq string (cdr string)))) -           (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)) -                          (?? -                           (if (js2-match-char ??) -                               (if (js2-match-char ?=) -                                   js2-ASSIGN_NULLISH -                                 (throw 'return js2-NULLISH-COALESCING)) -                             (if (js2-match-char ?.) -                                 (throw 'return js2-OPTIONAL-CHAINING) -                               (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 ?|) -                               (if (js2-match-char ?=) -                                   js2-ASSIGN_OR -                                 (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 ?&) -                               (if (js2-match-char ?=) -                                   js2-ASSIGN_AND -                                 (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))) -    (when js2-mode-change-syntax-p -      (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 -      (when js2-mode-change-syntax-p -        (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)) -                 (not (eq node (js2-object-prop-node-right 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--is-param (var-node params) -  "Recursively determine whether VAR-NODE is contained in PARAMS." -  (cond ((js2-object-prop-node-p params) -         (eq var-node (js2-object-prop-node-left params))) -        ((js2-name-node-p params) -         (eq var-node params)) -        (t -         (let ((isparam (if (listp params) -                            (memq var-node params) -                          (cl-loop with found = nil -                                   for p in (js2-node-child-list params) -                                   while (null found) -                                   do (setq found (eq var-node p)))))) -           (unless isparam -             (let ((kids (if (listp params) -                             params -                           (js2-node-child-list params)))) -               (cl-loop for p in kids -                        while (null isparam) -                        do (setq isparam (js2--is-param var-node p))))) -           isparam)))) - -(defun js2--is-function-param (parent var-node) -  "Determine whether VAR-NODE is a function parameter." -  (while (and parent (not (js2-function-node-p parent))) -    (if (or (js2-var-init-node-p parent) -            (js2-assign-node-p parent)) -        (setq parent nil) -    (setq parent (js2-node-parent parent)))) -  (when parent -    (js2--is-param var-node (js2-function-node-params parent)))) - -(defun js2--classify-variable (parent node vars) -  "Classify the single variable NODE, a js2-name-node, updating the VARS collection." -  (let ((function-param (js2--is-function-param parent node))) -    (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)) -               ((js2-class-node-p parent) -                (setq declared t -                      assigned t)) -               (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 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 (memq (js2-get-prop-name-token) `(,js2-NAME ,js2-PRIVATE_NAME)) -      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-declaration-or-expr) -    (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-declaration-or-expr () -  (if (memq (js2-peek-token) `(,js2-LP ,js2-DOT)) -      (js2-parse-expr-stmt) -    (js2-parse-import))) - -(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, for each-in or for await-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 is-for-await -        in-pos each-pos tmp-pos await-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) -    (when (js2-match-token js2-NAME) -      (cond -       ;; See if this is a for each () instead of just a for () -       ((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))) -       ;; See if this is a for await () instead of just a for () -       ((string= "await" (js2-current-token-string)) -        (progn -          (setq is-for-await t -                await-pos (- (js2-current-token-beg) for-pos)) ; relative -          (js2-record-face 'font-lock-keyword-face))) -       (t (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 -                                     :forawait-p is-for-await -                                     :await-pos await-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")) -          (js2-push-scope catch-node) -          (when (js2-match-token js2-LP) -            (setq lp (- (js2-current-token-beg) catch-pos)) -            (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 -      (if (eq (js2-peek-token) js2-RP) -          ;; Stop the parser from scanning too far: it's actually -          ;; valid syntax in arrow fun arguments, and we don't want -          ;; the RP token to get consumed. -          (js2-report-error "msg.syntax") -        (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-nullish-coalescing-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)) - - -(defun js2-parse-nullish-coalescing-expr () -  (let ((pn (js2-parse-or-expr))) -    (when (js2-match-token js2-NULLISH-COALESCING) -      (setq pn (js2-make-binary js2-NULLISH-COALESCING -                                pn -                                'js2-parse-nullish-coalescing-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-optional-chaining-operator (allow-call-syntax pn) -  (let ((tt (js2-peek-token))) -    (cond -     ((eq tt js2-NAME) -      (setq pn (js2-parse-property-access js2-DOT pn))) -     ((eq tt js2-LB) -      ;; skip left bracket token -      (js2-get-token) -      (setq pn (js2-parse-element-get pn))) -     ((and (eq tt js2-LP) allow-call-syntax) -      ;; unget optional chaining operator -      ;; so current token is function name and could be highlighted -      (js2-unget-token) -      (setq pn (js2-parse-function-call pn t))) -     (t -      (js2-report-error "msg.bad.optional.chaining"))) -    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-OPTIONAL-CHAINING) -        (setq pn (js2-parse-optional-chaining-operator allow-call-syntax pn)) -        (unless pn (setq continue nil))) -       ((= 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 &optional use-optional-chaining-p) -  (js2-highlight-function-call (js2-current-token)) -  (js2-get-token) -  (let (args -        (pos (js2-node-pos pn))) -    (when use-optional-chaining-p -      ;; skip optional chaining operator -      (js2-get-token)) -    (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] -       ((or (= tt js2-NAME) (= tt js2-PRIVATE_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-IMPORT) -      (js2-create-name-node nil nil "import")) -     ((= 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-type-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 -      ;; Clear out any lookahead tokens (possibly wrong modifier). -      ;; FIXME: Deal with this problem in a more systematic fashion. -      ;; Perhaps by making this modifier affect not how the token -      ;; struct value is constructed, but what js2-get-token returns -      ;; based on it. -      (when (> js2-ti-lookahead 0) -        (setq js2-ti-lookahead 0) -        (setq js2-ts-cursor (js2-current-token-end))) -      (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")) -                   (memq (js2-peek-token 'KEYWORD_IS_NAME) -                         `(,js2-NAME ,js2-PRIVATE_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) -       ((memq tt `(,js2-NAME ,js2-PRIVATE_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 class-p)) -        (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 allow-private) -  (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)) -   ((and allow-private -         (= tt js2-PRIVATE_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)) -                    (memq tt `(,js2-EOL ,js2-RC ,js2-SEMI))) -             (and (setq tt (js2-peek-token)) -                  (memq tt `(,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 (or (= js2-NAME tt) (= js2-PRIVATE_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 -  (set (make-local-variable 'js2-mode-change-syntax-p) nil) -  (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 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.") - -(define-derived-mode js2-error-buffer-mode special-mode "JS Lint Diagnostics" -  "Major mode for js2 diagnostics buffers. -Selecting an error will jump it to the corresponding source-buffer error. -\\{js2-error-buffer-mode-map}" -  (setq truncate-lines t) -  (set-buffer-modified-p nil) -  (setq buffer-read-only t)) - -(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 (derived-mode-p '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))) - -;;;###autoload -(define-derived-mode js2-jsx-mode js2-mode "JSX-IDE" -  "Major mode for editing JSX code in Emacs 26 and earlier. - -To edit JSX code in Emacs 27, use `js-mode' as your major mode -with `js2-minor-mode' enabled. - -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)" -  (unless (version< emacs-version "27.0") -    ;; Emacs 27 causes a regression in this mode since JSX indentation -    ;; begins to rely on js-mode’s `syntax-propertize-function', which -    ;; JS2 is not currently using. -    ;; https://github.com/mooz/js2-mode/issues/529 should address -    ;; this.  https://github.com/mooz/js2-mode/issues/530 also has a -    ;; piece related to the design of `js2-jsx-mode'.  Until these -    ;; issues are addressed, ward Emacs 27 users away from this mode. -    (display-warning 'js2-mode "For JSX support, use js-mode with js2-minor-mode")) -  (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' -                             (when js2-mode-change-syntax-p -                               (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 pos start))) -              (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-infix-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-infix-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) -    (cond -     ((js2-name-node-p (car names)) -      (setq val (js2-name-node-name (car names))) -      (setq node-init (js2-get-symbol-declaration node val))) -     ((and (js2-keyword-node-p (car names)) -           (equal (js2-keyword-node-type (car names)) -                  js2-THIS)) -      (let* ((scope (js2-node-get-enclosing-scope node)) -             (parent (js2-node-parent scope))) -        (when (or (js2-method-node-p parent) -                  (js2-object-prop-node-p parent)) -          ;; class or object -          (setq node-init (js2-node-parent parent)))))) - -    (when (> (length names) 1) -      (when node-init -        (cond -         ((js2-name-node-p (car names)) -          ;; Check var declarations -          (when (string= val (js2-name-node-name node-init)) -            (let ((parent (js2-node-parent node-init))) -              (setq node-init (when (js2-var-init-node-p parent) -                                (js2-search-object-for-prop -                                 (js2-var-init-node-initializer parent) -                                 (cdr names))))))) -         ((js2-object-node-p node-init) -          (setq node-init (js2-search-object-for-prop -                           node-init -                           (cdr 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)) -                          ;; 'this' or 'super' -                          (target-is-keyword (js2-keyword-node-p (car temp-names))) -                          (_ (when target-is-keyword -                               (pop temp-names))) -                          (found (unless target-is-keyword -                                   (cl-loop for prop in prop-list -                                            until (not (string= (js2-name-node-name -                                                                 (pop temp-names)) -                                                                (and (js2-name-node-p prop) -                                                                     (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  | 
