diff --git a/crates/tsv_debug/src/cli/commands/gap_audit_known.txt b/crates/tsv_debug/src/cli/commands/gap_audit_known.txt index f4d9e935..59b8f060 100644 --- a/crates/tsv_debug/src/cli/commands/gap_audit_known.txt +++ b/crates/tsv_debug/src/cli/commands/gap_audit_known.txt @@ -12,7 +12,7 @@ # the gate rather than being pinned. # # Format: KINDSHAPEPAYLOADS -# shapes: 642 +# shapes: 639 DROPPED !)⟨⟩!. annotation,block,jsdoc_cast DROPPED #⟨⟩IDENT annotation,block,jsdoc_cast,line,multiline DROPPED #⟨⟩\ annotation,block,jsdoc_cast,line,multiline @@ -168,7 +168,7 @@ DROPPED ,{}⟨⟩); annotation,block,jsdoc_cast,multiline DROPPED ,⟨⟩, annotation,block,jsdoc_cast DROPPED ,⟨⟩,, annotation,block,jsdoc_cast DROPPED ,⟨⟩/* annotation,block,jsdoc_cast,line,multiline -DROPPED ,⟨⟩// annotation,block,jsdoc_cast,multiline +DROPPED ,⟨⟩// annotation,block,jsdoc_cast DROPPED ,⟨⟩>( annotation,block,jsdoc_cast,multiline DROPPED ,⟨⟩>() annotation,block,jsdoc_cast,multiline DROPPED ,⟨⟩>(/ annotation,block,jsdoc_cast,multiline @@ -292,7 +292,6 @@ DROPPED IDENT⟨⟩--) annotation,block,jsdoc_cast DROPPED IDENT⟨⟩--; annotation,block,jsdoc_cast DROPPED IDENT⟨⟩/ annotation,block,jsdoc_cast,line,multiline DROPPED IDENT⟨⟩/* line,multiline -DROPPED IDENT⟨⟩/*, multiline DROPPED IDENT⟨⟩: annotation,block,jsdoc_cast,line,multiline DROPPED IDENT⟨⟩:[. annotation,block,jsdoc_cast,line,multiline DROPPED IDENT⟨⟩= annotation,block,jsdoc_cast,line,multiline @@ -459,7 +458,6 @@ DROPPED })⟨⟩) annotation,block,jsdoc_cast,line,multiline DROPPED })⟨⟩); annotation,block,jsdoc_cast,multiline DROPPED })⟨⟩␣ annotation,block,jsdoc_cast,multiline DROPPED }*#⟨⟩IDENT annotation,block,jsdoc_cast,line,multiline -DROPPED },⟨⟩// multiline DROPPED },⟨⟩>( annotation,block,jsdoc_cast,multiline DROPPED },⟨⟩>() annotation,block,jsdoc_cast,multiline DROPPED },⟨⟩␣ annotation,block,jsdoc_cast,multiline @@ -526,7 +524,6 @@ DROPPED ␣⟨⟩... multiline DROPPED ␣⟨⟩/ annotation,block,jsdoc_cast,line,multiline DROPPED ␣⟨⟩/* annotation,block,jsdoc_cast,line,multiline DROPPED ␣⟨⟩/** multiline -DROPPED ␣⟨⟩/*, multiline DROPPED ␣⟨⟩// annotation,block,jsdoc_cast,line,multiline DROPPED ␣⟨⟩: annotation,block,jsdoc_cast,line,multiline DROPPED ␣⟨⟩; annotation,block,jsdoc_cast,line,multiline diff --git a/crates/tsv_ts/src/printer/expressions/patterns.rs b/crates/tsv_ts/src/printer/expressions/patterns.rs index d52d4f12..3f1c4039 100644 --- a/crates/tsv_ts/src/printer/expressions/patterns.rs +++ b/crates/tsv_ts/src/printer/expressions/patterns.rs @@ -417,14 +417,25 @@ impl<'a> Printer<'a> { let mut parts = DocBuf::new(); let mut last_pos = prev_end; for comment in comments_to_emit_in_range(self.comments, prev_end, boundary) { - if self.is_same_line(prev_end, comment.span.start) { - continue; - } - if self.has_blank_line_between(last_pos, comment.span.start) { - parts.push(d.literalline()); + if self.is_same_line(last_pos, comment.span.start) { + // Same source line as the preceding content — e.g. a line comment + // trailing a *multiline* block's closing line (`*/ // c`). The + // per-element loop keys its same-line capture on the element's end, + // so a comment sitting on the block's *closing* line (a different + // line than the element) was never captured there; keep it inline + // here rather than dropping it or forcing it onto its own line. + // Keying the test on the running `last_pos` (not the original + // `prev_end`) also keeps two same-line dangling comments together + // (`/* a */ // b`). + parts.push(d.text(" ")); + parts.push(self.build_comment_doc(comment)); + } else { + if self.has_blank_line_between(last_pos, comment.span.start) { + parts.push(d.literalline()); + } + parts.push(d.hardline()); + parts.push(self.build_comment_doc(comment)); } - parts.push(d.hardline()); - parts.push(self.build_comment_doc(comment)); last_pos = comment.span.end; } d.concat(&parts) diff --git a/crates/tsv_ts/src/printer/statements/control_flow/loops/for_loop.rs b/crates/tsv_ts/src/printer/statements/control_flow/loops/for_loop.rs index 276ae299..abff9191 100644 --- a/crates/tsv_ts/src/printer/statements/control_flow/loops/for_loop.rs +++ b/crates/tsv_ts/src/printer/statements/control_flow/loops/for_loop.rs @@ -897,6 +897,7 @@ impl<'a> Printer<'a> { ) -> DocId { let d = self.d(); let left_start = self.get_for_in_of_left_start(left); + let binding_start = self.get_for_in_of_binding_start(left); let left_end = self.get_for_in_of_left_end(left); let right_start = right.span().start; let right_end = right.span().end; @@ -913,6 +914,7 @@ impl<'a> Printer<'a> { let keyword_pos = self .find_keyword_position(left_end, right_start, keyword) .unwrap_or(left_end); + let keyword_end = keyword_pos + keyword.len() as u32; // Preserve comments between keywords and `(` // for await: two gaps — for-to-await and await-to-paren @@ -940,14 +942,23 @@ impl<'a> Printer<'a> { self.build_keyword_paren_comments(for_keyword_end, open_paren) }; - // Check for line comments in the header - if present, use breaking layout - // We check from open paren to close paren + // Check for line comments in the header's *structural gaps* — if present, + // use the breaking layout that preserves them where the author placed them. + // Only gap comments count: a `//` *inside* the binding or the iterable + // expression is that expression's own content (printed by its doc) and must + // NOT force the header open — prettier keeps the head inline and only the + // iterable breaks. See `in_of_iterable_line_comment`. let close = close_paren.unwrap_or(right_end + 1); - let has_line_comments = if let Some(open) = open_paren { - self.has_line_comments_between(open + 1, close) - } else { - self.has_line_comments_between(left_start, close) - }; + let has_line_comments = self.for_in_of_header_has_structural_line_comments( + open_paren, + binding_start, + left_end, + keyword_pos, + keyword_end, + right_start, + right_end, + close, + ); // Build the `for ... (` opening once — shared by both the inline and the // breaking (line-comment) layouts, so each preserves any `for`-to-`(` @@ -1000,7 +1011,6 @@ impl<'a> Printer<'a> { } // Comments after the keyword, before right - let keyword_end = keyword_pos + keyword.len() as u32; let has_comment = self.append_for_in_of_block_comments(&mut parts, keyword_end, right_start); if !has_comment { @@ -1022,6 +1032,47 @@ impl<'a> Printer<'a> { d.group(d.concat(&parts)) } + /// Whether a `//` line comment sits in one of the for-in/for-of header's + /// *structural gaps* — after `(` up to the binding target (covering the + /// declaration keyword→binding gap, `const // c⏎x`), between the binding and + /// the `in`/`of` keyword, between the keyword and the iterable, or between the + /// iterable and `)`. + /// + /// Deliberately excludes the interiors of the binding *pattern* + /// (`[binding_start, left_end)`) and the iterable (`[right_start, right_end)`): a + /// line comment *inside* the iterable (`for (const x of [\n\t'a' // c\n])`) — or + /// inside a destructuring binding — is that expression's own content, printed by + /// its doc, and must not force the header-breaking layout + /// (`build_for_in_of_with_line_comments`). Prettier keeps the head inline and + /// only that expression breaks. A gap comment, by contrast, has no other line + /// break to ride, so inline the `//` would swallow the following header tokens — + /// tsv breaks the header and preserves it in place. See the + /// `in_of_iterable_line_comment` (excluded) and + /// `of_in_keyword_binding_line_comment_prettier_divergence` (keyword→binding gap, + /// included) fixtures. + #[allow(clippy::too_many_arguments)] + fn for_in_of_header_has_structural_line_comments( + &self, + open_paren: Option, + binding_start: u32, + left_end: u32, + keyword_pos: u32, + keyword_end: u32, + right_start: u32, + right_end: u32, + close: u32, + ) -> bool { + // `(` → binding target (spans the declaration keyword + its keyword→binding + // gap). Absent open paren (degenerate header) has no locatable gap. + open_paren.is_some_and(|open| self.has_line_comments_between(open + 1, binding_start)) + // binding → keyword + || self.has_line_comments_between(left_end, keyword_pos) + // keyword → iterable + || self.has_line_comments_between(keyword_end, right_start) + // iterable → `)` + || self.has_line_comments_between(right_end, close) + } + /// Build for-in/for-of statement with line comments preserved in their positions /// /// This is our divergence from Prettier - we preserve line comments where @@ -1193,6 +1244,23 @@ impl<'a> Printer<'a> { } } + /// Get the start position of the for-in/for-of binding *target* — the first + /// declarator's pattern for a `VariableDeclaration` left (`const [a, b]` → the + /// `[`), or the pattern itself for a bare `Pattern` left. Unlike + /// `get_for_in_of_left_start` (which points at the `const`/`let` keyword), this + /// skips the declaration kind so the keyword→binding gap (`const // c⏎x`) reads + /// as a header-structural position while the binding pattern's own interior does + /// not — see `for_in_of_header_has_structural_line_comments`. + fn get_for_in_of_binding_start(&self, left: &internal::ForInOfLeft<'_>) -> u32 { + match left { + internal::ForInOfLeft::VariableDeclaration(decl) => decl + .declarations + .first() + .map_or(decl.span.start, |declarator| declarator.id.span().start), + internal::ForInOfLeft::Pattern(expr) => expr.span().start, + } + } + /// Append inline block comments for for-in/for-of statements. /// Emits ` comment` for each block comment, plus trailing ` ` if any were added. /// Own-line comments normalize to inline. Line comments are skipped (handled by diff --git a/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/expected.json b/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/expected.json new file mode 100644 index 00000000..daafea20 --- /dev/null +++ b/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/expected.json @@ -0,0 +1,493 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 394, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [] + }, + "options": null, + "comments": [ + { + "type": "Line", + "value": " A multiline block comment followed by a line comment, trailing the last", + "start": 10, + "end": 84, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 75 + } + } + }, + { + "type": "Line", + "value": " element of an array pattern: both are preserved, the line comment following", + "start": 86, + "end": 164, + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 79 + } + } + }, + { + "type": "Line", + "value": " the block's close on the same line.", + "start": 166, + "end": 204, + "loc": { + "start": { + "line": 4, + "column": 1 + }, + "end": { + "line": 4, + "column": 39 + } + } + }, + { + "type": "Block", + "value": " line1\nline2 ", + "start": 223, + "end": 240, + "loc": { + "start": { + "line": 7, + "column": 4 + }, + "end": { + "line": 8, + "column": 8 + } + } + }, + { + "type": "Line", + "value": " trailing", + "start": 241, + "end": 252, + "loc": { + "start": { + "line": 8, + "column": 9 + }, + "end": { + "line": 8, + "column": 20 + } + } + }, + { + "type": "Line", + "value": " The same shape on the last property of an object pattern.", + "start": 265, + "end": 325, + "loc": { + "start": { + "line": 11, + "column": 1 + }, + "end": { + "line": 11, + "column": 61 + } + } + }, + { + "type": "Block", + "value": " line1\nline2 ", + "start": 344, + "end": 361, + "loc": { + "start": { + "line": 14, + "column": 4 + }, + "end": { + "line": 15, + "column": 8 + } + } + }, + { + "type": "Line", + "value": " trailing", + "start": 362, + "end": 373, + "loc": { + "start": { + "line": 15, + "column": 9 + }, + "end": { + "line": 15, + "column": 20 + } + } + } + ], + "instance": { + "type": "Script", + "start": 0, + "end": 393, + "context": "default", + "content": { + "type": "Program", + "start": 8, + "end": 384, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 17, + "column": 9 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 206, + "end": 262, + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 9, + "column": 9 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 212, + "end": 261, + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 9, + "column": 8 + } + }, + "id": { + "type": "ArrayPattern", + "start": 212, + "end": 255, + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 9, + "column": 2 + } + }, + "elements": [ + { + "type": "Identifier", + "start": 216, + "end": 217, + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "name": "a" + }, + { + "type": "Identifier", + "start": 221, + "end": 222, + "loc": { + "start": { + "line": 7, + "column": 2 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "name": "b", + "trailingComments": [ + { + "type": "Block", + "value": " line1\nline2 ", + "start": 223, + "end": 240 + } + ] + } + ] + }, + "init": { + "type": "Identifier", + "start": 258, + "end": 261, + "loc": { + "start": { + "line": 9, + "column": 5 + }, + "end": { + "line": 9, + "column": 8 + } + }, + "name": "arr", + "leadingComments": [ + { + "type": "Line", + "value": " trailing", + "start": 241, + "end": 252 + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " A multiline block comment followed by a line comment, trailing the last", + "start": 10, + "end": 84 + }, + { + "type": "Line", + "value": " element of an array pattern: both are preserved, the line comment following", + "start": 86, + "end": 164 + }, + { + "type": "Line", + "value": " the block's close on the same line.", + "start": 166, + "end": 204 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 327, + "end": 383, + "loc": { + "start": { + "line": 12, + "column": 1 + }, + "end": { + "line": 16, + "column": 9 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 333, + "end": 382, + "loc": { + "start": { + "line": 12, + "column": 7 + }, + "end": { + "line": 16, + "column": 8 + } + }, + "id": { + "type": "ObjectPattern", + "start": 333, + "end": 376, + "loc": { + "start": { + "line": 12, + "column": 7 + }, + "end": { + "line": 16, + "column": 2 + } + }, + "properties": [ + { + "type": "Property", + "start": 337, + "end": 338, + "loc": { + "start": { + "line": 13, + "column": 2 + }, + "end": { + "line": 13, + "column": 3 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 337, + "end": 338, + "loc": { + "start": { + "line": 13, + "column": 2 + }, + "end": { + "line": 13, + "column": 3 + } + }, + "name": "x" + }, + "value": { + "type": "Identifier", + "start": 337, + "end": 338, + "loc": { + "start": { + "line": 13, + "column": 2 + }, + "end": { + "line": 13, + "column": 3 + } + }, + "name": "x" + }, + "kind": "init" + }, + { + "type": "Property", + "start": 342, + "end": 343, + "loc": { + "start": { + "line": 14, + "column": 2 + }, + "end": { + "line": 14, + "column": 3 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 342, + "end": 343, + "loc": { + "start": { + "line": 14, + "column": 2 + }, + "end": { + "line": 14, + "column": 3 + } + }, + "name": "y" + }, + "value": { + "type": "Identifier", + "start": 342, + "end": 343, + "loc": { + "start": { + "line": 14, + "column": 2 + }, + "end": { + "line": 14, + "column": 3 + } + }, + "name": "y" + }, + "kind": "init", + "trailingComments": [ + { + "type": "Block", + "value": " line1\nline2 ", + "start": 344, + "end": 361 + } + ] + } + ] + }, + "init": { + "type": "Identifier", + "start": 379, + "end": 382, + "loc": { + "start": { + "line": 16, + "column": 5 + }, + "end": { + "line": 16, + "column": 8 + } + }, + "name": "obj", + "leadingComments": [ + { + "type": "Line", + "value": " trailing", + "start": 362, + "end": 373 + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " The same shape on the last property of an object pattern.", + "start": 265, + "end": 325 + } + ] + } + ], + "sourceType": "module" + }, + "attributes": [] + } +} diff --git a/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/input.svelte b/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/input.svelte new file mode 100644 index 00000000..2d434f39 --- /dev/null +++ b/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/input.svelte @@ -0,0 +1,17 @@ + diff --git a/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/unformatted_compact.svelte b/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/unformatted_compact.svelte new file mode 100644 index 00000000..db8eb38f --- /dev/null +++ b/tests/fixtures/typescript/expressions/destructuring/last_element_multiline_block_line_comment/unformatted_compact.svelte @@ -0,0 +1,13 @@ + diff --git a/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/expected.json b/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/expected.json new file mode 100644 index 00000000..37692a21 --- /dev/null +++ b/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/expected.json @@ -0,0 +1,1102 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 669, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [] + }, + "options": null, + "comments": [ + { + "type": "Line", + "value": " A line comment inside the iterable array keeps the for-of head inline", + "start": 10, + "end": 82, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 73 + } + } + }, + { + "type": "Line", + "value": " (`for (const item of [`); only the array breaks.", + "start": 84, + "end": 135, + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 52 + } + } + }, + { + "type": "Line", + "value": " trailing", + "start": 171, + "end": 182, + "loc": { + "start": { + "line": 6, + "column": 6 + }, + "end": { + "line": 6, + "column": 17 + } + } + }, + { + "type": "Line", + "value": " A leading line comment inside the array behaves the same.", + "start": 206, + "end": 266, + "loc": { + "start": { + "line": 11, + "column": 1 + }, + "end": { + "line": 11, + "column": 61 + } + } + }, + { + "type": "Line", + "value": " leading", + "start": 291, + "end": 301, + "loc": { + "start": { + "line": 13, + "column": 2 + }, + "end": { + "line": 13, + "column": 12 + } + } + }, + { + "type": "Line", + "value": " A line comment inside a destructuring binding stays with the pattern:", + "start": 338, + "end": 410, + "loc": { + "start": { + "line": 20, + "column": 1 + }, + "end": { + "line": 20, + "column": 73 + } + } + }, + { + "type": "Line", + "value": " the head stays inline and only the pattern breaks.", + "start": 412, + "end": 465, + "loc": { + "start": { + "line": 21, + "column": 1 + }, + "end": { + "line": 21, + "column": 54 + } + } + }, + { + "type": "Line", + "value": " leading", + "start": 482, + "end": 492, + "loc": { + "start": { + "line": 23, + "column": 2 + }, + "end": { + "line": 23, + "column": 12 + } + } + }, + { + "type": "Line", + "value": " for-in over an object iterable — same rule, shared code path.", + "start": 534, + "end": 598, + "loc": { + "start": { + "line": 30, + "column": 1 + }, + "end": { + "line": 30, + "column": 65 + } + } + }, + { + "type": "Line", + "value": " trailing", + "start": 627, + "end": 638, + "loc": { + "start": { + "line": 32, + "column": 7 + }, + "end": { + "line": 32, + "column": 18 + } + } + } + ], + "instance": { + "type": "Script", + "start": 0, + "end": 668, + "context": "default", + "content": { + "type": "Program", + "start": 8, + "end": 659, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 36, + "column": 9 + } + }, + "body": [ + { + "type": "ForOfStatement", + "start": 137, + "end": 203, + "loc": { + "start": { + "line": 4, + "column": 1 + }, + "end": { + "line": 9, + "column": 2 + } + }, + "await": false, + "left": { + "type": "VariableDeclaration", + "start": 142, + "end": 152, + "loc": { + "start": { + "line": 4, + "column": 6 + }, + "end": { + "line": 4, + "column": 16 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 148, + "end": 152, + "loc": { + "start": { + "line": 4, + "column": 12 + }, + "end": { + "line": 4, + "column": 16 + } + }, + "id": { + "type": "Identifier", + "start": 148, + "end": 152, + "loc": { + "start": { + "line": 4, + "column": 12 + }, + "end": { + "line": 4, + "column": 16 + } + }, + "name": "item" + }, + "init": null + } + ], + "kind": "const" + }, + "right": { + "type": "ArrayExpression", + "start": 156, + "end": 185, + "loc": { + "start": { + "line": 4, + "column": 20 + }, + "end": { + "line": 7, + "column": 2 + } + }, + "elements": [ + { + "type": "Literal", + "start": 160, + "end": 163, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 5 + } + }, + "value": "a", + "raw": "'a'" + }, + { + "type": "Literal", + "start": 167, + "end": 170, + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 5 + } + }, + "value": "b", + "raw": "'b'", + "trailingComments": [ + { + "type": "Line", + "value": " trailing", + "start": 171, + "end": 182 + } + ] + } + ] + }, + "body": { + "type": "BlockStatement", + "start": 187, + "end": 203, + "loc": { + "start": { + "line": 7, + "column": 4 + }, + "end": { + "line": 9, + "column": 2 + } + }, + "body": [ + { + "type": "ExpressionStatement", + "start": 191, + "end": 200, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 11 + } + }, + "expression": { + "type": "CallExpression", + "start": 191, + "end": 199, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 10 + } + }, + "callee": { + "type": "Identifier", + "start": 191, + "end": 193, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 4 + } + }, + "name": "fn" + }, + "arguments": [ + { + "type": "Identifier", + "start": 194, + "end": 198, + "loc": { + "start": { + "line": 8, + "column": 5 + }, + "end": { + "line": 8, + "column": 9 + } + }, + "name": "item" + } + ], + "optional": false + } + } + ] + }, + "leadingComments": [ + { + "type": "Line", + "value": " A line comment inside the iterable array keeps the for-of head inline", + "start": 10, + "end": 82 + }, + { + "type": "Line", + "value": " (`for (const item of [`); only the array breaks.", + "start": 84, + "end": 135 + } + ] + }, + { + "type": "ForOfStatement", + "start": 268, + "end": 335, + "loc": { + "start": { + "line": 12, + "column": 1 + }, + "end": { + "line": 18, + "column": 2 + } + }, + "await": false, + "left": { + "type": "VariableDeclaration", + "start": 273, + "end": 283, + "loc": { + "start": { + "line": 12, + "column": 6 + }, + "end": { + "line": 12, + "column": 16 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 279, + "end": 283, + "loc": { + "start": { + "line": 12, + "column": 12 + }, + "end": { + "line": 12, + "column": 16 + } + }, + "id": { + "type": "Identifier", + "start": 279, + "end": 283, + "loc": { + "start": { + "line": 12, + "column": 12 + }, + "end": { + "line": 12, + "column": 16 + } + }, + "name": "item" + }, + "init": null + } + ], + "kind": "const" + }, + "right": { + "type": "ArrayExpression", + "start": 287, + "end": 317, + "loc": { + "start": { + "line": 12, + "column": 20 + }, + "end": { + "line": 16, + "column": 2 + } + }, + "elements": [ + { + "type": "Literal", + "start": 304, + "end": 307, + "loc": { + "start": { + "line": 14, + "column": 2 + }, + "end": { + "line": 14, + "column": 5 + } + }, + "value": "a", + "raw": "'a'", + "leadingComments": [ + { + "type": "Line", + "value": " leading", + "start": 291, + "end": 301 + } + ] + }, + { + "type": "Literal", + "start": 311, + "end": 314, + "loc": { + "start": { + "line": 15, + "column": 2 + }, + "end": { + "line": 15, + "column": 5 + } + }, + "value": "b", + "raw": "'b'" + } + ] + }, + "body": { + "type": "BlockStatement", + "start": 319, + "end": 335, + "loc": { + "start": { + "line": 16, + "column": 4 + }, + "end": { + "line": 18, + "column": 2 + } + }, + "body": [ + { + "type": "ExpressionStatement", + "start": 323, + "end": 332, + "loc": { + "start": { + "line": 17, + "column": 2 + }, + "end": { + "line": 17, + "column": 11 + } + }, + "expression": { + "type": "CallExpression", + "start": 323, + "end": 331, + "loc": { + "start": { + "line": 17, + "column": 2 + }, + "end": { + "line": 17, + "column": 10 + } + }, + "callee": { + "type": "Identifier", + "start": 323, + "end": 325, + "loc": { + "start": { + "line": 17, + "column": 2 + }, + "end": { + "line": 17, + "column": 4 + } + }, + "name": "fn" + }, + "arguments": [ + { + "type": "Identifier", + "start": 326, + "end": 330, + "loc": { + "start": { + "line": 17, + "column": 5 + }, + "end": { + "line": 17, + "column": 9 + } + }, + "name": "item" + } + ], + "optional": false + } + } + ] + }, + "leadingComments": [ + { + "type": "Line", + "value": " A leading line comment inside the array behaves the same.", + "start": 206, + "end": 266 + } + ] + }, + { + "type": "ForOfStatement", + "start": 467, + "end": 531, + "loc": { + "start": { + "line": 22, + "column": 1 + }, + "end": { + "line": 28, + "column": 2 + } + }, + "await": false, + "left": { + "type": "VariableDeclaration", + "start": 472, + "end": 504, + "loc": { + "start": { + "line": 22, + "column": 6 + }, + "end": { + "line": 26, + "column": 2 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 478, + "end": 504, + "loc": { + "start": { + "line": 22, + "column": 12 + }, + "end": { + "line": 26, + "column": 2 + } + }, + "id": { + "type": "ArrayPattern", + "start": 478, + "end": 504, + "loc": { + "start": { + "line": 22, + "column": 12 + }, + "end": { + "line": 26, + "column": 2 + } + }, + "elements": [ + { + "type": "Identifier", + "start": 495, + "end": 496, + "loc": { + "start": { + "line": 24, + "column": 2 + }, + "end": { + "line": 24, + "column": 3 + } + }, + "name": "a", + "leadingComments": [ + { + "type": "Line", + "value": " leading", + "start": 482, + "end": 492 + } + ] + }, + { + "type": "Identifier", + "start": 500, + "end": 501, + "loc": { + "start": { + "line": 25, + "column": 2 + }, + "end": { + "line": 25, + "column": 3 + } + }, + "name": "b" + } + ] + }, + "init": null + } + ], + "kind": "const" + }, + "right": { + "type": "Identifier", + "start": 508, + "end": 513, + "loc": { + "start": { + "line": 26, + "column": 6 + }, + "end": { + "line": 26, + "column": 11 + } + }, + "name": "pairs" + }, + "body": { + "type": "BlockStatement", + "start": 515, + "end": 531, + "loc": { + "start": { + "line": 26, + "column": 13 + }, + "end": { + "line": 28, + "column": 2 + } + }, + "body": [ + { + "type": "ExpressionStatement", + "start": 519, + "end": 528, + "loc": { + "start": { + "line": 27, + "column": 2 + }, + "end": { + "line": 27, + "column": 11 + } + }, + "expression": { + "type": "CallExpression", + "start": 519, + "end": 527, + "loc": { + "start": { + "line": 27, + "column": 2 + }, + "end": { + "line": 27, + "column": 10 + } + }, + "callee": { + "type": "Identifier", + "start": 519, + "end": 521, + "loc": { + "start": { + "line": 27, + "column": 2 + }, + "end": { + "line": 27, + "column": 4 + } + }, + "name": "fn" + }, + "arguments": [ + { + "type": "Identifier", + "start": 522, + "end": 523, + "loc": { + "start": { + "line": 27, + "column": 5 + }, + "end": { + "line": 27, + "column": 6 + } + }, + "name": "a" + }, + { + "type": "Identifier", + "start": 525, + "end": 526, + "loc": { + "start": { + "line": 27, + "column": 8 + }, + "end": { + "line": 27, + "column": 9 + } + }, + "name": "b" + } + ], + "optional": false + } + } + ] + }, + "leadingComments": [ + { + "type": "Line", + "value": " A line comment inside a destructuring binding stays with the pattern:", + "start": 338, + "end": 410 + }, + { + "type": "Line", + "value": " the head stays inline and only the pattern breaks.", + "start": 412, + "end": 465 + } + ] + }, + { + "type": "ForInStatement", + "start": 600, + "end": 658, + "loc": { + "start": { + "line": 31, + "column": 1 + }, + "end": { + "line": 35, + "column": 2 + } + }, + "left": { + "type": "VariableDeclaration", + "start": 605, + "end": 614, + "loc": { + "start": { + "line": 31, + "column": 6 + }, + "end": { + "line": 31, + "column": 15 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 611, + "end": 614, + "loc": { + "start": { + "line": 31, + "column": 12 + }, + "end": { + "line": 31, + "column": 15 + } + }, + "id": { + "type": "Identifier", + "start": 611, + "end": 614, + "loc": { + "start": { + "line": 31, + "column": 12 + }, + "end": { + "line": 31, + "column": 15 + } + }, + "name": "key" + }, + "init": null + } + ], + "kind": "const" + }, + "right": { + "type": "ObjectExpression", + "start": 618, + "end": 641, + "loc": { + "start": { + "line": 31, + "column": 19 + }, + "end": { + "line": 33, + "column": 2 + } + }, + "properties": [ + { + "type": "Property", + "start": 622, + "end": 626, + "loc": { + "start": { + "line": 32, + "column": 2 + }, + "end": { + "line": 32, + "column": 6 + } + }, + "method": false, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 622, + "end": 623, + "loc": { + "start": { + "line": 32, + "column": 2 + }, + "end": { + "line": 32, + "column": 3 + } + }, + "name": "a" + }, + "value": { + "type": "Literal", + "start": 625, + "end": 626, + "loc": { + "start": { + "line": 32, + "column": 5 + }, + "end": { + "line": 32, + "column": 6 + } + }, + "value": 1, + "raw": "1" + }, + "kind": "init", + "trailingComments": [ + { + "type": "Line", + "value": " trailing", + "start": 627, + "end": 638 + } + ] + } + ] + }, + "body": { + "type": "BlockStatement", + "start": 643, + "end": 658, + "loc": { + "start": { + "line": 33, + "column": 4 + }, + "end": { + "line": 35, + "column": 2 + } + }, + "body": [ + { + "type": "ExpressionStatement", + "start": 647, + "end": 655, + "loc": { + "start": { + "line": 34, + "column": 2 + }, + "end": { + "line": 34, + "column": 10 + } + }, + "expression": { + "type": "CallExpression", + "start": 647, + "end": 654, + "loc": { + "start": { + "line": 34, + "column": 2 + }, + "end": { + "line": 34, + "column": 9 + } + }, + "callee": { + "type": "Identifier", + "start": 647, + "end": 649, + "loc": { + "start": { + "line": 34, + "column": 2 + }, + "end": { + "line": 34, + "column": 4 + } + }, + "name": "fn" + }, + "arguments": [ + { + "type": "Identifier", + "start": 650, + "end": 653, + "loc": { + "start": { + "line": 34, + "column": 5 + }, + "end": { + "line": 34, + "column": 8 + } + }, + "name": "key" + } + ], + "optional": false + } + } + ] + }, + "leadingComments": [ + { + "type": "Line", + "value": " for-in over an object iterable — same rule, shared code path.", + "start": 534, + "end": 598 + } + ] + } + ], + "sourceType": "module" + }, + "attributes": [] + } +} diff --git a/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/input.svelte b/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/input.svelte new file mode 100644 index 00000000..2cb382ec --- /dev/null +++ b/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/input.svelte @@ -0,0 +1,36 @@ + diff --git a/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/unformatted_compact.svelte b/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/unformatted_compact.svelte new file mode 100644 index 00000000..b934b7f9 --- /dev/null +++ b/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/unformatted_compact.svelte @@ -0,0 +1,21 @@ + diff --git a/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/unformatted_spaces.svelte b/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/unformatted_spaces.svelte new file mode 100644 index 00000000..7376b51c --- /dev/null +++ b/tests/fixtures/typescript/statements/for/in_of_iterable_line_comment/unformatted_spaces.svelte @@ -0,0 +1,39 @@ +