Skip to content

Commit afcea0b

Browse files
authored
Merge pull request #4 from datalakehouse/sql-formatter-newline-comma-option
fix(comments): Added a major fix to how comments are handled to make it consistent across different situations
2 parents 776d993 + 8b7458d commit afcea0b

4 files changed

Lines changed: 109 additions & 6 deletions

File tree

docs/commaPosition.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ WHERE
3939
```
4040

4141
### leadingWithSpace
42+
4243
```sql
4344
SELECT
4445
name
@@ -53,6 +54,7 @@ WHERE
5354
```
5455

5556
### Other examples
57+
5658
```sql
5759
-- No effect on INSERT Statements
5860
INSERT INTO

src/formatter/ExpressionFormatter.ts

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export default class ExpressionFormatter {
6868
private dialectCfg: ProcessedDialectFormatOptions;
6969
private params: Params;
7070
private layout: Layout;
71+
private pendingLineComment: (LineCommentNode | BlockCommentNode | DisableCommentNode)[] = []; // Keeps track if any pending Line/Comment is remaining
7172

7273
private inline = false;
7374
private nodes: AstNode[] = [];
@@ -184,9 +185,38 @@ export default class ExpressionFormatter {
184185
}
185186

186187
private formatPropertyAccess(node: PropertyAccessNode) {
187-
this.formatNode(node.object);
188+
this.formatComments(node.object.leadingComments);
189+
this.formatNodeWithoutComments(node.object);
190+
191+
// Handle trailing comments of object BEFORE the dot (inline)
192+
if (node.object.trailingComments && node.object.trailingComments.length > 0) {
193+
for (const comment of node.object.trailingComments) {
194+
if (comment.type === NodeType.block_comment) {
195+
this.layout.add(WS.NO_SPACE, WS.SPACE, (comment as BlockCommentNode).text);
196+
} else if (comment.type === NodeType.line_comment) {
197+
this.layout.add(WS.NO_SPACE, WS.SPACE, (comment as LineCommentNode).text);
198+
}
199+
}
200+
}
201+
202+
// Add the dot operator
188203
this.layout.add(WS.NO_SPACE, node.operator);
189-
this.formatNode(node.property);
204+
205+
// Handle leading comments of property AFTER the dot (inline)
206+
if (node.property.leadingComments && node.property.leadingComments.length > 0) {
207+
for (const comment of node.property.leadingComments) {
208+
if (comment.type === NodeType.block_comment) {
209+
this.layout.add((comment as BlockCommentNode).text, WS.SPACE);
210+
} else if (comment.type === NodeType.line_comment) {
211+
this.layout.add((comment as LineCommentNode).text, WS.SPACE);
212+
}
213+
}
214+
// Format property without leading comments (already handled above)
215+
this.formatNodeWithoutComments(node.property);
216+
this.formatComments(node.property.trailingComments);
217+
} else {
218+
this.formatNode(node.property);
219+
}
190220
}
191221

192222
private formatParenthesis(node: ParenthesisNode) {
@@ -345,13 +375,36 @@ export default class ExpressionFormatter {
345375
// Look ahead: check if next node is a line comment
346376
this.formatLeadingComma();
347377
} else {
348-
this.layout.add(WS.NO_SPACE, ',', WS.NEWLINE, WS.INDENT);
378+
this.formatTrailingComma();
349379
}
350380
} else {
351381
this.layout.add(WS.NO_SPACE, ',', WS.SPACE);
352382
}
353383
}
354-
384+
private formatTrailingComma() {
385+
if (this.pendingLineComment && this.pendingLineComment.length > 0) {
386+
// We have a line comment that should come after the comma
387+
while (this.pendingLineComment.length > 0) {
388+
const comment = this.pendingLineComment.shift();
389+
if (comment) {
390+
if (comment.type === NodeType.line_comment) {
391+
this.layout.add(
392+
WS.NO_SPACE,
393+
',',
394+
WS.SPACE,
395+
(comment as LineCommentNode).text,
396+
WS.MANDATORY_NEWLINE,
397+
WS.INDENT
398+
);
399+
} else {
400+
this.layout.add((comment as BlockCommentNode).text, WS.MANDATORY_NEWLINE, WS.INDENT);
401+
}
402+
}
403+
}
404+
} else {
405+
this.layout.add(WS.NO_SPACE, ',', WS.NEWLINE, WS.INDENT);
406+
}
407+
}
355408
private formatLeadingComma() {
356409
const comments: AstNode[] = [];
357410
let lookAheadIndex = this.index + 1;
@@ -433,6 +486,11 @@ export default class ExpressionFormatter {
433486
}
434487

435488
private formatLineComment(node: LineCommentNode) {
489+
if (!this.inline && this.cfg.commaPosition === 'trailing' && this.isNextNonCommentNodeComma()) {
490+
// Store the comment to be output after the comma
491+
this.pendingLineComment.push(node);
492+
return;
493+
}
436494
if (isMultiline(node.precedingWhitespace || '')) {
437495
this.layout.add(WS.NEWLINE, WS.INDENT, node.text, WS.MANDATORY_NEWLINE, WS.INDENT);
438496
} else if (this.layout.getLayoutItems().length > 0) {
@@ -442,8 +500,25 @@ export default class ExpressionFormatter {
442500
this.layout.add(node.text, WS.MANDATORY_NEWLINE, WS.INDENT);
443501
}
444502
}
503+
private isNextNonCommentNodeComma(): boolean {
504+
let lookAheadIndex = this.index + 1;
505+
while (lookAheadIndex < this.nodes.length) {
506+
const nextNode = this.nodes[lookAheadIndex];
507+
if (nextNode.type === NodeType.line_comment || nextNode.type === NodeType.block_comment) {
508+
lookAheadIndex++;
509+
} else {
510+
return nextNode.type === NodeType.comma;
511+
}
512+
}
513+
return false;
514+
}
445515

446516
private formatBlockComment(node: BlockCommentNode | DisableCommentNode) {
517+
if (!this.inline && this.cfg.commaPosition === 'trailing' && this.isNextNonCommentNodeComma()) {
518+
// Store the comment to be output after the comma
519+
this.pendingLineComment.push(node);
520+
return;
521+
}
447522
if (node.type === NodeType.block_comment && this.isMultilineBlockComment(node)) {
448523
this.splitBlockComment(node.text).forEach(line => {
449524
this.layout.add(WS.NEWLINE, WS.INDENT, line);

test/features/comments.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ export default function supportsComments(format: FormatFn, opts: CommentsConfig
9393
`)
9494
).toBe(dedent`
9595
SELECT
96-
a --comment
97-
,
96+
a, --comment
9897
b
9998
`);
10099
});

test/options/commaPosition.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,31 @@ export default function supportsCommaPosition(format: FormatFn) {
242242
`
243243
);
244244
});
245+
246+
it('supports trailing comma with comments from a leading comma formatted query', () => {
247+
const result = format(
248+
`SELECT
249+
id -- comment 1
250+
,first_name -- comment 2
251+
/* block comment */
252+
,last_name
253+
,email
254+
FROM
255+
users;
256+
`,
257+
{ commaPosition: 'trailing' }
258+
);
259+
expect(result).toBe(
260+
dedent`
261+
SELECT
262+
id, -- comment 1
263+
first_name, -- comment 2
264+
/* block comment */
265+
last_name,
266+
email
267+
FROM
268+
users;
269+
`
270+
);
271+
});
245272
}

0 commit comments

Comments
 (0)