;;; js2-mode.el --- Improved JavaScript editing mode
-;; Copyright (C) 2009, 2011-2013 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2014 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: 20130619
+;; Version: 20141118
;; Keywords: languages, javascript
;; Package-Requires: ((emacs "24.1"))
;; 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 is mentioned here for posterity, it has
-;; outdated information and is mostly unmaintained.
+;; The original homepage at Google Code has outdated information and is mostly
+;; unmaintained.
;;; Code:
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
(defun js2-mark-safe-local (name pred)
:type 'symbol)
(js2-mark-safe-local 'js2-pretty-multiline-declarations 'symbolp)
+(defcustom js2-indent-switch-body nil
+ "When nil, case labels are indented on the same level as the
+containing switch statement. Otherwise, all lines inside
+switch statement body are indented one additional level."
+ :type 'boolean
+ :group 'js2-mode)
+(js2-mark-safe-local 'js2-indent-case-same-as-switch 'booleanp)
+
(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."
(defvar js2-GETVAR 55)
(defvar js2-SETVAR 56)
(defvar js2-CATCH_SCOPE 57)
-(defvar js2-ENUM_INIT_KEYS 58)
+(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-DEBUGGER 159)
(defvar js2-COMMENT 160)
-(defvar js2-ENUM 161) ; for "enum" reserved word
-(defvar js2-TRIPLEDOT 162) ; for rest parameter
+(defvar js2-TRIPLEDOT 161) ; for rest parameter
+(defvar js2-ARROW 162) ; function arrow (=>)
+(defvar js2-CLASS 163)
+(defvar js2-EXTENDS 164)
+(defvar js2-STATIC 165)
+(defvar js2-SUPER 166)
-(defconst js2-num-tokens (1+ js2-TRIPLEDOT))
+(defconst js2-num-tokens (1+ js2-SUPER))
(defconst js2-debug-print-trees nil)
comment-type
follows-eol-p)
+(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
(defconst js2-end-drops-off #x1)
(defconst js2-end-returns #x2)
(defconst js2-end-returns-value #x4)
-(defconst js2-end-yields #x8)
;; Rhino awkwardly passes a statementLabel parameter to the
;; statementHelper() function, the main statement parser, which
"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-instance-member
'((t :foreground "DarkOrchid"))
"Face used to highlight instance variables in javascript.
: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
:group 'js2-mode)
(defvar js2-mode-map
- (let ((map (make-sparse-keymap))
- keys)
+ (let ((map (make-sparse-keymap)))
(define-key map [mouse-1] #'js2-mode-show-node)
(define-key map (kbd "M-j") #'js2-line-break)
(define-key map (kbd "C-c C-e") #'js2-mode-hide-element)
map)
"Keymap used in `js2-mode' buffers.")
-(defconst js2-mode-identifier-re "[a-zA-Z_$][a-zA-Z0-9_$]*")
+(defconst js2-mode-identifier-re "[[:alpha:]_$][[:alnum:]_$]*")
(defvar js2-mode-//-comment-re "^\\(\\s-*\\)//.+"
"Matches a //-comment line. Must be first non-whitespace on line.
(js2-deflocal js2-imenu-function-map nil "Private variable")
(defvar js2-paragraph-start
- "\\(@[a-zA-Z]+\\>\\|$\\)")
+ "\\(@[[:alpha:]]+\\>\\|$\\)")
;; Note that we also set a 'c-in-sws text property in html comments,
;; so that `c-forward-sws' and `c-backward-sws' work properly.
"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.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")
"missing ) in parenthetical")
(js2-msg "msg.reserved.id"
- "identifier is a reserved word")
+ "'%s' is a reserved identifier")
(js2-msg "msg.no.paren.catch"
"missing ( before catch-block condition")
"return statement is inconsistent with previous usage")
(js2-msg "msg.generator.returns"
- "TypeError: generator function '%s' returns a value")
+ "TypeError: legacy generator function '%s' returns a value")
(js2-msg "msg.anon.generator.returns"
- "TypeError: anonymous generator function returns a value")
+ "TypeError: anonymous legacy generator function returns a value")
(js2-msg "msg.syntax"
"syntax error")
"illegal octal literal digit %s; "
"interpreting it as a decimal digit")
-(js2-msg "msg.reserved.keyword"
- "illegal usage of future reserved keyword %s; "
- "interpreting it as ordinary identifier")
+(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.")
(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)
(- (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)
,@body)
(modify-syntax-entry ?_ ,old-syntax js2-mode-syntax-table)))))
-(defsubst js2-char-uppercase-p (c)
- "Return t if C is an uppercase character.
-Handles unicode and latin chars properly."
- (/= c (downcase c)))
-
-(defsubst js2-char-lowercase-p (c)
- "Return t if C is an uppercase character.
-Handles unicode and latin chars properly."
- (/= c (upcase c)))
-
;;; AST struct and function definitions
;; flags for ast node property 'member-type (used for e4x operators)
(:constructor make-js2-script-node (&key (type js2-SCRIPT)
(pos (js2-current-token-beg))
len
+ ;; FIXME: What are those?
var-decls
fun-decls)))
functions ; Lisp list of nested functions
(insert "each "))
(insert "(")
(js2-print-ast (js2-for-in-node-iterator n) 0)
- (if forof
- (insert " of ")
- (insert " in "))
+ (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))
(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) (1+ 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.
(name "")
params rest-p
body
+ generator-type
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|EXPRESSION_STATEMENT}
+ 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
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
- is-generator ; t if this function contains a yield
+ generator-type ; STAR, LEGACY, COMPREHENSION or nil
member-expr) ; nonstandard Ecma extension from Rhino
(put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node)
(js2-visit-ast (js2-function-node-body n) v))
(defun js2-print-function-node (n i)
- (let ((pad (js2-make-pad i))
- (getter (js2-node-get-prop n 'GETTER_SETTER))
- (name (js2-function-node-name n))
- (params (js2-function-node-params n))
- (rest-p (js2-function-node-rest-p n))
- (body (js2-function-node-body n))
- (expr (eq (js2-function-node-form n) 'FUNCTION_EXPRESSION)))
- (unless getter
- (insert pad "function"))
+ (let* ((pad (js2-make-pad i))
+ (getter (js2-node-get-prop n 'GETTER_SETTER))
+ (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 (or getter arrow)
+ (insert pad "function")
+ (when (eq (js2-function-node-generator-type n) 'STAR)
+ (insert "*")))
(when name
- (insert " ")
- (js2-print-ast name 0))
+ (insert " ")
+ (js2-print-ast name 0))
(insert "(")
(loop with len = (length params)
for param in params
(js2-print-ast param 0)
(when (< count len)
(insert ", ")))
- (insert ") {")
+ (insert ") ")
+ (when arrow
+ (insert "=> "))
+ (insert "{")
+ ;; TODO: fix this to be smarter about indenting, etc.
(unless expr
(insert "\n"))
- ;; TODO: fix this to be smarter about indenting, etc.
- (js2-print-body body (1+ i))
+ (if (js2-block-node-p body)
+ (js2-print-body body (1+ i))
+ (js2-print-ast body 0))
(insert pad "}")
(unless expr
(insert "\n"))))
(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 "=")
len operand)))
"AST node type for unary operator nodes.
The type field can be NOT, BITNOT, POS, NEG, INC, DEC,
-TYPEOF, or DELPROP. For INC or DEC, a 'postfix node
+TYPEOF, DELPROP or TRIPLEDOT. For INC or DEC, a 'postfix node
property is added if the operator follows the operand."
operand) ; a `js2-node' expression
(defun js2-print-let-node (n i)
(insert (js2-make-pad i) "let (")
- (js2-print-ast (js2-let-node-vars n) 0)
+ (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))
(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-node-p (node)
- "Return t if NODE is a `js2-literal-node' of type js2-THIS."
- (eq (js2-node-type node) js2-THIS))
+(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))))
(defstruct (js2-new-node
(:include js2-node)
(defun js2-print-array-node (n i)
(insert (js2-make-pad i) "[")
- (js2-print-list (js2-array-node-elems n))
+ (let ((elems (js2-array-node-elems n)))
+ (js2-print-list elems)
+ (when (and elems (null (car (last elems))))
+ (insert ",")))
(insert "]"))
+(defstruct (js2-class-node
+ (:include js2-node)
+ (:constructor nil)
+ (: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)
+ elems)
+
+(put 'cl-struct-js2-class-node 'js2-visitor 'js2-visit-class-node)
+(put 'cl-struct-js2-class-node 'js2-printer 'js2-print-class-node)
+
+(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 "}")))
+
(defstruct (js2-object-node
(:include js2-node)
(:constructor nil)
len
elems)))
"AST node for an object literal expression.
-`elems' is a list of either `js2-object-prop-node' or `js2-name-node'.
-The latter represents abbreviation in destructuring expressions."
+`elems' is a list of `js2-object-prop-node'."
elems)
(put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
right op-pos)))
"AST node for an object literal prop:value entry.
The `left' field is the property: a name node, string node or number node.
-The `right' field is a `js2-node' representing the initializer value.")
+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.")
(put 'cl-struct-js2-object-prop-node 'js2-visitor 'js2-visit-infix-node)
(put 'cl-struct-js2-object-prop-node 'js2-printer 'js2-print-object-prop-node)
(defun js2-print-object-prop-node (n i)
- (insert (js2-make-pad i))
- (js2-print-ast (js2-object-prop-node-left n) 0)
- (insert ": ")
- (js2-print-ast (js2-object-prop-node-right n) 0))
+ (let* ((left (js2-object-prop-node-left n))
+ (computed (not (or (js2-string-node-p left)
+ (js2-number-node-p left)
+ (js2-name-node-p left)))))
+ (insert (js2-make-pad i))
+ (if computed
+ (insert "["))
+ (js2-print-ast left 0)
+ (if computed
+ (insert "]"))
+ (if (not (js2-node-get-prop n 'SHORTHAND))
+ (progn
+ (insert ": ")
+ (js2-print-ast (js2-object-prop-node-right n) 0)))))
(defstruct (js2-getter-setter-node
(:include js2-infix-node)
(:constructor nil)
- (:constructor make-js2-getter-setter-node (&key type ; GET or SET
+ (:constructor make-js2-getter-setter-node (&key type ; GET, SET, or FUNCTION
(pos js2-ts-cursor)
len left right)))
"AST node for a getter/setter property in an object literal.
The `left' field is the `js2-name-node' naming the getter/setter prop.
The `right' field is always an anonymous `js2-function-node' with a node
-property `GETTER_SETTER' set to js2-GET or js2-SET. ")
+property `GETTER_SETTER' set to js2-GET, js2-SET, or js2-FUNCTION. ")
(put 'cl-struct-js2-getter-setter-node 'js2-visitor 'js2-visit-infix-node)
(put 'cl-struct-js2-getter-setter-node 'js2-printer 'js2-print-getter-setter)
(left (js2-getter-setter-node-left n))
(right (js2-getter-setter-node-right n)))
(insert pad)
- (insert (if (= (js2-node-type n) js2-GET) "get " "set "))
+ (if (/= (js2-node-type n) js2-FUNCTION)
+ (insert (if (= (js2-node-type n) js2-GET) "get " "set ")))
(js2-print-ast left 0)
(js2-print-ast right 0)))
(:constructor nil)
(:constructor make-js2-yield-node (&key (type js2-YIELD)
(pos js2-ts-cursor)
- len value)))
+ len value star-p)))
"AST node for yield statement or expression."
+ star-p ; whether it's yield*
value) ; optional: value to be yielded
(put 'cl-struct-js2-yield-node 'js2-visitor 'js2-visit-yield-node)
(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)))
(js2-print-ast (js2-paren-node-expr n) 0)
(insert ")"))
-(defstruct (js2-array-comp-node
+(defstruct (js2-comp-node
(:include js2-scope)
(:constructor nil)
- (:constructor make-js2-array-comp-node (&key (type js2-ARRAYCOMP)
- (pos js2-ts-cursor)
- len result
- loops filter
- if-pos lp rp)))
+ (: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-array-comp-loop-node'
- filter ; guard/filter expression
- if-pos ; buffer pos of 'if' keyword, if present, else nil
- lp ; buffer position of if-guard left-paren, or nil if not present
- rp) ; buffer position of if-guard right-paren, or nil if not present
-
-(put 'cl-struct-js2-array-comp-node 'js2-visitor 'js2-visit-array-comp-node)
-(put 'cl-struct-js2-array-comp-node 'js2-printer 'js2-print-array-comp-node)
-
-(defun js2-visit-array-comp-node (n v)
- (js2-visit-ast (js2-array-comp-node-result n) v)
- (dolist (l (js2-array-comp-node-loops n))
+ 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.
+ )
+
+(put 'cl-struct-js2-comp-node 'js2-visitor 'js2-visit-comp-node)
+(put 'cl-struct-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))
- (js2-visit-ast (js2-array-comp-node-filter n) v))
+ (dolist (f (js2-comp-node-filters n))
+ (js2-visit-ast f v)))
-(defun js2-print-array-comp-node (n i)
+(defun js2-print-comp-node (n i)
(let ((pad (js2-make-pad i))
- (result (js2-array-comp-node-result n))
- (loops (js2-array-comp-node-loops n))
- (filter (js2-array-comp-node-filter n)))
- (insert pad "[")
- (js2-print-ast result 0)
+ (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)
- (insert " ")
- (js2-print-ast l 0))
- (when filter
- (insert " if (")
- (js2-print-ast filter 0)
- (insert ")"))
- (insert "]")))
-
-(defstruct (js2-array-comp-loop-node
+ (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 ")" "]"))))
+
+(defstruct (js2-comp-loop-node
(:include js2-for-in-node)
(:constructor nil)
- (:constructor make-js2-array-comp-loop-node (&key (type js2-FOR)
- (pos js2-ts-cursor)
- len iterator
- object in-pos
- foreach-p
- each-pos
- forof-p
- lp rp)))
+ (: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.")
-(put 'cl-struct-js2-array-comp-loop-node 'js2-visitor 'js2-visit-array-comp-loop)
-(put 'cl-struct-js2-array-comp-loop-node 'js2-printer 'js2-print-array-comp-loop)
+(put 'cl-struct-js2-comp-loop-node 'js2-visitor 'js2-visit-comp-loop)
+(put 'cl-struct-js2-comp-loop-node 'js2-printer 'js2-print-comp-loop)
-(defun js2-visit-array-comp-loop (n v)
- (js2-visit-ast (js2-array-comp-loop-node-iterator n) v)
- (js2-visit-ast (js2-array-comp-loop-node-object n) v))
+(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-array-comp-loop (n i)
+(defun js2-print-comp-loop (n _i)
(insert "for ")
- (when (js2-array-comp-loop-node-foreach-p n) (insert "each "))
+ (when (js2-comp-loop-node-foreach-p n) (insert "each "))
(insert "(")
- (js2-print-ast (js2-array-comp-loop-node-iterator n) 0)
- (if (js2-array-comp-loop-node-forof-p n)
- (insert " of ")
- (insert " in "))
- (js2-print-ast (js2-array-comp-loop-node-object n) 0)
+ (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 ")"))
(defstruct (js2-empty-expr-node
;; All because Common Lisp doesn't support multiple inheritance for defstructs.
(defconst js2-paren-expr-nodes
- '(cl-struct-js2-array-comp-loop-node
- cl-struct-js2-array-comp-node
+ '(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
(js2-catch-node-lp node))
((js2-let-node-p node)
(js2-let-node-lp node))
- ((js2-array-comp-node-p node)
- (js2-array-comp-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)
(js2-catch-node-rp node))
((js2-let-node-p node)
(js2-let-node-rp node))
- ((js2-array-comp-node-p node)
- (js2-array-comp-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)
(let ((kids (if (js2-function-node-p parent)
(js2-block-node-kids (js2-function-node-body parent))
(js2-node-child-list parent)))
- (beg (if (js2-function-node-p parent)
- (js2-node-abs-pos (js2-function-node-body parent))
- (js2-node-abs-pos 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 '>= '<))
(setq kid (car kids))
(if (funcall fn (+ beg (js2-node-pos kid)) pos)
(setq result kid
- continue (if after nil t))
- (setq continue (if after t nil)))
+ continue (not after))
+ (setq continue after))
(setq kids (cdr kids)))
result))
(setq node (js2-node-parent node)))
node))
-(defun js2-mode-find-enclosing-node (beg end)
- "Find script or function fully enclosing BEG and END."
+ (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 (js2-function-node-p node)
- (<= (setq pos (js2-node-abs-pos node)) beg)
- (>= (+ pos (js2-node-len node)) end)))
+ (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))
;;; visitor infrastructure
-(defun js2-visit-none (node callback)
+(defun js2-visit-none (_node _callback)
"Visitor for AST node that have no node children."
nil)
-(defun js2-print-none (node indent)
+(defun js2-print-none (_node _indent)
"Visitor for AST node with no printed representation.")
(defun js2-print-body (node indent)
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))
- (pos (js2-node-abs-pos node)))
+ (i (or indent 0)))
;; TODO: wedge comments in here somewhere
(if printer
(funcall printer node i))))
js2-CALL
js2-CATCH
js2-CATCH_SCOPE
+ js2-CLASS
js2-CONST
js2-CONTINUE
js2-DEBUGGER
(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-\\(?:[A-Z_]+\\)")))
+ (syms (apropos-internal "^js2-\\(?:[[:upper:]_]+\\)")))
(loop for sym in syms
for i from 0
do
(unless no-throw
(throw 'return js2-ERROR)))
-(defun js2-get-string-from-buffer ()
- "Reverse the char accumulator and return it as a string."
- (setf (js2-token-end (js2-current-token)) js2-ts-cursor)
- (if js2-ts-string-buffer
- (apply #'string (nreverse js2-ts-string-buffer))
- ""))
+(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.
(ignore-errors
(let ((s (buffer-substring-no-properties js2-ts-cursor
(+ 4 js2-ts-cursor))))
- (if (string-match "[a-zA-Z0-9]\\{4\\}" s)
+ (if (string-match "[0-9a-fA-F]\\{4\\}" s)
(read (concat "?\\u" s))))))
(defun js2-match-char (test)
(js2-get-char)
(js2-unget-char)))
-(defun js2-java-identifier-start-p (c)
+(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 '(?$ ?_))
- (js2-char-uppercase-p c)
- (js2-char-lowercase-p c)))
+ (memq (get-char-code-property c 'general-category)
+ ;; Letters
+ '(Lu Ll Lt Lm Lo Nl))))
-(defun js2-java-identifier-part-p (c)
- "Implementation of java.lang.Character.isJavaIdentifierPart()."
- ;; TODO: make me Unicode-friendly. See comments above.
+(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 '(?$ ?_))
- (js2-char-uppercase-p c)
- (js2-char-lowercase-p c)
- (and (>= c ?0) (<= c ?9))))
+ (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)
(defun js2-skip-line ()
"Skip to end of line."
- (let (c)
- (while (not (memq (setq c (js2-get-char)) js2-eol-chars)))
- (js2-unget-char)
- (setf (js2-token-end (js2-current-token)) js2-ts-cursor)))
+ (while (not (memq (js2-get-char) js2-eol-chars)))
+ (js2-unget-char)
+ (setf (js2-token-end (js2-current-token)) js2-ts-cursor)
+ (setq js2-token-end js2-ts-cursor))
(defun js2-init-scanner (&optional buf line)
"Create token stream for BUF starting on LINE.
(defconst js2-keywords
'(break
- case catch const continue
+ case catch class const continue
debugger default delete do
- else enum
+ else extends
false finally for function
if in instanceof import
let
new null
return
- switch
+ static 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. enum isn't in the tokens, and delete is js2-DELPROP.
+;; 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-CONST js2-CONTINUE
+ js2-CASE js2-CATCH js2-CLASS js2-CONST js2-CONTINUE
js2-DEBUGGER js2-DEFAULT js2-DELPROP js2-DO
- js2-ELSE
+ js2-ELSE js2-EXTENDS
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-SWITCH
+ js2-STATIC js2-SUPER js2-SWITCH
js2-THIS js2-THROW js2-TRUE js2-TRY js2-TYPEOF
js2-VAR
js2-WHILE js2-WITH
(aset table js2-REGEXP '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.")
-(defconst js2-reserved-words
- '(abstract
- boolean byte
- char class
- double
- enum export extends
- final float
- goto
- implements import int interface
- long
- native
- package private protected public
- short static super synchronized
- throws transient
- volatile))
+;; 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 super)
+ "Future reserved keywords in ECMAScript 5.1.")
(defconst js2-keyword-names
(let ((table (make-hash-table :test 'equal)))
(defun js2-collect-string (buf)
"Convert BUF, a list of chars, to a string.
Reverses BUF before converting."
- (cond
- ((stringp buf)
- buf)
- ((null buf) ; for emacs21 compat
- "")
- (t
- (if buf
- (apply #'string (nreverse buf))
- ""))))
+ (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.
(setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens)))
(defun js2-get-token-internal ()
- (let ((tt (js2-get-token-internal-1)) ; call scanner
- saw-eol
- face)
+ (let* ((token (js2-get-token-internal-1)) ; 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)
- (if js2-record-comments
- (js2-record-comment)))
+ (when js2-record-comments
+ (js2-record-comment token)))
(setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens))
- (setq tt (js2-get-token-internal-1))) ; call scanner again
+ (setq token (js2-get-token-internal-1) ; call scanner again
+ tt (js2-token-type token)))
(when saw-eol
- (setf (js2-token-follows-eol-p (js2-current-token)) t))
+ (setf (js2-token-follows-eol-p token) t))
;; perform lexical fontification as soon as token is scanned
(when js2-parse-ide-mode
(cond
((minusp tt)
- (js2-record-face 'js2-error))
+ (js2-record-face 'js2-error token))
((setq face (aref js2-kwd-tokens tt))
- (js2-record-face face))
+ (js2-record-face face token))
((and (= tt js2-NAME)
- (equal (js2-current-token-string) "undefined"))
- (js2-record-face 'font-lock-constant-face))))
+ (equal (js2-token-string token) "undefined"))
+ (js2-record-face 'font-lock-constant-face token))))
tt))
(defun js2-get-token-internal-1 ()
During operation, creates an instance of `js2-token' struct, sets
its relevant fields and puts it into `js2-ti-tokens'."
(let (c c1 identifier-start is-unicode-escape-start
- contains-escape escape-val escape-start str result base
- is-integer quote-char val look-for-slash continue tt
+ contains-escape escape-val str result base
+ quote-char val look-for-slash continue tt
(token (js2-new-token 0)))
(setq
tt
(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-unget-char)
(setq c ?\\)))
(t
- (when (setq identifier-start (js2-java-identifier-start-p c))
+ (when (setq identifier-start (js2-identifier-start-p c))
(setq js2-ts-string-buffer nil)
(js2-add-to-string c))))
(when identifier-start
;; an error here.
(progn
(setq escape-val 0)
- (dotimes (i 4)
+ (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
(js2-report-scan-error "msg.illegal.character" t)))
(t
(if (or (eq c js2-EOF_CHAR)
- (not (js2-java-identifier-part-p c)))
+ (not (js2-identifier-part-p c)))
(throw 'break nil))
(js2-add-to-string c))))))
(js2-unget-char)
- (setq str (js2-get-string-from-buffer))
+ (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.
(memq result '(js2-LET js2-YIELD)))
;; LET and YIELD are tokens only in 1.7 and later
(setq result 'js2-NAME))
- (if (not (eq result 'js2-RESERVED))
- (throw 'return (js2-tt-code result)))
- (js2-report-warning "msg.reserved.keyword" str)))
+ (when (eq result 'js2-RESERVED)
+ (setf (js2-token-string token) str))
+ (throw 'return (js2-tt-code result))))
;; If we want to intern these as Rhino does, just use (intern str)
(setf (js2-token-string token) str)
(throw 'return js2-NAME)) ; end identifier/kwd check
((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 c (js2-get-char)))
((js2-digit-p c)
- (setq base 8))
+ (setq base 'maybe-8))
(t
(js2-add-to-string ?0))))
- (if (eq base 16)
+ (cond
+ ((eq base 16)
+ (if (> 0 (js2-x-digit-to-int c 0))
+ (js2-report-scan-error "msg.missing.hex.digits")
(while (<= 0 (js2-x-digit-to-int c 0))
(js2-add-to-string c)
- (setq c (js2-get-char)))
+ (setq c (js2-get-char)))))
+ ((eq base 2)
+ (if (not (memq c '(?0 ?1)))
+ (js2-report-scan-error "msg.missing.binary.digits")
+ (while (memq c '(?0 ?1))
+ (js2-add-to-string c)
+ (setq c (js2-get-char)))))
+ ((eq base 8)
+ (if (or (> ?0 c) (< ?7 c))
+ (js2-report-scan-error "msg.missing.octal.digits")
+ (while (and (<= ?0 c) (>= ?7 c))
+ (js2-add-to-string c)
+ (setq c (js2-get-char)))))
+ (t
(while (and (<= ?0 c) (<= c ?9))
;; We permit 08 and 09 as decimal numbers, which
;; makes our behavior a superset of the ECMA
;; numeric grammar. We might not always be so
;; permissive, so we warn about it.
- (when (and (eq base 8) (>= c ?8))
+ (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))))
- (setq is-integer t)
+ (setq c (js2-get-char)))
+ (when (eq base 'maybe-8)
+ (setq base 8))))
(when (and (eq base 10) (memq c '(?. ?e ?E)))
- (setq is-integer nil)
(when (eq c ?.)
(loop do
(js2-add-to-string c)
(setq c (js2-get-char))
while (js2-digit-p c))))
(js2-unget-char)
- (let ((str (js2-get-string-from-buffer)))
- (setf (js2-token-string token) str
- (js2-token-number token)
- (if (and (eq base 10) (not is-integer))
- (string-to-number str)
- ;; TODO: call runtime number-parser. Some of it is in
- ;; js2-util.el, but I need to port ScriptRuntime.stringToNumber.
- (string-to-number str))))
+ (let ((str (js2-set-string-from-buffer token)))
+ (setf (js2-token-number token)
+ (js2-string-to-number str base)))
(throw 'return js2-NUMBER))
;; is it a string?
(when (memq c '(?\" ?\'))
;; just copy the string in IDE-mode
(js2-add-to-string ?\\)
(js2-add-to-string ?u)
- (dotimes (i 3)
+ (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
;; literal character sequence that follows.
(js2-add-to-string ?u)
(setq escape-val 0)
- (dotimes (i 4)
+ (dotimes (_ 4)
(setq c (js2-get-char)
escape-val (js2-x-digit-to-int c escape-val))
(if (minusp escape-val)
(setq c val)))))
(js2-add-to-string c)
(setq c (js2-get-char)))))
- (setf (js2-token-string token) (js2-get-string-from-buffer))
+ (js2-set-string-from-buffer token)
(throw 'return js2-STRING))
- (case c
+ (js2-ts-return token
+ (case c
(?\;
(throw 'return js2-SEMI))
(?\[
(throw 'return js2-HOOK))
(?:
(if (js2-match-char ?:)
- (js2-ts-return token js2-COLONCOLON)
+ js2-COLONCOLON
(throw 'return js2-COLON)))
(?.
(if (js2-match-char ?.)
(if (js2-match-char ?.)
- (js2-ts-return token js2-TRIPLEDOT)
- (js2-ts-return token js2-DOTDOT))
+ js2-TRIPLEDOT js2-DOTDOT)
(if (js2-match-char ?\()
- (js2-ts-return token js2-DOTQUERY)
+ js2-DOTQUERY
(throw 'return js2-DOT))))
(?|
(if (js2-match-char ?|)
(throw 'return js2-OR)
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_BITOR)
+ js2-ASSIGN_BITOR
(throw 'return js2-BITOR))))
(?^
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_BITOR)
+ js2-ASSIGN_BITOR
(throw 'return js2-BITXOR)))
(?&
(if (js2-match-char ?&)
(throw 'return js2-AND)
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_BITAND)
+ js2-ASSIGN_BITAND
(throw 'return js2-BITAND))))
(?=
(if (js2-match-char ?=)
(if (js2-match-char ?=)
- (js2-ts-return token js2-SHEQ)
+ js2-SHEQ
(throw 'return js2-EQ))
- (throw 'return js2-ASSIGN)))
+ (if (js2-match-char ?>)
+ (js2-ts-return token js2-ARROW)
+ (throw 'return js2-ASSIGN))))
(?!
(if (js2-match-char ?=)
(if (js2-match-char ?=)
- (js2-ts-return token js2-SHNE)
- (js2-ts-return token js2-NE))
+ js2-SHNE
+ js2-NE)
(throw 'return js2-NOT)))
(?<
;; NB:treat HTML begin-comment as comment-till-eol
(js2-unget-char))
(if (js2-match-char ?<)
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_LSH)
- (js2-ts-return token js2-LSH))
+ js2-ASSIGN_LSH
+ js2-LSH)
(if (js2-match-char ?=)
- (js2-ts-return token js2-LE)
+ js2-LE
(throw 'return js2-LT))))
(?>
(if (js2-match-char ?>)
(if (js2-match-char ?>)
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_URSH)
- (js2-ts-return token js2-URSH))
+ js2-ASSIGN_URSH
+ js2-URSH)
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_RSH)
- (js2-ts-return token js2-RSH)))
+ js2-ASSIGN_RSH
+ js2-RSH))
(if (js2-match-char ?=)
- (js2-ts-return token js2-GE)
+ js2-GE
(throw 'return js2-GT))))
(?*
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_MUL)
+ js2-ASSIGN_MUL
(throw 'return js2-MUL)))
(?/
;; is it a // comment?
(setf look-for-slash nil
(js2-token-end token) js2-ts-cursor)))))
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_DIV)
+ js2-ASSIGN_DIV
(throw 'return js2-DIV)))
(?#
(when js2-skip-preprocessor-directives
(throw 'return js2-ERROR))
(?%
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_MOD)
+ js2-ASSIGN_MOD
(throw 'return js2-MOD)))
(?~
(throw 'return js2-BITNOT))
(?+
(if (js2-match-char ?=)
- (js2-ts-return token js2-ASSIGN_ADD)
+ js2-ASSIGN_ADD
(if (js2-match-char ?+)
- (js2-ts-return token js2-INC)
+ js2-INC
(throw 'return js2-ADD))))
(?-
(cond
(t
(setq c js2-SUB)))
(setq js2-ts-dirty-line t)
- (js2-ts-return token c))
+ c)
(otherwise
- (js2-report-scan-error "msg.illegal.character"))))))
+ (js2-report-scan-error "msg.illegal.character")))))))
(setf (js2-token-type token) tt)
- tt))
+ token))
+
+(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-read-regexp (start-tt)
"Called by parser when it gets / or /= in literal context."
(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))
- (setf (js2-token-string token) (js2-collect-string js2-ts-string-buffer)
- (js2-token-end token) js2-ts-cursor)
- ;; tell `parse-partial-sexp' to ignore this range of chars
- (js2-record-text-property (js2-current-token-beg)
- (js2-current-token-end) 'syntax-class '(2)))
+ (js2-set-string-from-buffer token))
(js2-collect-string flags)))
(defun js2-get-first-xml-token ()
(decf js2-ts-xml-open-tags-count)))
(?{
(js2-unget-char)
- (setf (js2-token-string token) (js2-get-string-from-buffer))
+ (js2-set-string-from-buffer token)
(throw 'return js2-XML))
((?\' ?\")
(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))
- (setf (js2-token-string token) (js2-get-string-from-buffer))
+ (js2-set-string-from-buffer token)
(throw 'return js2-XMLEND)))
(t
;; else not tag content
(incf js2-ts-xml-open-tags-count))))
(?{
(js2-unget-char)
- (setf (js2-token-string token) (js2-get-string-from-buffer))
+ (js2-set-string-from-buffer token)
(throw 'return js2-XML))
(t
(js2-add-to-string c))))))))
(js2-name-node-p target)
(js2-name-node-name target)))
(prop-name (if prop (js2-name-node-name prop)))
- (level1 (>= js2-highlight-level 1))
(level2 (>= js2-highlight-level 2))
- (level3 (>= js2-highlight-level 3))
- pos face)
+ (level3 (>= js2-highlight-level 3)))
(when level2
- (if call-p
- (cond
- ((and target prop)
- (cond
- ((and level3 (string-match js2-ecma-function-props prop-name))
- (setq face 'font-lock-builtin-face))
- ((and target-name prop)
- (cond
- ((string= target-name "Date")
- (if (string-match js2-ecma-date-props prop-name)
- (setq face 'font-lock-builtin-face)))
- ((string= target-name "Math")
- (if (string-match js2-ecma-math-funcs prop-name)
- (setq face 'font-lock-builtin-face)))))))
- (prop
- (if (string-match js2-ecma-global-funcs prop-name)
- (setq face 'font-lock-builtin-face))))
- (cond
- ((and target prop)
- (cond
- ((string= target-name "Number")
- (if (string-match js2-ecma-number-props prop-name)
- (setq face 'font-lock-constant-face)))
- ((string= target-name "Math")
- (if (string-match js2-ecma-math-props prop-name)
- (setq face 'font-lock-constant-face)))))
- (prop
- (if (string-match js2-ecma-object-props prop-name)
- (setq face 'font-lock-constant-face)))))
- (when face
- (js2-set-face (setq pos (+ (js2-node-pos parent) ; absolute
- (js2-node-pos prop))) ; relative
- (+ pos (js2-node-len prop))
- face 'record)))))
+ (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 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.
(setq target (js2-prop-get-node-left node)
prop (js2-prop-get-node-right node)))
(cond
- ((js2-name-node-p target)
- (if (js2-name-node-p prop)
- ;; case 2a: simple target, simple prop name, e.g. foo.bar
- (js2-parse-highlight-prop-get parent target prop call-p)
- ;; case 2b: simple target, complex name, e.g. foo.x[y]
- (js2-parse-highlight-prop-get parent target nil call-p)))
((js2-name-node-p prop)
- ;; case 2c: complex target, simple name, e.g. x[y].bar
- (js2-parse-highlight-prop-get parent target prop call-p)))))))
+ ;; 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) {...}.
"\\(?:param\\|argument\\)"
"\\)"
"\\s-*\\({[^}]+}\\)?" ; optional type
- "\\s-*\\[?\\([a-zA-Z0-9_$\.]+\\)?\\]?" ; name
+ "\\s-*\\[?\\([[:alnum:]_$\.]+\\)?\\]?" ; name
"\\>")
"Matches jsdoc tags with optional type and optional param name.")
"Matches a jsdoc @see tag.")
(defconst js2-jsdoc-html-tag-regexp
- "\\(</?\\)\\([a-zA-Z]+\\)\\s-*\\(/?>\\)"
+ "\\(</?\\)\\([[:alpha:]]+\\)\\s-*\\(/?>\\)"
"Matches a simple (no attributes) html start- or end-tag.")
(defun js2-jsdoc-highlight-helper ()
(match-end 3)
'js2-jsdoc-html-tag-delimiter))))))))
-(defun js2-highlight-assign-targets (node left right)
+(defun js2-highlight-assign-targets (_node left right)
"Highlight function properties and external variables."
- (let (leftpos end name)
+ (let (leftpos name)
;; highlight vars and props assigned function values
- (when (js2-function-node-p right)
+ (when (or (js2-function-node-p right)
+ (js2-class-node-p right))
(cond
;; var foo = function() {...}
((js2-name-node-p left)
(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 end)
+ (let ((leftpos (js2-node-abs-pos node)))
(push (list node js2-current-scope
- (setq leftpos (js2-node-abs-pos node))
- (setq end (+ leftpos (js2-node-len node))))
+ leftpos
+ (+ leftpos (js2-node-len node)))
js2-recorded-identifiers)))
(defun js2-highlight-undeclared-vars ()
(setq js2-default-externs
(append js2-ecma-262-externs
(if js2-include-browser-externs js2-browser-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)
((and (js2-number-node-p node)
(string-match "^[0-9]+$" (js2-number-node-value node)))
(js2-number-node-value node))
- ((js2-this-node-p node)
- "this")))
+ ((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.
When passed arguments of wrong type, does nothing."
(when js2-parse-ide-mode
(let ((fun-p (js2-function-node-p node))
- qname left fname-node pos)
+ qname fname-node)
(cond
;; non-anonymous function declaration?
((and fun-p
(let (left right head)
(cond
((or (js2-name-node-p node)
- (js2-this-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
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 (left right prop-qname)
+ (let (right)
(dolist (e (js2-object-node-elems node)) ; e is a `js2-object-prop-node'
(let ((left (js2-infix-node-left e))
;; Element positions are relative to the parent position.
(this-scope (js2-node-get-enclosing-scope node))
defining-scope)
(cond
- ((js2-this-node-p node)
+ ((js2-this-or-super-node-p node)
nil)
((null this-scope)
t)
(let ((parent (js2-node-parent node)))
(or
;; function(){...}();
- (js2-call-node-p parent)
+ (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)))
'("call" "apply"))
(js2-call-node-p (js2-node-parent parent))))))))
-(defun js2-browse-postprocess-chains (entries)
+(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 head fn current-fn parent-qname qname p elem)
- (dolist (entry entries)
+ (let (result fn parent-qname p elem)
+ (dolist (entry js2-imenu-recorder)
;; function node goes first
(destructuring-bind (current-fn &rest (&whole chain head &rest)) entry
;; Examine head's defining scope:
;; Pre-processed chain, or top-level/external, keep as-is.
(if (or (stringp head) (js2-node-top-level-decl-p head))
(push chain result)
- (when (js2-this-node-p head)
+ (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))
(gethash grandparent js2-imenu-function-map 'skip)))
'skip))
(puthash fn parent-qname js2-imenu-function-map))
- (unless (eq parent-qname 'skip)
- ;; prefix parent fn qname to this chain.
+ (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)))))))
- ;; finally replace each node in each chain with its name.
+ ;; 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
(defun js2-build-imenu-index ()
"Turn `js2-imenu-recorder' into an imenu data structure."
- (unless (eq js2-imenu-recorder 'empty)
- (let* ((chains (js2-browse-postprocess-chains js2-imenu-recorder))
- (result (js2-build-alist-trie chains nil)))
- (js2-flatten-trie result))))
+ (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.
(defconst js2-version "1.8.5"
"Version of JavaScript supported.")
-(defmacro js2-record-face (face)
- "Record a style run of FACE for the current token."
- `(js2-set-face (js2-current-token-beg) (js2-current-token-end) ,face 'record))
+(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.
(+ (js2-node-pos n)
(js2-node-len n)))
-(defun js2-record-comment ()
+(defun js2-record-comment (token)
"Record a comment in `js2-scanned-comments'."
- (push (make-js2-comment-node :len (js2-current-token-len)
- :format (js2-token-comment-type
- (js2-current-token)))
- js2-scanned-comments)
- (when js2-parse-ide-mode
- (js2-record-face (if (eq (js2-token-comment-type
- (js2-current-token))
- 'jsdoc)
- 'font-lock-doc-face
- 'font-lock-comment-face))
- (when (memq (js2-token-comment-type
- (js2-current-token))
- '(html preprocessor))
- ;; Tell cc-engine the bounds of the comment.
- (js2-record-text-property (js2-current-token-beg) (1- (js2-current-token-end)) 'c-in-sws t))))
+ (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 ()
"Return the next token type without consuming it.
(defalias 'js2-next-token 'js2-get-token)
-(defun js2-match-token (match)
+(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 (js2-unget-token))
+ (ignore (unless dont-unget (js2-unget-token)))
t))
(defun js2-match-contextual-kwd (name)
(or (= tt js2-NAME)
(and js2-allow-keywords-as-property-names
(plusp tt)
- (aref js2-kwd-tokens tt))))
+ (or (= tt js2-RESERVED)
+ (aref js2-kwd-tokens tt)))))
(defun js2-match-prop-name ()
"Consume token and return t if next token is a valid property name.
"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)
+ (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))
(defsubst js2-inside-function ()
(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)
+(defun js2-check-activation-name (name _token)
(when (js2-inside-function)
;; skip language-version 1.2 check from Rhino
(if (or (string= "arguments" name)
(js2-set-requires-activation))))
(defun js2-set-is-generator ()
- (if (js2-function-node-p js2-current-script-or-fn)
- (setf (js2-function-node-is-generator js2-current-script-or-fn) t)))
+ (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
(max-specpdl-size (max max-specpdl-size 3000))
(case-fold-search nil)
ast)
- (message nil) ; clear any error message from previous parse
(with-current-buffer (or buf (current-buffer))
(setq js2-scanned-comments nil
js2-parsed-errors nil
(while (/= (setq tt (js2-get-token)) js2-EOF)
(if (= tt js2-FUNCTION)
(progn
- (setq n (js2-parse-function (if js2-called-by-compile-function
- 'FUNCTION_EXPRESSION
- 'FUNCTION_STATEMENT))))
+ (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)))
(js2-highlight-undeclared-vars))
root))
-(defun js2-function-parser ()
- (js2-get-token)
- (js2-parse-function 'FUNCTION_EXPRESSION_STATEMENT))
-
(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)))
(js2-block-node-push pn (if (/= tt js2-FUNCTION)
(js2-parse-statement)
(js2-get-token)
- (js2-parse-function 'FUNCTION_STATEMENT))))
+ (js2-parse-function-stmt))))
(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)
((js2-object-node-p node)
(dolist (elem (js2-object-node-elems node))
(js2-define-destruct-symbols
- (if (js2-object-prop-node-p elem)
- (js2-object-prop-node-right elem)
- ;; abbreviated destructuring {a, b}
- elem)
+ ;; In abbreviated destructuring {a, b}, right == left.
+ (js2-object-prop-node-right elem)
decl-type face ignore-not-in-block)))
((js2-array-node-p node)
(dolist (elem (js2-array-node-elems node))
(t (js2-report-error "msg.no.parm" nil (js2-node-abs-pos node)
(js2-node-len node)))))
-(defun js2-parse-function-params (fn-node pos)
+(defun js2-parse-function-params (function-type fn-node pos)
(if (js2-match-token js2-RP)
(setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos))
- (let (params len param default-found rest-param-at)
+ (let ((paren-free-arrow (and (eq function-type 'FUNCTION_ARROW)
+ (eq (js2-current-token-type) js2-NAME)))
+ params param default-found rest-param-at)
+ (when paren-free-arrow
+ (js2-unget-token))
(loop for tt = (js2-peek-token)
do
(cond
;; destructuring param
- ((or (= tt js2-LB) (= tt js2-LC))
+ ((and (not paren-free-arrow)
+ (or (= tt js2-LB) (= tt js2-LC)))
(js2-get-token)
(when default-found
(js2-report-error "msg.no.default.after.default.param"))
;; 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 js2-NAME "msg.no.parm")
+ (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-node-len param)))
(and (>= js2-language-version 200)
(js2-match-token js2-ASSIGN)))
+ (assert (not paren-free-arrow))
(let* ((pos (js2-node-pos param))
(tt (js2-current-token-type))
(op-pos (- (js2-current-token-beg) pos))
(js2-node-pos param) (js2-node-len param)))
while
(js2-match-token js2-COMMA))
- (when (js2-must-match js2-RP "msg.no.paren.after.parms")
+ (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))
(js2-name-node-name name) pos end)
(js2-add-strict-warning "msg.anon.no.return.value" nil pos end)))))
-(defun js2-parse-function (function-type)
- "Function parser. FUNCTION-TYPE is a symbol."
- (let ((pos (js2-current-token-beg)) ; start of 'function' keyword
- name name-beg name-end fn-node lp
- (synthetic-type function-type)
- member-expr-node)
- ;; parse function name, expression, or non-name (anonymous)
- (cond
- ;; function foo(...)
- ((js2-match-token js2-NAME)
- (setq name (js2-create-name-node t)
- name-beg (js2-current-token-beg)
- name-end (js2-current-token-end))
- (unless (js2-match-token js2-LP)
- (when js2-allow-member-expr-as-function-name
- ;; function foo.bar(...)
- (setq member-expr-node name
- name nil
- member-expr-node (js2-parse-member-expr-tail
- nil member-expr-node)))
- (js2-must-match js2-LP "msg.no.paren.parms")))
- ((js2-match-token js2-LP)
- nil) ; anonymous function: leave name as null
- (t
- ;; function random-member-expr(...)
- (when js2-allow-member-expr-as-function-name
- ;; Note that memberExpr can not start with '(' like
- ;; in function (1+2).toString(), because 'function (' already
- ;; processed as anonymous function
- (setq member-expr-node (js2-parse-member-expr)))
- (js2-must-match js2-LP "msg.no.paren.parms")))
+(defun js2-parse-function-stmt ()
+ (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 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)
+ (js2-function-node-member-expr pn) member-expr)
+ pn)
+ (t
+ (js2-report-error "msg.no.paren.parms")
+ (make-js2-error-node))))))
+
+(defun js2-parse-function-expr ()
+ (let ((pos (js2-current-token-beg))
+ (star-p (js2-match-token js2-MUL))
+ name)
+ (when (js2-match-token js2-NAME)
+ (setq name (js2-create-name-node t)))
+ (js2-must-match js2-LP "msg.no.paren.parms")
+ (js2-parse-function 'FUNCTION_EXPRESSION pos star-p name)))
+
+(defun js2-parse-function (function-type pos star-p &optional 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 (fn-node lp)
(if (= (js2-current-token-type) js2-LP) ; eventually matched LP?
(setq lp (js2-current-token-beg)))
- (if member-expr-node
- (progn
- (setq synthetic-type 'FUNCTION_EXPRESSION)
- (js2-parse-highlight-member-expr-fn-name member-expr-node))
- (if name
- (js2-set-face name-beg name-end
- 'font-lock-function-name-face 'record)))
- (if (and (not (eq synthetic-type 'FUNCTION_EXPRESSION))
- (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))
(setf fn-node (make-js2-function-node :pos pos
:name name
:form function-type
- :lp (if lp (- lp pos))))
+ :lp (if lp (- lp pos))
+ :generator-type (and star-p 'STAR)))
+ (when name
+ (js2-set-face (js2-node-pos name) (js2-node-end name)
+ 'font-lock-function-name-face 'record)
+ (when (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)))
(if (or (js2-inside-function) (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.
js2-label-set
js2-loop-set
js2-loop-and-switch-set)
- (js2-parse-function-params fn-node pos)
+ (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))
- (if name
- (js2-node-add-children fn-node name))
(js2-check-inconsistent-return-warning fn-node name)
- ;; Function expressions define a name only in the body of the
- ;; function, and only if not hidden by a parameter name
- (if (and name
- (eq synthetic-type 'FUNCTION_EXPRESSION)
- (null (js2-scope-get-symbol js2-current-scope
- (js2-name-node-name 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))
- (if (and name
- (not (eq function-type 'FUNCTION_EXPRESSION)))
- (js2-record-imenu-functions fn-node)))
- (setf (js2-node-len fn-node) (- js2-ts-cursor pos)
- (js2-function-node-member-expr fn-node) member-expr-node) ; may be nil
+ (when (eq function-type 'FUNCTION_STATEMENT)
+ (js2-record-imenu-functions fn-node))))
+
+ (setf (js2-node-len fn-node) (- js2-ts-cursor 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
pn))
(defun js2-parse-statement ()
- (let (tt pn beg end)
+ (let (pn beg end)
;; coarse-grained user-interrupt check - needs work
(and js2-parse-interruptable-p
(zerop (% (incf js2-parse-stmt-count)
(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-FOR #'js2-parse-for)
- (aset parsers js2-FUNCTION #'js2-function-parser)
+ (aset parsers js2-FUNCTION #'js2-parse-function-stmt)
(aset parsers js2-IF #'js2-parse-if)
(aset parsers js2-LC #'js2-parse-block)
(aset parsers js2-LET #'js2-parse-let-stmt)
js2-LC
js2-ERROR
js2-SEMI
+ js2-CLASS
js2-FUNCTION)
"List of tokens that don't do automatic semicolon insertion.")
(defun js2-statement-helper ()
(let* ((tt (js2-get-token))
(first-tt tt)
- (beg (js2-current-token-beg))
(parser (if (= tt js2-ERROR)
#'js2-parse-semi
(aref js2-parsers tt)))
pn))
(defun js2-parse-switch ()
- "Parser for if-statement. Last matched token must be js2-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 rp)
+ 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)
(js2-define-destruct-symbols param js2-LET nil))
;; simple name
(t
- (js2-must-match js2-NAME "msg.bad.catchcond")
+ (js2-must-match-name "msg.bad.catchcond")
(setq param (js2-create-name-node))
(js2-define-symbol js2-LET (js2-current-token-string) param))))
;; pattern guard
(= (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))
- e ret name)
+ (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 (js2-parse-expr)
(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
- (unless (js2-inside-function)
- (js2-report-error "msg.bad.yield"))
- (js2-set-flag js2-end-flags js2-end-yields)
(setq ret (make-js2-yield-node :pos pos
:len (- end pos)
- :value e))
+ :value e
+ :star-p yield-star-p))
(js2-node-add-children ret e)
(unless expr-context
(setq e ret
(js2-set-is-generator))))
;; see if we are mixing yields and value returns.
(when (and inside-function
- (js2-now-all-set before js2-end-flags
- (logior js2-end-yields js2-end-returns-value)))
+ (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))
(if (eq tt js2-SEMI)
(make-js2-empty-expr-node :len 1)
(setq pos (js2-current-token-beg)
- len (- (js2-current-token-beg) pos))
+ 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 es)
+ end len expr unary)
(js2-must-have-xml)
(js2-set-requires-activation)
(setq len (- js2-ts-cursor pos))
`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))
- (end (js2-current-token-end))
- expr stmt pn bundle
+ 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.
- (setq pn (js2-wrap-with-expr-stmt pos (js2-parse-expr) t))
+ (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)
(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))
end (js2-node-end destructuring))
;; Simple variable name
(js2-unget-token)
- (when (js2-must-match js2-NAME "msg.bad.var")
+ (when (js2-must-match-name "msg.bad.var")
(setq name (js2-create-name-node)
nbeg (js2-current-token-beg)
nend (js2-current-token-end)
(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 (eq (js2-get-token) js2-LC))
+ (if (and stmt-p (js2-match-token js2-LC))
;; let statement
(progn
(setf beg (js2-current-token-beg) ; position stmt at LC
(js2-let-node-body pn) body
(js2-node-type pn) js2-LET))
;; let expression
- (js2-unget-token)
(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))
(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)))
+ (if (and (>= js2-language-version 200)
+ (js2-match-token js2-FOR))
+ (js2-parse-generator-comp px-pos)
+ (let* ((js2-in-for-init nil)
+ (expr (js2-parse-expr))
+ (pn (make-js2-paren-node :pos px-pos
+ :expr expr
+ :len (- (js2-current-token-end)
+ px-pos))))
+ (js2-node-add-children pn (js2-paren-node-expr pn))
+ (js2-must-match js2-RP "msg.no.paren")
+ pn))))
+
(defun js2-parse-expr (&optional oneshot)
(let* ((pn (js2-parse-assign-expr))
(pos (js2-node-pos pn))
(defun js2-parse-assign-expr ()
(let ((tt (js2-get-token))
(pos (js2-current-token-beg))
- pn left right op-pos)
+ pn left right op-pos
+ ts-state recorded-identifiers parsed-errors)
(if (= tt js2-YIELD)
(js2-parse-return-or-yield tt 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))
- (if (not (and (<= js2-first-assign tt)
- (<= tt js2-last-assign)))
- (js2-unget-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)
(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 (= tt js2-ARROW)
+ (>= js2-language-version 200))
+ (js2-ts-seek ts-state)
+ (setq js2-recorded-identifiers recorded-identifiers
+ js2-parsed-errors parsed-errors)
+ (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) nil)))
+ (t
+ (js2-unget-token)))
pn)))
(defun js2-parse-cond-expr ()
c-pos)
(when (js2-match-token js2-HOOK)
(setq q-pos (- (js2-current-token-beg) pos)
- if-true (js2-parse-assign-expr))
+ 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)
(let (result)
(unless (js2-match-token js2-RP)
(loop do
- (if (= (js2-peek-token) js2-YIELD)
- (js2-report-error "msg.yield.parenthesized"))
- (push (js2-parse-assign-expr) result)
+ (let ((tt (js2-get-token)))
+ (if (= tt js2-YIELD)
+ (js2-report-error "msg.yield.parenthesized"))
+ (if (and (= tt js2-TRIPLEDOT)
+ (>= js2-language-version 200))
+ (push (js2-make-unary tt 'js2-parse-assign-expr) result)
+ (js2-unget-token)
+ (push (js2-parse-assign-expr) result)))
while
(js2-match-token js2-COMMA))
(js2-must-match js2-RP "msg.no.paren.arg")
(defun js2-parse-member-expr (&optional allow-call-syntax)
(let ((tt (js2-current-token-type))
- pn pos target args beg end init tail)
+ pn pos target args beg end init)
(if (/= tt js2-NEW)
(setq pn (js2-parse-primary-expr))
;; parse a 'new' expression
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.
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 ((beg (js2-node-pos pn))
- tt
+ (let (tt
(continue t))
(while continue
(setq tt (js2-get-token))
((= 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))
- (js2-unget-token)
(setq continue nil)))
(t
(js2-unget-token)
(js2-elem-get-node-element pn))
pn))
+(defun js2-highlight-function-call (token)
+ (when (eq (js2-token-type token) js2-NAME)
+ (js2-record-face 'js2-function-call token)))
+
(defun js2-parse-function-call (pn)
+ (js2-highlight-function-call (js2-current-token))
+ (js2-get-token)
(let (args
(pos (js2-node-pos pn)))
(setq pn (make-js2-call-node :pos pos
(let ((pos (or at-pos (js2-current-token-beg)))
colon-pos
(name (js2-create-name-node t (js2-current-token-type) s))
- ns tt ref pn)
+ ns tt pn)
(catch 'return
(when (js2-match-token js2-COLONCOLON)
(setq ns name
Includes complex literals such as functions, object-literals,
array-literals, array comprehensions and regular expressions."
(let (pn ; parent node (usually return value)
- tt
- px-pos ; paren-expr pos
- len
- flags ; regexp flags
- expr)
+ tt)
(setq tt (js2-current-token-type))
(cond
+ ((= tt js2-CLASS)
+ (js2-parse-class-expr))
((= tt js2-FUNCTION)
- (js2-parse-function 'FUNCTION_EXPRESSION))
+ (js2-parse-function-expr))
((= tt js2-LB)
- (js2-parse-array-literal))
+ (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)
- (setq px-pos (js2-current-token-beg)
- expr (js2-parse-expr))
- (js2-must-match js2-RP "msg.no.paren")
- (setq pn (make-js2-paren-node :pos px-pos
- :expr expr
- :len (- (js2-current-token-end) px-pos)))
- (js2-node-add-children pn (js2-paren-node-expr pn))
- pn)
+ (js2-parse-paren-expr-or-generator-comp))
((= tt js2-XMLATTR)
(js2-must-have-xml)
(js2-parse-attribute-access))
(js2-record-face 'font-lock-string-face)))
((or (= tt js2-DIV) (= tt js2-ASSIGN_DIV))
;; Got / or /= which in this context means a regexp literal
- (setq px-pos (js2-current-token-beg))
- (setq flags (js2-read-regexp tt))
- (prog1
- (make-js2-regexp-node :pos px-pos
- :len (- js2-ts-cursor px-pos)
- :value (js2-current-token-string)
- :flags flags)
- (js2-set-face px-pos js2-ts-cursor 'font-lock-string-face 'record)
- (js2-record-text-property px-pos js2-ts-cursor 'syntax-table '(2))))
+ (let ((px-pos (js2-current-token-beg))
+ (flags (js2-read-regexp tt))
+ (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)
+ (js2-record-text-property px-pos end 'syntax-table '(2)))))
((or (= tt js2-NULL)
(= tt js2-THIS)
+ (= tt js2-SUPER)
(= tt js2-FALSE)
(= tt js2-TRUE))
(make-js2-keyword-node :type tt))
+ ((= tt 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)
+ (progn
+ (js2-unget-token) ; Put back the right paren.
+ ;; 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)))
+ ((= 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))
;; the scanner or one of its subroutines reported the error.
(make-js2-error-node))
((= tt js2-EOF)
- (setq 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 px-pos :len len))
+ (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-name (tt)
+(defun js2-parse-name (_tt)
(let ((name (js2-current-token-string))
node)
(setq node (if js2-compiler-xml-available
(point)))
comma-pos))
-(defun js2-parse-array-literal ()
- (let ((pos (js2-current-token-beg))
- (end (js2-current-token-end))
- (after-lb-or-comma t)
+(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
(continue t))
(unless js2-is-in-destructuring
- (js2-push-scope (make-js2-scope))) ; for array comp
+ (js2-push-scope (make-js2-scope))) ; for the legacy array comp
(while continue
(setq tt (js2-get-token))
(cond
(= 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
- end (js2-current-token-end)
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))
- (when (and after-comma (not js2-is-in-destructuring))
- (js2-parse-warn-trailing-comma "msg.array.trailing.comma"
- pos elems after-comma)))
+ (apply #'js2-node-add-children pn (js2-array-node-elems pn)))
;; destructuring binding
(js2-is-in-destructuring
(push (if (or (= tt js2-LC)
(not (cdr elems))) ; but no 2nd element
(js2-unget-token)
(setf continue nil
- pn (js2-parse-array-comprehension (car elems) pos)))
-
+ pn (js2-parse-legacy-array-comp (car elems) pos)))
;; another element
(t
(unless after-lb-or-comma
(js2-report-error "msg.no.bracket.arg"))
- (js2-unget-token)
- (push (js2-parse-assign-expr) elems)
+ (if (and (= tt js2-TRIPLEDOT)
+ (>= js2-language-version 200))
+ ;; spread operator
+ (push (js2-make-unary tt 'js2-parse-assign-expr)
+ elems)
+ (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-array-comprehension (expr pos)
- "Parse a JavaScript 1.7 Array Comprehension.
+(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 (loops loop first prev filter if-pos result)
- (while (= (js2-get-token) js2-FOR)
- (let ((prev (car loops))) ; rearrange scope chain
- (push (setq loop (js2-parse-array-comp-loop)) loops)
- (if prev ; each loop is parent scope to the next one
- (setf (js2-scope-parent-scope loop) prev)
- ; first loop takes expr scope's parent
- (setf (js2-scope-parent-scope (setq first loop))
- (js2-scope-parent-scope js2-current-scope)))))
- (js2-unget-token)
- ;; set expr scope's parent to the last loop
- (setf (js2-scope-parent-scope js2-current-scope) (car loops))
- (if (/= (js2-get-token) js2-IF)
- (js2-unget-token)
- (setq if-pos (- (js2-current-token-beg) pos) ; relative
- filter (js2-parse-condition)))
+ (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-array-comp-node :pos pos
- :len (- js2-ts-cursor pos)
- :result expr
- :loops (nreverse loops)
- :filter (car filter)
- :lp (js2-relpos (second filter) pos)
- :rp (js2-relpos (third filter) pos)
- :if-pos if-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))
(apply #'js2-node-add-children result expr (car filter)
- (js2-array-comp-node-loops result))
- (setq js2-current-scope first) ; pop to the first loop
+ (js2-comp-node-loops result))
result))
-(defun js2-parse-array-comp-loop ()
- "Parse a 'for [each] (foo [in|of] bar)' expression in an Array comprehension.
-Last token peeked should be the initial FOR."
- (let ((pos (js2-current-token-beg))
- (pn (make-js2-array-comp-loop-node))
- tt iter obj foreach-p forof-p in-pos each-pos lp rp)
- (assert (= (js2-current-token-type) js2-FOR))
- (js2-push-scope pn)
+(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)
(unwind-protect
(progn
- (when (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 (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-array-comp-loop-node-iterator pn) iter
- (js2-array-comp-loop-node-object pn) obj
- (js2-array-comp-loop-node-in-pos pn) in-pos
- (js2-array-comp-loop-node-each-pos pn) each-pos
- (js2-array-comp-loop-node-foreach-p pn) foreach-p
- (js2-array-comp-loop-node-forof-p pn) forof-p
- (js2-array-comp-loop-node-lp pn) lp
- (js2-array-comp-loop-node-rp pn) rp)
- (js2-node-add-children pn iter obj))
- (js2-pop-scope))
+ (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)))
+ (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))
+ 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")
+ (js2-parse-class pos 'CLASS_STATEMENT (js2-create-name-node t))))
+
+(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)
+ (when name
+ (js2-set-face (js2-node-pos name) (js2-node-end name)
+ 'font-lock-function-name-face 'record))
+ (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 (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-parse-object-literal-elems (&optional class-p)
(let ((pos (js2-current-token-beg))
- tt elems result after-comma
- (continue t))
+ (static nil)
+ (continue t)
+ tt elems elem after-comma)
(while continue
- (setq tt (js2-get-token))
+ (setq static (and class-p (js2-match-token js2-STATIC))
+ tt (js2-get-token)
+ elem nil)
(cond
;; {foo: ...}, {'foo': ...}, {foo, bar, ...},
- ;; {get foo() {...}}, or {set foo(x) {...}}
+ ;; {get foo() {...}}, {set foo(x) {...}}, or {foo(x) {...}}
+ ;; TODO(sdh): support *foo() {...}
((or (js2-valid-prop-name-token tt)
(= tt js2-STRING))
(setq after-comma nil
- result (js2-parse-named-prop tt))
- (if (and (null result)
+ elem (js2-parse-named-prop tt))
+ (if (and (null elem)
(not js2-recover-from-parse-errors))
- (setq continue nil)
- (push result elems)))
+ (setq continue nil)))
+ ;; {[Symbol.iterator]: ...}
+ ((and (= tt js2-LB)
+ (>= js2-language-version 200))
+ (let ((expr (js2-parse-expr)))
+ (js2-must-match js2-RB "msg.missing.computed.rb")
+ (setq after-comma nil
+ elem (js2-parse-plain-property expr))))
;; {12: x} or {10.7: x}
((= tt js2-NUMBER)
- (setq after-comma nil)
- (push (js2-parse-plain-property (make-js2-number-node)) elems))
- ;; trailing comma
- ((= tt js2-RC)
+ (setq after-comma nil
+ elem (js2-parse-plain-property (make-js2-number-node))))
+ ;; 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-report-error "msg.bad.prop")
(unless js2-recover-from-parse-errors
(setq continue nil)))) ; end switch
- (if (js2-match-token js2-COMMA)
- (setq after-comma (js2-current-token-end))
- (setq continue nil))) ; end loop
+ ;; 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 ((comma (js2-match-token js2-COMMA)))
+ (if class-p
+ (if comma
+ (js2-report-error "msg.class.unexpected.comma"))
+ (if comma
+ (setq after-comma (js2-current-token-end))
+ (setq continue nil))))
+ ;; Append any parsed element.
+ (if elem (push elem elems))) ; end loop
(js2-must-match js2-RC "msg.no.brace.prop")
- (setq result (make-js2-object-node :pos pos
- :len (- js2-ts-cursor pos)
- :elems (nreverse elems)))
- (apply #'js2-node-add-children result (js2-object-node-elems result))
- result))
+ (nreverse elems)))
(defun js2-parse-named-prop (tt)
"Parse a name, string, or getter/setter object property.
(js2-set-face ppos pend 'font-lock-keyword-face 'record) ; get/set
(js2-record-face 'font-lock-function-name-face) ; for peeked name
(setq name (js2-create-name-node)) ; discard get/set & use peeked name
- (js2-parse-getter-setter-prop ppos name (string= prop "get")))
- ;; Abbreviated destructuring binding, e.g. {a, b} = c;
- ;; XXX: To be honest, the value of `js2-is-in-destructuring' becomes t only
- ;; when patterns are used in variable declarations, function parameters,
- ;; catch-clause, and iterators.
- ;; We have to set `js2-is-in-destructuring' to t when the current
- ;; expressions are on the left side of any assignment, but it's difficult
- ;; because it requires looking ahead of expression.
- ((and js2-is-in-destructuring
- (= tt js2-NAME)
- (let ((ctk (js2-peek-token)))
- (or (= ctk js2-COMMA)
- (= ctk js2-RC)
- (js2-valid-prop-name-token ctk))))
- name)
+ (js2-parse-getter-setter-prop ppos name prop))
+ ;; method definition: {f() {...}}
+ ((and (= (js2-peek-token) js2-LP)
+ (>= js2-language-version 200))
+ (js2-set-face ppos pend 'font-lock-keyword-face 'record) ; name
+ (js2-parse-getter-setter-prop ppos name ""))
;; regular prop
(t
(prog1
(defun js2-parse-plain-property (prop)
"Parse a non-getter/setter property in an object literal.
PROP is the node representing the property: a number, name or string."
- (js2-must-match js2-COLON "msg.no.colon.prop")
- (let* ((pos (js2-node-pos prop))
- (colon (- (js2-current-token-beg) pos))
- (expr (js2-parse-assign-expr))
- (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-getter-setter-prop (pos prop get-p)
+ (let* ((tt (js2-get-token))
+ (pos (js2-node-pos prop))
+ colon expr result)
+ (cond
+ ;; Abbreviated property, as in {foo, bar}
+ ((and (>= js2-language-version 200)
+ (or (= tt js2-COMMA)
+ (= tt js2-RC)))
+ (js2-unget-token)
+ (setq result (make-js2-object-prop-node
+ :pos pos
+ :left prop
+ :right prop
+ :op-pos (js2-current-token-len)))
+ (js2-node-add-children result prop)
+ (js2-node-set-prop result 'SHORTHAND t)
+ result)
+ ;; Normal property
+ (t
+ (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-getter-setter-prop (pos prop type-string)
"Parse getter or setter property in an object literal.
JavaScript syntax is:
POS is the start position of the `get' or `set' keyword.
PROP is the `js2-name-node' representing the property name.
GET-P is non-nil if the keyword was `get'."
- (let ((type (if get-p js2-GET js2-SET))
+ (let ((type (cond
+ ((string= "get" type-string) js2-GET)
+ ((string= "set" type-string) js2-SET)
+ (t js2-FUNCTION)))
result end
- (fn (js2-parse-function 'FUNCTION_EXPRESSION)))
+ (fn (js2-parse-function-expr)))
;; it has to be an anonymous function, as we already parsed the name
(if (/= (js2-node-type fn) js2-FUNCTION)
(js2-report-error "msg.bad.prop")
followed by an opening brace.")
(defconst js2-indent-operator-re
- (concat "[-+*/%<>=&^|?:.]\\([^-+*/]\\|$\\)\\|"
+ (concat "[-+*/%<>&^|?:.]\\([^-+*/]\\|$\\)\\|!?=\\|"
(regexp-opt '("in" "instanceof") 'words))
"Regular expression matching operators that affect indentation
of continued expressions.")
(syntax-ppss (point))))
(cond ((nth 3 parse)
(re-search-forward
- (concat "\\([^\\]\\|^\\)" (string (nth 3 parse)))
+ (concat "\\(\\=\\|[^\\]\\|^\\)" (string (nth 3 parse)))
(save-excursion (end-of-line) (point)) t))
((nth 7 parse)
(forward-line))
"Search forward but ignore strings and comments.
Invokes `re-search-forward' but treats the buffer as if strings
and comments have been removed."
- (let ((saved-point (point))
- (search-expr
- (cond ((null count)
- '(js2-re-search-forward-inner regexp bound 1))
- ((< count 0)
- '(js2-re-search-backward-inner regexp bound (- count)))
- ((> count 0)
- '(js2-re-search-forward-inner regexp bound count)))))
+ (let ((saved-point (point)))
(condition-case err
- (eval search-expr)
+ (cond ((null count)
+ (js2-re-search-forward-inner regexp bound 1))
+ ((< count 0)
+ (js2-re-search-backward-inner regexp bound (- count)))
+ ((> count 0)
+ (js2-re-search-forward-inner regexp bound count)))
(search-failed
(goto-char saved-point)
(unless noerror
"Search backward but ignore strings and comments.
Invokes `re-search-backward' but treats the buffer as if strings
and comments have been removed."
- (let ((saved-point (point))
- (search-expr
- (cond ((null count)
- '(js2-re-search-backward-inner regexp bound 1))
- ((< count 0)
- '(js2-re-search-forward-inner regexp bound (- count)))
- ((> count 0)
- '(js2-re-search-backward-inner regexp bound count)))))
+ (let ((saved-point (point)))
(condition-case err
- (eval search-expr)
+ (cond ((null count)
+ (js2-re-search-backward-inner regexp bound 1))
+ ((< count 0)
+ (js2-re-search-forward-inner regexp bound (- count)))
+ ((> count 0)
+ (js2-re-search-backward-inner regexp bound count)))
(search-failed
(goto-char saved-point)
(unless noerror
(backward-char)
(when (js2-looking-at-operator-p)
(backward-char)
- (not (looking-at "\\*\\|++\\|--\\|/[/*]")))))))
+ (not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]")))))))
(defun js2-end-of-do-while-loop-p ()
"Return non-nil if word after point is `while' of a do-while
(js2-backward-sws))
(not (eq (char-before) ?\;)))
(js2-same-line pos)))))
- (condition-case err
+ (condition-case _
(backward-sexp)
(scan-error (setq at-opening-bracket t))))
(when (looking-at js2-declaration-keyword-re)
"Return the proper indentation for the current line."
(save-excursion
(back-to-indentation)
- (let ((ctrl-stmt-indent (js2-ctrl-statement-indentation))
- (same-indent-p (looking-at "[]})]\\|\\<case\\>\\|\\<default\\>"))
- (continued-expr-p (js2-continued-expression-p))
- (declaration-indent (and js2-pretty-multiline-declarations
- (js2-multiline-decl-indentation)))
- (bracket (nth 1 parse-status))
- beg)
+ (let* ((ctrl-stmt-indent (js2-ctrl-statement-indentation))
+ (at-closing-bracket (looking-at "[]})]"))
+ (same-indent-p (or at-closing-bracket
+ (looking-at "\\<case\\>[^:]")
+ (and (looking-at "\\<default:")
+ (save-excursion
+ (js2-backward-sws)
+ (not (memq (char-before) '(?, ?{)))))))
+ (continued-expr-p (js2-continued-expression-p))
+ (declaration-indent (and js2-pretty-multiline-declarations
+ (js2-multiline-decl-indentation)))
+ (bracket (nth 1 parse-status))
+ beg indent)
(cond
;; indent array comprehension continuation lines specially
((and bracket
(and (eq js2-pretty-multiline-declarations 'all)
(looking-at js2-declaration-keyword-re)
(goto-char (1+ (match-end 0))))
- (cond (same-indent-p
- (current-column))
- (continued-expr-p
- (+ (current-column) (* 2 js2-basic-offset)))
- (t
- (+ (current-column) js2-basic-offset))))
+ (setq indent
+ (cond (same-indent-p
+ (current-column))
+ (continued-expr-p
+ (+ (current-column) (* 2 js2-basic-offset)))
+ (t
+ (+ (current-column) js2-basic-offset))))
+ (if (and js2-indent-switch-body
+ (not at-closing-bracket)
+ (looking-at "\\_<switch\\_>"))
+ (+ indent js2-basic-offset)
+ indent))
(t
(unless same-indent-p
(forward-char)
(forward-line 0) ; move to bol
(1+ (count-lines (point-min) (point)))))
positions pos main-pos anchor arglist-cont same-indent
- prev-line-col basic-offset computed-pos)
+ 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))
(back-to-indentation)
(js2-backward-sws)
(back-to-indentation)
- (setq prev-line-col (current-column)))
+ (current-column))
js2-basic-offset))
positions)
(back-to-indentation)
(js2-backward-sws)
(back-to-indentation)
- (setq prev-line-col (current-column)))
+ (current-column))
(* 2 js2-basic-offset)))
positions)
(defun js2-indent-line (&optional bounce-backwards)
"Indent the current line as JavaScript source text."
(interactive)
- (let (parse-status current-indent offset indent-col moved
+ (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))
(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 `espresso-mode', while retaining the asynchronous error/warning
+such as `js-mode', while retaining the asynchronous error/warning
highlighting features of `js2-mode'."
:group 'js2-mode
:lighter " js-lint"
(let ((inhibit-read-only t))
(erase-buffer)
(dolist (err all-errs)
- (destructuring-bind ((msg-key beg end &rest) type line) err
+ (destructuring-bind ((msg-key beg _end &rest) type line) err
(insert-text-button
(format "line %d: %s" line (js2-get-msg msg-key))
'face type
(put 'js2-mode 'find-tag-default-function #'js2-mode-find-tag)
(set (make-local-variable 'electric-indent-chars)
- (append '("{" "}" "(" ")" "[" "]" ":" ";" "," "*")
- electric-indent-chars))
+ (append "{}()[]:;,*." electric-indent-chars))
(set (make-local-variable 'electric-layout-rules)
'((?\; . after) (?\{ . after) (?\} . before)))
(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."
- (if (eq buffer (current-buffer))
- (js2-reparse)
- ;; reparse when the buffer is selected again
- (with-current-buffer buffer
- (add-hook 'window-configuration-change-hook
- #'js2-mode-idle-reparse-inner
- nil t))))
+ (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
t)
(js2-reparse))
-(defun js2-mode-edit (beg end len)
+(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)
+(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."
(js2-node-short-name (js2-node-parent node))
"nil"))))))
-(defun js2-mode-hide-overlay (&optional p1 p2)
+(defun js2-mode-hide-overlay (&optional _p1 p2)
"Remove the debugging overlay when the point moves.
P1 and P2 are the old and new values of point, respectively."
(when js2-mode-node-overlay
(dolist (e (js2-ast-root-warnings js2-mode-ast))
(js2-mode-show-warn-or-err e 'js2-warning))))
-(defun js2-echo-error (old-point new-point)
+(defun js2-echo-error (_old-point new-point)
"Called by point-motion hooks."
(let ((msg (get-text-property new-point 'help-echo)))
(when (and (stringp msg)
(not (current-message)))
(message msg))))
-(defalias #'js2-echo-help #'js2-echo-error)
+(defalias 'js2-echo-help #'js2-echo-error)
-(defun js2-line-break (&optional soft)
+(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."
(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* ((col (current-column))
- (quote-char (nth 3 parse-status))
- (string-beg (nth 8 parse-status))
+ (let* ((quote-char (nth 3 parse-status))
(at-eol (eq js2-concat-multiline-strings 'eol)))
(insert quote-char)
- (if at-eol
- (insert " +\n")
- (insert "\n"))
+ (insert (if at-eol " +\n" "\n"))
(unless at-eol
(insert "+ "))
(js2-indent-line)
"Toggle point between bol and first non-whitespace char in line.
Also moves past comment delimiters when inside comments."
(interactive)
- (let (node beg)
+ (let (node)
(cond
((bolp)
(back-to-indentation))
;; 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)
+(defun js2-isearch-open-invisible (_overlay)
;; We rely on the fact that isearch places point on the matched text.
(js2-mode-show-element))
(message "Oops - parsing failed")
(setq js2-mode-comments-hidden t)
(dolist (n (js2-ast-root-comments js2-mode-ast))
- (let ((format (js2-comment-node-format n)))
- (when (js2-block-comment-p n)
- (js2-mode-hide-comment n))))
+ (when (js2-block-comment-p n)
+ (js2-mode-hide-comment n)))
(js2-mode-hide-//-comments)))
(defun js2-mode-extend-//-comment (direction)
(while (and (zerop (forward-line direction))
(looking-at js2-mode-//-comment-re)
(eq indent (length (match-string 1))))
- (setq pos (point-at-eol))
- pos))))
+ (setq pos (point-at-eol)))
+ pos)))
(defun js2-mode-hide-//-comments ()
"Fold adjacent 1-line comments, showing only snippet of first one."
(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
- ;; 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 (minusp arg))
- (dotimes (i (- arg))
- (js2-backward-sws)
- (forward-char -1) ; Enter the node we backed up to.
- (when (setq node (js2-node-at-point (point) t))
- (setq pos (js2-node-abs-pos node))
- (let ((parens (js2-mode-forward-sexp-parens node pos)))
- (setq lp (car parens)
- rp (cdr parens)))
- (when (and lp (> start lp))
- (if (and rp (<= start rp))
- ;; Between parens, check if there's a child node we can jump.
- (when (setq child (js2-node-closest-child node (point) lp t))
- (setq pos (js2-node-abs-pos child)))
- ;; Before both parens.
- (setq pos lp)))
- (let ((state (parse-partial-sexp start pos)))
- (goto-char (if (not (zerop (car state)))
- ;; Stumble at the unbalanced paren if < 0, or
- ;; jump a bit further if > 0.
- (scan-sexps start -1)
- pos))))
- (unless pos (goto-char (point-min)))))
- (t
- ;; forward-sexp
- (dotimes (i 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 curren node.
- (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))))))))
+ (js2-reparse)
+ (let (forward-sexp-function
+ node (start (point)) pos lp rp child)
+ (cond
+ ;; 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 (minusp arg))
+ (dotimes (_ (- arg))
+ (js2-backward-sws)
+ (forward-char -1) ; Enter the node we backed up to.
+ (when (setq node (js2-node-at-point (point) t))
+ (setq pos (js2-node-abs-pos node))
+ (let ((parens (js2-mode-forward-sexp-parens node pos)))
+ (setq lp (car parens)
+ rp (cdr parens)))
+ (when (and lp (> start lp))
+ (if (and rp (<= start rp))
+ ;; Between parens, check if there's a child node we can jump.
+ (when (setq child (js2-node-closest-child node (point) lp t))
+ (setq pos (js2-node-abs-pos child)))
+ ;; Before both parens.
+ (setq pos lp)))
+ (let ((state (parse-partial-sexp start pos)))
+ (goto-char (if (not (zerop (car state)))
+ ;; Stumble at the unbalanced paren if < 0, or
+ ;; jump a bit further if > 0.
+ (scan-sexps start -1)
+ pos))))
+ (unless pos (goto-char (point-min)))))
+ (t
+ ;; forward-sexp
+ (dotimes (_ arg)
+ (js2-forward-sws)
+ (when (setq node (js2-node-at-point (point) t))
+ (setq pos (js2-node-abs-pos node))
+ (let ((parens (js2-mode-forward-sexp-parens node pos)))
+ (setq lp (car parens)
+ rp (cdr parens)))
+ (or
+ (when (and rp (<= start rp))
+ (if (> start lp)
+ (when (setq child (js2-node-closest-child node (point) rp))
+ (setq pos (js2-node-abs-end child)))
+ (setq pos (1+ rp))))
+ ;; No parens or child nodes, looks for the end of the curren node.
+ (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-array-comp-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)
(catch 'done
(js2-visit-ast
parent
- (lambda (node end-p)
+ (lambda (node _end-p)
(if (eq node parent)
t
(let ((pos (js2-node-pos node)) ;; Both relative values.
(let (beg end)
(js2-with-underscore-as-word-syntax
(save-excursion
- (if (and (not (looking-at "[A-Za-z0-9_$]"))
- (looking-back "[A-Za-z0-9_$]"))
+ (if (and (not (looking-at "[[:alnum:]_$]"))
+ (looking-back "[[:alnum:]_$]"))
(setq beg (progn (forward-word -1) (point))
end (progn (forward-word 1) (point)))
(setq beg (progn (forward-word 1) (point))
(js2-beginning-of-defun (1- arg))
t)))
(when (js2-end-of-defun)
- (if (>= arg -1)
- (js2-beginning-of-defun 1)
- (js2-beginning-of-defun (1+ arg))))))
+ (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
(let ((sib (save-excursion
(goto-char (mark))
(if (js2-mode-forward-sibling)
- (point))))
- node)
+ (point)))))
(if sib
(progn
(set-mark sib)