-
-
Notifications
You must be signed in to change notification settings - Fork 1
Fix formatter comment handling around SQL expressions #884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "rawsql-ts": patch | ||
| --- | ||
|
|
||
| Fix SQL formatter comment handling for comma-prefixed expressions, LIMIT/OFFSET values, and parenthesized predicates. Comments are no longer duplicated around function arguments, ORDER BY/GROUP BY items, and parenthesized WHERE predicates, comments after HAVING/JOIN ON/LIMIT/OFFSET keywords are preserved, and comments after list commas now use readable before-comma and after-comma layouts for SELECT, ORDER BY, and GROUP BY items. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -344,13 +344,19 @@ export class SqlPrinter { | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const hasRenderableLeadingComment = leadingCommentContexts.some(item => item.shouldRender); | ||||||||||||||||||||||||
| const leadingCommentIndentLevel = hasRenderableLeadingComment | ||||||||||||||||||||||||
| ? this.getLeadingCommentIndentLevel(parentContainerType, level) | ||||||||||||||||||||||||
| ? this.getLeadingCommentIndentLevel( | ||||||||||||||||||||||||
| token.containerType === SqlPrintTokenContainerType.SelectItem | ||||||||||||||||||||||||
| ? token.containerType | ||||||||||||||||||||||||
| : parentContainerType, | ||||||||||||||||||||||||
| level | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| : null; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| hasRenderableLeadingComment | ||||||||||||||||||||||||
| && !this.isOnelineMode() | ||||||||||||||||||||||||
| && this.shouldAddNewlineBeforeLeadingComments(parentContainerType) | ||||||||||||||||||||||||
| && !this.shouldKeepLeadingCommentOnCommaLine() | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| const currentLine = this.linePrinter.getCurrentLine(); | ||||||||||||||||||||||||
| if (currentLine.text.trim().length > 0) { | ||||||||||||||||||||||||
|
|
@@ -373,6 +379,12 @@ export class SqlPrinter { | |||||||||||||||||||||||
| leading.context, | ||||||||||||||||||||||||
| false | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| token.containerType === SqlPrintTokenContainerType.SelectItem && | ||||||||||||||||||||||||
| this.smartCommentBlockBuilder?.mode === 'line' | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| this.flushSmartCommentBlockBuilder(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (this.smartCommentBlockBuilder && token.containerType !== SqlPrintTokenContainerType.CommentBlock && token.type !== SqlPrintTokenType.commentNewline) { | ||||||||||||||||||||||||
|
|
@@ -400,7 +412,17 @@ export class SqlPrinter { | |||||||||||||||||||||||
| if (!this.shouldRenderComment(token, effectiveCommentContext)) { | ||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (this.shouldKeepLeadingCommentOnCommaLine()) { | ||||||||||||||||||||||||
| this.ensureTrailingSpace(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| const commentLevel = this.getCommentBaseIndentLevel(level, parentContainerType); | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| parentContainerType === SqlPrintTokenContainerType.SelectItem && | ||||||||||||||||||||||||
| effectiveCommentContext.position === 'leading' && | ||||||||||||||||||||||||
| this.linePrinter.getCurrentLine().text.trim() === '' | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| this.linePrinter.getCurrentLine().level = commentLevel; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| this.handleCommentBlockContainer(token, commentLevel, effectiveCommentContext); | ||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
@@ -1375,7 +1397,11 @@ export class SqlPrinter { | |||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| const content = this.extractLineCommentContent(trimmed); | ||||||||||||||||||||||||
| if (content !== null) { | ||||||||||||||||||||||||
| lines.push(content); | ||||||||||||||||||||||||
| if (!content && trimmed.startsWith('/*') && trimmed.endsWith('*/')) { | ||||||||||||||||||||||||
| lines.push(this.sanitizeCommentLine(this.escapeCommentDelimiters(trimmed))); | ||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||
| lines.push(content); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+1400
to
+1404
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid rewriting empty block comments into escaped delimiter text. At Line 1401, Suggested fix if (content !== null) {
- if (!content && trimmed.startsWith('/*') && trimmed.endsWith('*/')) {
- lines.push(this.sanitizeCommentLine(this.escapeCommentDelimiters(trimmed)));
- } else {
- lines.push(content);
- }
+ if (!content && trimmed.startsWith('/*') && trimmed.endsWith('*/')) {
+ // Preserve empty block-comment intent without emitting escaped delimiters as text.
+ lines.push('');
+ } else {
+ lines.push(content);
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
@@ -1575,6 +1601,12 @@ export class SqlPrinter { | |||||||||||||||||||||||
| if (parentType === SqlPrintTokenContainerType.SelectClause) { | ||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| parentType === SqlPrintTokenContainerType.OrderByItem || | ||||||||||||||||||||||||
| parentType === SqlPrintTokenContainerType.GroupByClause | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (parentType === SqlPrintTokenContainerType.ExplainStatement) { | ||||||||||||||||||||||||
| // Ensure EXPLAIN targets print header comments on a dedicated line. | ||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||
|
|
@@ -1586,6 +1618,15 @@ export class SqlPrinter { | |||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private getLeadingCommentIndentLevel(parentType: SqlPrintTokenContainerType | undefined, currentLevel: number): number { | ||||||||||||||||||||||||
| if (parentType === SqlPrintTokenContainerType.SelectItem) { | ||||||||||||||||||||||||
| return currentLevel; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||
| parentType === SqlPrintTokenContainerType.OrderByItem || | ||||||||||||||||||||||||
| parentType === SqlPrintTokenContainerType.GroupByClause | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| return currentLevel; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (parentType === SqlPrintTokenContainerType.TupleExpression) { | ||||||||||||||||||||||||
| return currentLevel + 1; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
@@ -1603,6 +1644,13 @@ export class SqlPrinter { | |||||||||||||||||||||||
| return currentLevel; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private shouldKeepLeadingCommentOnCommaLine(): boolean { | ||||||||||||||||||||||||
| if (this.commaBreak !== 'before') { | ||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| return this.linePrinter.getCurrentLine().text.trim() === ','; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
| * Determines if the printer is in oneliner mode. | ||||||||||||||||||||||||
| * Oneliner mode uses single spaces instead of actual newlines. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure list-continuation marker cleanup is exception-safe.
Line 2145 adds to
listContinuationCommentComponents, but cleanup at Line 2149 is skipped ifthis.visit(arg.items[i])throws. That can leak transient formatter state into subsequent reuse of the same parser instance.Suggested fix
for (let i = 0; i < arg.items.length; i++) { if (i > 0) { token.innerTokens.push(...SqlPrintTokenParser.commaSpaceTokens()); - this.listContinuationCommentComponents.add(arg.items[i]); + this.listContinuationCommentComponents.add(arg.items[i]); } - token.innerTokens.push(this.visit(arg.items[i])); - if (i > 0) { - this.listContinuationCommentComponents.delete(arg.items[i]); - } + try { + token.innerTokens.push(this.visit(arg.items[i])); + } finally { + if (i > 0) { + this.listContinuationCommentComponents.delete(arg.items[i]); + } + } }🤖 Prompt for AI Agents