]> code.delx.au - gnu-emacs-elpa/commitdiff
Merge commit 'ac93b9eef9b6ac44d187b9688d68a7a5f205b3fe' from js2-mode
authorDmitry Gutov <dgutov@yandex.ru>
Fri, 24 Apr 2015 02:56:24 +0000 (05:56 +0300)
committerDmitry Gutov <dgutov@yandex.ru>
Fri, 24 Apr 2015 02:56:24 +0000 (05:56 +0300)
Conflicts:
packages/js2-mode/js2-mode.el

1  2 
packages/js2-mode/js2-mode.el
packages/js2-mode/tests/indent.el
packages/js2-mode/tests/parser.el

index 5bc0770e786c038c741be424faf15ea4bdc5092d,2dd5d9d8f3fd4e8cd0cffdd8a62d5319ce4c85dc..4fc2823657827197a3041f387ad210dbfa231614
@@@ -263,14 -263,37 +263,37 @@@ js2-mode also binds `js2-bounce-indent-
        b = 20,
        c = 30;
  
- If the value is not `all', and the first assigned value in
+ If the value is t, and the first assigned value in the
  declaration is a function/array/object literal spanning several
  lines, it won't be indented additionally:
  
    var o = {                   var bar = 2,
      foo: 3          vs.           o = {
    },                                foo: 3
-       bar = 2;                    };"
+       bar = 2;                    };
+ If the value is `all', it will always be indented additionally:
+   var o = {
+         foo: 3
+       };
+   var o = {
+         foo: 3
+       },
+       bar = 2;
+ If the value is `dynamic', it will be indented additionally only
+ if the declaration contains more than one variable:
+   var o = {
+     foo: 3
+   };
+   var o = {
+         foo: 3
+       },
+       bar = 2;"
    :group 'js2-mode
    :type 'symbol)
  (js2-mark-safe-local 'js2-pretty-multiline-declarations 'symbolp)
@@@ -650,11 -673,10 +673,10 @@@ which doesn't seem particularly useful
  (defvar js2-ARROW 162)         ; function arrow (=>)
  (defvar js2-CLASS 163)
  (defvar js2-EXTENDS 164)
- (defvar js2-STATIC 165)
- (defvar js2-SUPER 166)
- (defvar js2-TEMPLATE_HEAD 167)    ; part of template literal before substitution
- (defvar js2-NO_SUBS_TEMPLATE 168) ; template literal without substitutions
- (defvar js2-TAGGED_TEMPLATE 169)  ; tagged template literal
+ (defvar js2-SUPER 165)
+ (defvar js2-TEMPLATE_HEAD 166)    ; part of template literal before substitution
+ (defvar js2-NO_SUBS_TEMPLATE 167) ; template literal without substitutions
+ (defvar js2-TAGGED_TEMPLATE 168)  ; tagged template literal
  
  (defconst js2-num-tokens (1+ js2-TAGGED_TEMPLATE))
  
@@@ -1274,6 -1296,7 +1296,7 @@@ First match-group is the leading whites
  (defvar js2-mode-syntax-table
    (let ((table (make-syntax-table)))
      (c-populate-syntax-table table)
+     (modify-syntax-entry ?` "\"" table)
      table)
    "Syntax table used in `js2-mode' buffers.")
  
@@@ -1384,7 -1407,7 +1407,7 @@@ the correct number of ARGS must be prov
           "Invalid assignment left-hand side.")
  
  (js2-msg "msg.bad.decr"
-          "Invalid decerement operand.")
+          "Invalid decrement operand.")
  
  (js2-msg "msg.bad.incr"
           "Invalid increment operand.")
@@@ -2308,24 -2331,29 +2331,29 @@@ If any given node in NODES is nil, does
  (defun js2-node-get-enclosing-scope (node)
    "Return the innermost `js2-scope' node surrounding NODE.
  Returns nil if there is no enclosing scope node."
-   (let ((parent (js2-node-parent node)))
-     (while (not (js2-scope-p parent))
-       (setq parent (js2-node-parent parent)))
-     parent))
+   (while (and (setq node (js2-node-parent node))
+               (not (js2-scope-p node))))
+   node)
  
- (defun js2-get-defining-scope (scope name)
+ (defun js2-get-defining-scope (scope name &optional point)
    "Search up scope chain from SCOPE looking for NAME, a string or symbol.
- Returns `js2-scope' in which NAME is defined, or nil if not found."
+ Returns `js2-scope' in which NAME is defined, or nil if not found.
+ If POINT is non-nil, and if the found declaration type is
+ `js2-LET', also check that the declaration node is before POINT."
    (let ((sym (if (symbolp name)
                   name
                 (intern name)))
-         table
          result
          (continue t))
      (while (and scope continue)
        (if (or
-            (and (setq table (js2-scope-symbol-table scope))
-                 (assq sym table))
+            (let ((entry (cdr (assq sym (js2-scope-symbol-table scope)))))
+              (and entry
+                   (or (not point)
+                       (not (eq js2-LET (js2-symbol-decl-type entry)))
+                       (>= point
+                           (js2-node-abs-pos (js2-symbol-ast-node entry))))))
             (and (eq sym 'arguments)
                  (js2-function-node-p scope)))
            (setq continue nil
@@@ -5360,7 -5388,7 +5388,7 @@@ Returns logical OR of END_* flags.
    (let* ((rv js2-END_DROPS_OFF)
           (kids (js2-block-node-kids node))
           (n (car kids)))
-     ;; Check each statment.  If the statement can continue onto the next
+     ;; Check each statement.  If the statement can continue onto the next
      ;; one (i.e. END_DROPS_OFF is set), then check the next statement.
      (while (and n (js2-flag-set-p rv js2-END_DROPS_OFF))
        (js2-clear-flag rv js2-END_DROPS_OFF)
@@@ -5684,7 -5712,7 +5712,7 @@@ into temp buffers.
      let
      new null
      return
-     static super switch
+     super switch
      this throw true try typeof
      var void
      while with
                 js2-LET
                 js2-NEW js2-NULL
                 js2-RETURN
-                js2-STATIC js2-SUPER js2-SWITCH
+                js2-SUPER js2-SWITCH
                 js2-THIS js2-THROW js2-TRUE js2-TRY js2-TYPEOF
                 js2-VAR
                 js2-WHILE js2-WITH
@@@ -5730,7 -5758,7 +5758,7 @@@ The values are default faces to use fo
  
  ;; 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)
+ (defconst js2-reserved-words '(class enum export extends import static super)
    "Future reserved keywords in ECMAScript 5.1.")
  
  (defconst js2-keyword-names
@@@ -6158,8 -6186,11 +6186,11 @@@ its relevant fields and puts it into `j
                               (setf (js2-token-beg token) (- js2-ts-cursor 2))
                               (js2-skip-line)
                               (setf (js2-token-comment-type token) 'line)
-                              ;; include newline so highlighting goes to end of window
-                              (cl-incf (js2-token-end token))
+                              ;; include newline so highlighting goes to end of
+                              ;; window, if there actually is a newline; if we
+                              ;; hit eof, then implicitly there isn't
+                              (unless js2-ts-hit-eof
+                                (cl-incf (js2-token-end token)))
                               (throw 'return js2-COMMENT))
                             ;; is it a /* comment?
                             (when (js2-match-char ?*)
@@@ -6774,7 -6805,7 +6805,7 @@@ Shown at or above `js2-highlight-level
  
  (defun js2-parse-highlight-member-expr-node (node)
    "Perform syntax highlighting of EcmaScript built-in properties.
- The variable `js2-highlight-level' governs this highighting."
+ The variable `js2-highlight-level' governs this highlighting."
    (let (face target prop name pos end parent call-p callee)
      (cond
       ;; case 1:  simple name, e.g. foo
@@@ -7035,7 -7066,7 +7066,7 @@@ it is considered declared.
          (unless (or (member name js2-global-externs)
                      (member name js2-default-externs)
                      (member name js2-additional-externs)
-                     (js2-get-defining-scope scope name))
+                     (js2-get-defining-scope scope name pos))
            (js2-report-warning "msg.undeclared.variable" name pos (- end pos)
                                'js2-external-variable))))
      (setq js2-recorded-identifiers nil)))
@@@ -8089,7 -8120,8 +8120,8 @@@ node are given relative start position
          js2-ERROR
          js2-SEMI
          js2-CLASS
-         js2-FUNCTION)
+         js2-FUNCTION
+         js2-EXPORT)
    "List of tokens that don't do automatic semicolon insertion.")
  
  (defconst js2-autoinsert-semi-and-warn
@@@ -8524,9 -8556,13 +8556,13 @@@ invalid export statements.
      (when from-clause
        (push from-clause children))
      (when declaration
-       (push declaration children))
+       (push declaration children)
+       (when (not (js2-function-node-p declaration))
+         (js2-auto-insert-semicolon declaration)))
      (when default
-       (push default children))
+       (push default children)
+       (when (not (js2-function-node-p default))
+         (js2-auto-insert-semicolon default)))
      (let ((node (make-js2-export-node
                    :pos beg
                    :len (- (js2-current-token-end) beg)
    "Parse a for, for-in or for each-in statement.
  Last matched token must be js2-FOR."
    (let ((for-pos (js2-current-token-beg))
+         (tmp-scope (make-js2-scope))
          pn is-for-each is-for-in-or-of is-for-of
          in-pos each-pos tmp-pos
          init  ; Node init is also foo in 'foo in object'.
      (if (js2-must-match js2-LP "msg.no.paren.for")
          (setq lp (- (js2-current-token-beg) for-pos)))
      (setq tt (js2-get-token))
-     ;; 'for' makes local scope
-     (js2-push-scope (make-js2-scope))
+     ;; Capture identifiers inside parens.  We can't create the node
+     ;; (and use it as the current scope) until we know its type.
+     (js2-push-scope tmp-scope)
      (unwind-protect
-         ;; parse init clause
-         (let ((js2-in-for-init t))  ; set as dynamic variable
-           (cond
-            ((= tt js2-SEMI)
-             (js2-unget-token)
-             (setq init (make-js2-empty-expr-node)))
-            ((or (= tt js2-VAR) (= tt js2-LET))
-             (setq init (js2-parse-variables tt (js2-current-token-beg))))
-            (t
-             (js2-unget-token)
-             (setq init (js2-parse-expr)))))
-       (if (or (js2-match-token js2-IN)
-               (and (>= js2-language-version 200)
-                    (js2-match-contextual-kwd "of")
-                    (setq is-for-of t)))
-           (setq is-for-in-or-of t
-                 in-pos (- (js2-current-token-beg) for-pos)
-                 ;; scope of iteration target object is not the scope we've created above.
-                 ;; stash current scope temporary.
-                 cond (let ((js2-current-scope (js2-scope-parent-scope js2-current-scope)))
-                        (js2-parse-expr)))  ; object over which we're iterating
-         ;; else ordinary for loop - parse cond and incr
-         (js2-must-match js2-SEMI "msg.no.semi.for")
-         (setq cond (if (= (js2-peek-token) js2-SEMI)
-                        (make-js2-empty-expr-node) ; no loop condition
-                      (js2-parse-expr)))
-         (js2-must-match js2-SEMI "msg.no.semi.for.cond")
-         (setq tmp-pos (js2-current-token-end)
-               incr (if (= (js2-peek-token) js2-RP)
-                        (make-js2-empty-expr-node :pos tmp-pos)
-                      (js2-parse-expr))))
-       (if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
-           (setq rp (- (js2-current-token-beg) for-pos)))
-       (if (not is-for-in-or-of)
-           (setq pn (make-js2-for-node :init init
-                                       :condition cond
-                                       :update incr
-                                       :lp lp
-                                       :rp rp))
-         ;; cond could be null if 'in obj' got eaten by the init node.
-         (if (js2-infix-node-p init)
-             ;; it was (foo in bar) instead of (var foo in bar)
-             (setq cond (js2-infix-node-right init)
-                   init (js2-infix-node-left init))
-           (if (and (js2-var-decl-node-p init)
-                    (> (length (js2-var-decl-node-kids init)) 1))
-               (js2-report-error "msg.mult.index")))
-         (setq pn (make-js2-for-in-node :iterator init
-                                        :object cond
-                                        :in-pos in-pos
-                                        :foreach-p is-for-each
-                                        :each-pos each-pos
-                                        :forof-p is-for-of
-                                        :lp lp
-                                        :rp rp)))
-       (unwind-protect
-           (progn
-             (js2-enter-loop pn)
-             ;; We have to parse the body -after- creating the loop node,
-             ;; so that the loop node appears in the js2-loop-set, allowing
-             ;; break/continue statements to find the enclosing loop.
-             (setf body (js2-parse-statement)
-                   (js2-loop-node-body pn) body
-                   (js2-node-pos pn) for-pos
-                   (js2-node-len pn) (- (js2-node-end body) for-pos))
-             (js2-node-add-children pn init cond incr body))
-         ;; finally
-         (js2-exit-loop))
+         (progn
+           ;; parse init clause
+           (let ((js2-in-for-init t))  ; set as dynamic variable
+             (cond
+              ((= tt js2-SEMI)
+               (js2-unget-token)
+               (setq init (make-js2-empty-expr-node)))
+              ((or (= tt js2-VAR) (= tt js2-LET))
+               (setq init (js2-parse-variables tt (js2-current-token-beg))))
+              (t
+               (js2-unget-token)
+               (setq init (js2-parse-expr)))))
+           (if (or (js2-match-token js2-IN)
+                   (and (>= js2-language-version 200)
+                        (js2-match-contextual-kwd "of")
+                        (setq is-for-of t)))
+               (setq is-for-in-or-of t
+                     in-pos (- (js2-current-token-beg) for-pos)
+                     ;; scope of iteration target object is not the scope we've created above.
+                     ;; stash current scope temporary.
+                     cond (let ((js2-current-scope (js2-scope-parent-scope js2-current-scope)))
+                            (js2-parse-expr)))  ; object over which we're iterating
+             ;; else ordinary for loop - parse cond and incr
+             (js2-must-match js2-SEMI "msg.no.semi.for")
+             (setq cond (if (= (js2-peek-token) js2-SEMI)
+                            (make-js2-empty-expr-node) ; no loop condition
+                          (js2-parse-expr)))
+             (js2-must-match js2-SEMI "msg.no.semi.for.cond")
+             (setq tmp-pos (js2-current-token-end)
+                   incr (if (= (js2-peek-token) js2-RP)
+                            (make-js2-empty-expr-node :pos tmp-pos)
+                          (js2-parse-expr)))))
        (js2-pop-scope))
+     (if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
+         (setq rp (- (js2-current-token-beg) for-pos)))
+     (if (not is-for-in-or-of)
+         (setq pn (make-js2-for-node :init init
+                                     :condition cond
+                                     :update incr
+                                     :lp lp
+                                     :rp rp))
+       ;; cond could be null if 'in obj' got eaten by the init node.
+       (if (js2-infix-node-p init)
+           ;; it was (foo in bar) instead of (var foo in bar)
+           (setq cond (js2-infix-node-right init)
+                 init (js2-infix-node-left init))
+         (if (and (js2-var-decl-node-p init)
+                  (> (length (js2-var-decl-node-kids init)) 1))
+             (js2-report-error "msg.mult.index")))
+       (setq pn (make-js2-for-in-node :iterator init
+                                      :object cond
+                                      :in-pos in-pos
+                                      :foreach-p is-for-each
+                                      :each-pos each-pos
+                                      :forof-p is-for-of
+                                      :lp lp
+                                      :rp rp)))
+     ;; Transplant the declarations.
+     (setf (js2-scope-symbol-table pn)
+           (js2-scope-symbol-table tmp-scope))
+     (unwind-protect
+         (progn
+           (js2-enter-loop pn)
+           ;; We have to parse the body -after- creating the loop node,
+           ;; so that the loop node appears in the js2-loop-set, allowing
+           ;; break/continue statements to find the enclosing loop.
+           (setf body (js2-parse-statement)
+                 (js2-loop-node-body pn) body
+                 (js2-node-pos pn) for-pos
+                 (js2-node-len pn) (- (js2-node-end body) for-pos))
+           (js2-node-add-children pn init cond incr body))
+       ;; finally
+       (js2-exit-loop))
      pn))
  
  (defun js2-parse-try ()
@@@ -9151,7 -9193,7 +9193,7 @@@ Returns the parsed `js2-var-decl-node' 
  (defun js2-parse-let (pos &optional stmt-p)
    "Parse a let expression or statement.
  A let-expression is of the form `let (vars) expr'.
- A let-statment is of the form `let (vars) {statements}'.
+ A let-statement is of the form `let (vars) {statements}'.
  The third form of let is a variable declaration list, handled
  by `js2-parse-variables'."
    (let ((pn (make-js2-let-node :pos pos))
@@@ -9256,11 -9298,10 +9298,10 @@@ If NODE is non-nil, it is the AST node 
        (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))))
+                                       :expr expr)))
          (js2-node-add-children pn (js2-paren-node-expr pn))
          (js2-must-match js2-RP "msg.no.paren")
+         (setf (js2-node-len pn) (- (js2-current-token-end) px-pos))
          pn)))))
  
  (defun js2-parse-expr (&optional oneshot)
@@@ -10124,7 -10165,7 +10165,7 @@@ EXPR is the first expression after the 
  POS is the beginning of the LB token preceding EXPR.
  We should have just parsed the 'for' keyword before calling this function."
    (let ((current-scope js2-current-scope)
 -        loops first filter result)
 +        loops filter result)
      (unwind-protect
          (progn
            (while (js2-match-token js2-FOR)
                (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))))
 +          (setf (js2-scope-parent-scope (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))
                                       :loops (nreverse loops)
                                       :filters (and filter (list (car filter)))
                                       :form 'LEGACY_ARRAY))
+     ;; Set comp loop's parent to the last loop.
+     ;; TODO: Get rid of the bogus expr scope.
+     (setf (js2-scope-parent-scope result) first)
      (apply #'js2-node-add-children result expr (car filter)
             (js2-comp-node-loops result))
      result))
@@@ -10302,9 -10346,14 +10346,14 @@@ If ONLY-OF-P is non-nil, only the 'for 
          (continue t)
          tt elems elem after-comma)
      (while continue
-       (setq static (and class-p (js2-match-token js2-STATIC))
-             tt (js2-get-prop-name-token)
+       (setq tt (js2-get-prop-name-token)
+             static nil
              elem nil)
+       (when (and class-p (= js2-NAME tt)
+                  (string= "static" (js2-current-token-string)))
+         (js2-record-face 'font-lock-keyword-face)
+         (setq static t
+               tt (js2-get-prop-name-token)))
        (cond
         ;; {foo: ...}, {'foo': ...}, {foo, bar, ...},
         ;; {get foo() {...}}, {set foo(x) {...}}, or {foo(x) {...}}
            (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))))
+       (let ((tok (js2-get-prop-name-token)))
+         (if (eq tok js2-COMMA)
+             (if class-p
+                 (js2-report-error "msg.class.unexpected.comma")
+               (setq after-comma (js2-current-token-end)))
+           (js2-unget-token)
+           (unless class-p (setq continue nil))))
        ;; Append any parsed element.
        (if elem (push elem elems)))       ; end loop
      (js2-must-match js2-RC "msg.no.brace.prop")
@@@ -10754,6 -10803,40 +10803,40 @@@ In particular, return the buffer positi
        (goto-char for-kwd)
        (current-column))))
  
+ (defun js2-maybe-goto-declaration-keyword-end (bracket)
+   "Helper function for `js2-proper-indentation'.
+ Depending on the value of `js2-pretty-multiline-declarations',
+ move point to the end of a variable declaration keyword so that
+ indentation is aligned to that column."
+   (cond
+    ((eq js2-pretty-multiline-declarations 'all)
+     (when (looking-at js2-declaration-keyword-re)
+       (goto-char (1+ (match-end 0)))))
+    ((eq js2-pretty-multiline-declarations 'dynamic)
+     (let (declaration-keyword-end
+           at-closing-bracket-p
+           comma-p)
+       (when (looking-at js2-declaration-keyword-re)
+         ;; Preserve the match data lest it somehow be overridden.
+         (setq declaration-keyword-end (match-end 0))
+         (save-excursion
+           (goto-char bracket)
+           (setq at-closing-bracket-p
+                 ;; Handle scan errors gracefully.
+                 (condition-case nil
+                     (progn
+                       ;; Use the regular `forward-sexp-function' because the
+                       ;; normal one for this mode uses the AST.
+                       (let (forward-sexp-function)
+                         (forward-sexp))
+                       t)
+                   (error nil)))
+           (when at-closing-bracket-p
+             (js2-forward-sws)
+             (setq comma-p (looking-at-p ","))))
+         (when comma-p
+           (goto-char (1+ declaration-keyword-end))))))))
  (defun js2-proper-indentation (parse-status)
    "Return the proper indentation for the current line."
    (save-excursion
                                  (looking-at ")"))
              (backward-list))
            (back-to-indentation)
-           (and (eq js2-pretty-multiline-declarations 'all)
-                (looking-at js2-declaration-keyword-re)
-                (goto-char (1+ (match-end 0))))
+           (js2-maybe-goto-declaration-keyword-end bracket)
            (setq indent
                  (cond (same-indent-p
                         (current-column))
@@@ -11088,22 -11169,24 +11169,24 @@@ If so, we don't ever want to use bounce
            offset (- (point) (save-excursion
                                (back-to-indentation)
                                (point))))
-     (js2-with-underscore-as-word-syntax
-      (if (nth 4 parse-status)
-          (js2-lineup-comment parse-status)
-        (setq indent-col (js2-proper-indentation parse-status))
-        ;; See comments below about `js2-mode-last-indented-line'.
-        (cond
-         ;; bounce-indenting is disabled during electric-key indent.
-         ;; It doesn't work well on first line of buffer.
-         ((and js2-bounce-indent-p
-               (not (js2-same-line (point-min)))
-               (not (js2-1-line-comment-continuation-p)))
-          (js2-bounce-indent indent-col parse-status bounce-backwards))
-         ;; just indent to the guesser's likely spot
-         (t (indent-line-to indent-col))))
-      (when (cl-plusp offset)
-        (forward-char offset)))))
+     ;; Don't touch multiline strings.
+     (unless (nth 3 parse-status)
+       (js2-with-underscore-as-word-syntax
+         (if (nth 4 parse-status)
+             (js2-lineup-comment parse-status)
+           (setq indent-col (js2-proper-indentation parse-status))
+           ;; See comments below about `js2-mode-last-indented-line'.
+           (cond
+            ;; bounce-indenting is disabled during electric-key indent.
+            ;; It doesn't work well on first line of buffer.
+            ((and js2-bounce-indent-p
+                  (not (js2-same-line (point-min)))
+                  (not (js2-1-line-comment-continuation-p)))
+             (js2-bounce-indent indent-col parse-status bounce-backwards))
+            ;; just indent to the guesser's likely spot
+            (t (indent-line-to indent-col))))
+         (when (cl-plusp offset)
+           (forward-char offset))))))
  
  (defun js2-indent-region (start end)
    "Indent the region, but don't use bounce indenting."
@@@ -11543,13 -11626,6 +11626,6 @@@ The last element is optional.  When pre
          (when (overlay-get o 'js2-error)
            (delete-overlay o))))))
  
- (defun js2-error-at-point (&optional pos)
-   "Return non-nil if there's an error overlay at POS.
- Defaults to point."
-   (cl-loop with pos = (or pos (point))
-            for o in (overlays-at pos)
-            thereis (overlay-get o 'js2-error)))
  (defun js2-mode-apply-deferred-properties ()
    "Apply fontifications and other text properties recorded during parsing."
    (when (cl-plusp js2-highlight-level)
@@@ -11619,9 -11695,6 +11695,6 @@@ was found on `point-entered' or in `cur
                 (not (current-message)))
        (message msg))))
  
- ;; FIXME: Why do we keep this?
- (define-obsolete-function-alias 'js2-echo-help #'js2-echo-error "forever")
  (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
@@@ -11678,12 -11751,7 +11751,7 @@@ PARSE-STATUS is as documented in `parse
      ;; comment.
      (setq needs-close
            (or
-            ;; FIXME: Why not (get-char-property <pos> 'js2-error) instead?
-            (if (fboundp 'cursor-sensor-mode)
-                (equal (get-text-property (1- (point)) 'cursor-sensor-functions)
-                       '(js2-echo-error))
-              (eq (get-text-property (1- (point)) 'point-entered)
-                  'js2-echo-error))
+            (get-char-property (1- (point)) 'js2-error)
             ;; The heuristic above doesn't work well when we're
             ;; creating a comment and there's another one downstream,
             ;; as our parser thinks this one ends at the end of the
@@@ -12090,7 -12158,7 +12158,7 @@@ move backward across N balanced express
                     (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.
+              ;; No parens or child nodes, looks for the end of the current node.
               (cl-incf pos (js2-node-len
                             (if (js2-expr-stmt-node-p (js2-node-parent node))
                                 ;; Stop after the semicolon.
index affbd58a36a3c0362053521ae8a753a5b8ffd576,df692028508404028a6723e5c8e0f33ba83947b6..df692028508404028a6723e5c8e0f33ba83947b6
  (require 'js2-mode)
  (require 'cl-lib)
  
- (defun js2-test-indent (content)
+ (defun js2-test-indent (content keep-indent)
    (let ((s (replace-regexp-in-string "^ *|" "" content)))
      (with-temp-buffer
-       (insert (replace-regexp-in-string "^ *" "" s))
+       (insert
+        (if keep-indent
+            s
+          (replace-regexp-in-string "^ *" "" s)))
        (js2-mode)
        (indent-region (point-min) (point-max))
        (should (string= s (buffer-substring-no-properties
                            (point-min) (point)))))))
  
- (cl-defmacro js2-deftest-indent (name content &key bind)
+ (cl-defmacro js2-deftest-indent (name content &key bind keep-indent)
    `(ert-deftest ,(intern (format "js2-%s" name)) ()
       (let ,(append '((js2-basic-offset 2)
                       (js2-pretty-multiline-declarations t)
                       (inhibit-point-motion-hooks t))
                     bind)
-        (js2-test-indent ,content))))
+        (js2-test-indent ,content ,keep-indent))))
  
  (put 'js2-deftest-indent 'lisp-indent-function 'defun)
  
    |  default: 'donkey',
    |  tee: 'ornery'
    |};")
+ (js2-deftest-indent multiline-string-noop
+   "`multiline string
+   |       contents
+   |  are kept
+   |        unchanged!`"
+   :keep-indent t)
+ (js2-deftest-indent no-multiline-decl-first-arg-function-dynamic
+   "var foo = function() {
+   |  return 7;
+   |};"
+   :bind ((js2-pretty-multiline-declarations 'dynamic)))
+ (js2-deftest-indent multiline-decl-first-arg-function-indent-dynamic
+   "var foo = function() {
+   |      return 7;
+   |    },
+   |    bar = 8;"
+   :bind ((js2-pretty-multiline-declarations 'dynamic)))
+ (js2-deftest-indent multiline-decl-first-arg-function-indent-dynamic-comment
+   "var foo = function() {
+   |      return 7;
+   |    }/* MUAHAHAHA, ah ha! */,
+   |    bar = 8;"
+   :bind ((js2-pretty-multiline-declarations 'dynamic)))
+ (js2-deftest-indent multiline-decl-first-arg-function-indent-dynamic-scan-error
+   "var foo = function() {
+   |  return 7;
+   |  ,
+   |  bar = 8;"
+   :bind ((js2-pretty-multiline-declarations 'dynamic)))
index b4f2d8fe23e7a6166d855643dd184a854145db67,0dd1502c29147ca102aa71ffe2bfbc56011f8739..0dd1502c29147ca102aa71ffe2bfbc56011f8739
@@@ -608,6 -608,16 +608,16 @@@ the test.
      (should export-node)
      (should (js2-export-node-default export-node))))
  
+ (js2-deftest export-function-no-semicolon "export default function foo() {}"
+   (js2-mode)
+   (should (equal nil js2-parsed-warnings)))
+ (js2-deftest export-default-function-no-semicolon "export function foo() {}"
+   (js2-mode)
+   (should (equal nil js2-parsed-warnings)))
+ (js2-deftest export-anything-else-does-require-a-semicolon "export var obj = {}"
+   (js2-mode)
+   (should (not (equal nil js2-parsed-warnings))))
  (js2-deftest-parse parse-export-rexport "export * from 'other/lib';")
  (js2-deftest-parse parse-export-export-named-list "export {foo, bar as bang};")
  (js2-deftest-parse parse-re-export-named-list "export {foo, bar as bang} from 'other/lib';")
  (js2-deftest-parse parse-super-keyword
    "class Foo {\n  constructor() {  super(42);\n}\n  foo() {  super.foo();\n}\n}")
  
+ (js2-deftest-parse parse-class-keywordlike-method
+   "class C {\n  delete() {}\n  if() {}\n}")
  ;;; Scopes
  
  (js2-deftest ast-symbol-table-includes-fn-node "function foo() {}"
      (should (= (js2-symbol-decl-type var-entry) js2-VAR))
      (should (js2-name-node-p (js2-symbol-ast-node var-entry)))))
  
+ (js2-deftest for-node-is-declaration-scope "for (let i = 0; i; ++i) {};"
+   (js2-mode)
+   (search-forward "i")
+   (forward-char -1)
+   (let ((scope (js2-node-get-enclosing-scope (js2-node-at-point))))
+     (should (js2-for-node-p (js2-get-defining-scope scope "i")))))
+ (js2-deftest array-comp-is-result-scope "[x * 2 for (x in y)];"
+   (js2-mode)
+   (search-forward "x")
+   (forward-char -1)
+   (let ((scope (js2-node-get-enclosing-scope (js2-node-at-point))))
+     (should (js2-comp-loop-node-p (js2-get-defining-scope scope "x")))))
  ;;; Tokenizer
  
  (js2-deftest get-token "(1+1)"
  (js2-deftest function-without-parens-error "function b {}"
    ;; Should finish the parse.
    (js2-mode))
+ ;;; Comments
+ (js2-deftest comment-node-length "//"
+   (js2-mode)
+   (let ((node (js2-node-at-point (point-min))))
+     (should (= (js2-node-len node) 2))))
+ (js2-deftest comment-node-length-newline "//\n"
+   (js2-mode)
+   (let ((node (js2-node-at-point (point-min))))
+     (should (= (js2-node-len node) 3))))