branch: master commit 54df2598550c8040def7aec80d04847458d990df Merge: c10ba4b ac93b9e Author: Dmitry Gutov <dgu...@yandex.ru> Commit: Dmitry Gutov <dgu...@yandex.ru>
Merge commit 'ac93b9eef9b6ac44d187b9688d68a7a5f205b3fe' from js2-mode Conflicts: packages/js2-mode/js2-mode.el --- packages/js2-mode/js2-mode.el | 362 ++++++++++++++++++++++--------------- packages/js2-mode/tests/indent.el | 45 ++++- packages/js2-mode/tests/parser.el | 39 ++++ 3 files changed, 295 insertions(+), 151 deletions(-) diff --git a/packages/js2-mode/js2-mode.el b/packages/js2-mode/js2-mode.el index 5bc0770..4fc2823 100644 --- a/packages/js2-mode/js2-mode.el +++ b/packages/js2-mode/js2-mode.el @@ -263,14 +263,37 @@ js2-mode also binds `js2-bounce-indent-backwards' to Shift-Tab." 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 @@ which doesn't seem particularly useful, but Rhino permits it." (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 @@ First match-group is the leading whitespace.") (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 @@ the correct number of ARGS must be provided." "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 @@ If any given node in NODES is nil, doesn't record that link." (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 @@ 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 @@ into temp buffers." let new null return - static super switch + super switch this throw true try typeof var void while with @@ -5705,7 +5733,7 @@ into temp buffers." 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 @@ The values are default faces to use for highlighting the keywords.") ;; FIXME: Support strict mode-only future reserved words, after we know ;; which parts scopes are in strict mode, and which are not. -(defconst js2-reserved-words '(class enum export extends import 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 @@ its relevant fields and puts it into `js2-ti-tokens'." (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 @@ Shown at or above `js2-highlight-level' 3.") (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 @@ 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 @@ node are given relative start positions and correct lengths." 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 @@ 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) @@ -8541,6 +8577,7 @@ invalid export statements." "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'. @@ -8558,78 +8595,83 @@ Last matched token must be js2-FOR." (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 @@ Returns the parsed `js2-var-decl-node' expression 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 @@ If NODE is non-nil, it is the AST node associated with the symbol." (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) @@ -10149,6 +10190,9 @@ We should have just parsed the 'for' keyword before calling this function." :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 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed." (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) {...}} @@ -10344,13 +10393,13 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed." (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 @@ In particular, return the buffer position of the first `for' kwd." (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 @@ -10797,9 +10880,7 @@ In particular, return the buffer position of the first `for' kwd." (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 @@ If so, we don't ever want to use bounce-indent." 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 @@ The last element is optional. When present, use instead of FACE." (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 @@ was found on `point-entered' or in `cursor-sensor-functions'." (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 @@ PARSE-STATUS is as documented in `parse-partial-sexp'." ;; 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 @@ move backward across N balanced expressions." (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. diff --git a/packages/js2-mode/tests/indent.el b/packages/js2-mode/tests/indent.el index affbd58..df69202 100644 --- a/packages/js2-mode/tests/indent.el +++ b/packages/js2-mode/tests/indent.el @@ -23,22 +23,25 @@ (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) @@ -102,3 +105,37 @@ | 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))) diff --git a/packages/js2-mode/tests/parser.el b/packages/js2-mode/tests/parser.el index b4f2d8f..0dd1502 100644 --- a/packages/js2-mode/tests/parser.el +++ b/packages/js2-mode/tests/parser.el @@ -608,6 +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';") @@ -673,6 +683,9 @@ the test." (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() {}" @@ -696,6 +709,20 @@ the test." (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)" @@ -766,3 +793,15 @@ the test." (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))))