rawsql-ts 0.24.2 → 0.24.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +10 -1
  2. package/dist/esm/index.min.js +9 -9
  3. package/dist/esm/index.min.js.map +2 -2
  4. package/dist/esm/models/ValueComponent.d.ts +2 -1
  5. package/dist/esm/models/ValueComponent.js +2 -1
  6. package/dist/esm/models/ValueComponent.js.map +1 -1
  7. package/dist/esm/parsers/CommandExpressionParser.d.ts +2 -0
  8. package/dist/esm/parsers/CommandExpressionParser.js +49 -4
  9. package/dist/esm/parsers/CommandExpressionParser.js.map +1 -1
  10. package/dist/esm/parsers/HavingParser.js +6 -0
  11. package/dist/esm/parsers/HavingParser.js.map +1 -1
  12. package/dist/esm/parsers/JoinOnClauseParser.js +6 -0
  13. package/dist/esm/parsers/JoinOnClauseParser.js.map +1 -1
  14. package/dist/esm/parsers/LimitClauseParser.js +6 -0
  15. package/dist/esm/parsers/LimitClauseParser.js.map +1 -1
  16. package/dist/esm/parsers/OffsetClauseParser.js +6 -0
  17. package/dist/esm/parsers/OffsetClauseParser.js.map +1 -1
  18. package/dist/esm/parsers/OrderByClauseParser.d.ts +2 -0
  19. package/dist/esm/parsers/OrderByClauseParser.js +42 -29
  20. package/dist/esm/parsers/OrderByClauseParser.js.map +1 -1
  21. package/dist/esm/parsers/ParameterDecorator.d.ts +2 -2
  22. package/dist/esm/parsers/ParameterDecorator.js +1 -1
  23. package/dist/esm/parsers/ParameterDecorator.js.map +1 -1
  24. package/dist/esm/parsers/ParameterExpressionParser.js +2 -1
  25. package/dist/esm/parsers/ParameterExpressionParser.js.map +1 -1
  26. package/dist/esm/parsers/SqlPrintTokenParser.d.ts +18 -4
  27. package/dist/esm/parsers/SqlPrintTokenParser.js +135 -18
  28. package/dist/esm/parsers/SqlPrintTokenParser.js.map +1 -1
  29. package/dist/esm/parsers/ValueParser.js +5 -3
  30. package/dist/esm/parsers/ValueParser.js.map +1 -1
  31. package/dist/esm/transformers/FormatOptionResolver.js +1 -0
  32. package/dist/esm/transformers/FormatOptionResolver.js.map +1 -1
  33. package/dist/esm/transformers/LinePrinter.d.ts +1 -1
  34. package/dist/esm/transformers/OnelineFormattingHelper.d.ts +1 -0
  35. package/dist/esm/transformers/OnelineFormattingHelper.js.map +1 -1
  36. package/dist/esm/transformers/SqlFormatter.d.ts +9 -5
  37. package/dist/esm/transformers/SqlFormatter.js +4 -4
  38. package/dist/esm/transformers/SqlFormatter.js.map +1 -1
  39. package/dist/esm/transformers/SqlPrinter.d.ts +26 -2
  40. package/dist/esm/transformers/SqlPrinter.js +325 -26
  41. package/dist/esm/transformers/SqlPrinter.js.map +1 -1
  42. package/dist/index.min.js +9 -9
  43. package/dist/index.min.js.map +2 -2
  44. package/dist/models/ValueComponent.js +2 -1
  45. package/dist/models/ValueComponent.js.map +1 -1
  46. package/dist/parsers/CommandExpressionParser.js +49 -4
  47. package/dist/parsers/CommandExpressionParser.js.map +1 -1
  48. package/dist/parsers/HavingParser.js +6 -0
  49. package/dist/parsers/HavingParser.js.map +1 -1
  50. package/dist/parsers/JoinOnClauseParser.js +6 -0
  51. package/dist/parsers/JoinOnClauseParser.js.map +1 -1
  52. package/dist/parsers/LimitClauseParser.js +6 -0
  53. package/dist/parsers/LimitClauseParser.js.map +1 -1
  54. package/dist/parsers/OffsetClauseParser.js +6 -0
  55. package/dist/parsers/OffsetClauseParser.js.map +1 -1
  56. package/dist/parsers/OrderByClauseParser.js +42 -29
  57. package/dist/parsers/OrderByClauseParser.js.map +1 -1
  58. package/dist/parsers/ParameterDecorator.js +1 -1
  59. package/dist/parsers/ParameterDecorator.js.map +1 -1
  60. package/dist/parsers/ParameterExpressionParser.js +2 -1
  61. package/dist/parsers/ParameterExpressionParser.js.map +1 -1
  62. package/dist/parsers/SqlPrintTokenParser.js +136 -18
  63. package/dist/parsers/SqlPrintTokenParser.js.map +1 -1
  64. package/dist/parsers/ValueParser.js +5 -3
  65. package/dist/parsers/ValueParser.js.map +1 -1
  66. package/dist/src/models/ValueComponent.d.ts +2 -1
  67. package/dist/src/parsers/CommandExpressionParser.d.ts +2 -0
  68. package/dist/src/parsers/OrderByClauseParser.d.ts +2 -0
  69. package/dist/src/parsers/ParameterDecorator.d.ts +2 -2
  70. package/dist/src/parsers/SqlPrintTokenParser.d.ts +18 -4
  71. package/dist/src/transformers/LinePrinter.d.ts +1 -1
  72. package/dist/src/transformers/OnelineFormattingHelper.d.ts +1 -0
  73. package/dist/src/transformers/SqlFormatter.d.ts +9 -5
  74. package/dist/src/transformers/SqlPrinter.d.ts +26 -2
  75. package/dist/transformers/FormatOptionResolver.js +1 -0
  76. package/dist/transformers/FormatOptionResolver.js.map +1 -1
  77. package/dist/transformers/OnelineFormattingHelper.js.map +1 -1
  78. package/dist/transformers/SqlFormatter.js +5 -3
  79. package/dist/transformers/SqlFormatter.js.map +1 -1
  80. package/dist/transformers/SqlPrinter.js +325 -26
  81. package/dist/transformers/SqlPrinter.js.map +1 -1
  82. package/dist/tsconfig.browser.tsbuildinfo +1 -1
  83. package/package.json +2 -2
@@ -30,7 +30,7 @@ export class SqlPrinter {
30
30
  * @param options Optional style settings for pretty printing
31
31
  */
32
32
  constructor(options) {
33
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
33
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
34
34
  /** Track whether we are currently inside a WITH clause for full-oneline formatting */
35
35
  this.insideWithClause = false;
36
36
  /** Tracks nesting depth while formatting MERGE WHEN predicate segments */
@@ -43,6 +43,8 @@ export class SqlPrinter {
43
43
  this.pendingLineCommentBreak = null;
44
44
  /** Accumulates lines when reconstructing multi-line block comments inside CommentBlocks */
45
45
  this.smartCommentBlockBuilder = null;
46
+ /** Tracks function calls whose arguments must expand because rendered comments are present */
47
+ this.commentedFunctionCallDepth = 0;
46
48
  // Resolve logical options to their control character representations before applying defaults.
47
49
  const resolvedIndentChar = resolveIndentCharOption(options === null || options === void 0 ? void 0 : options.indentChar);
48
50
  const resolvedNewline = resolveNewlineOption(options === null || options === void 0 ? void 0 : options.newline);
@@ -56,26 +58,28 @@ export class SqlPrinter {
56
58
  this.valuesCommaBreak = (_d = options === null || options === void 0 ? void 0 : options.valuesCommaBreak) !== null && _d !== void 0 ? _d : this.commaBreak;
57
59
  this.andBreak = (_e = options === null || options === void 0 ? void 0 : options.andBreak) !== null && _e !== void 0 ? _e : 'none';
58
60
  this.orBreak = (_f = options === null || options === void 0 ? void 0 : options.orBreak) !== null && _f !== void 0 ? _f : 'none';
59
- this.joinOnBreak = (_g = options === null || options === void 0 ? void 0 : options.joinOnBreak) !== null && _g !== void 0 ? _g : 'none';
60
- this.keywordCase = (_h = options === null || options === void 0 ? void 0 : options.keywordCase) !== null && _h !== void 0 ? _h : 'none';
61
+ this.joinOnBreak = this.normalizeJoinOnBreak(options === null || options === void 0 ? void 0 : options.joinOnBreak);
62
+ this.keywordCase = this.normalizeKeywordCase(options === null || options === void 0 ? void 0 : options.keywordCase);
61
63
  this.commentExportMode = this.resolveCommentExportMode(options === null || options === void 0 ? void 0 : options.exportComment);
62
- this.withClauseStyle = (_j = options === null || options === void 0 ? void 0 : options.withClauseStyle) !== null && _j !== void 0 ? _j : 'standard';
63
- this.commentStyle = (_k = options === null || options === void 0 ? void 0 : options.commentStyle) !== null && _k !== void 0 ? _k : 'block';
64
- this.parenthesesOneLine = (_l = options === null || options === void 0 ? void 0 : options.parenthesesOneLine) !== null && _l !== void 0 ? _l : false;
65
- this.betweenOneLine = (_m = options === null || options === void 0 ? void 0 : options.betweenOneLine) !== null && _m !== void 0 ? _m : false;
66
- this.valuesOneLine = (_o = options === null || options === void 0 ? void 0 : options.valuesOneLine) !== null && _o !== void 0 ? _o : false;
67
- this.joinOneLine = (_p = options === null || options === void 0 ? void 0 : options.joinOneLine) !== null && _p !== void 0 ? _p : false;
68
- this.caseOneLine = (_q = options === null || options === void 0 ? void 0 : options.caseOneLine) !== null && _q !== void 0 ? _q : false;
69
- this.subqueryOneLine = (_r = options === null || options === void 0 ? void 0 : options.subqueryOneLine) !== null && _r !== void 0 ? _r : false;
70
- this.indentNestedParentheses = (_s = options === null || options === void 0 ? void 0 : options.indentNestedParentheses) !== null && _s !== void 0 ? _s : false;
71
- this.insertColumnsOneLine = (_t = options === null || options === void 0 ? void 0 : options.insertColumnsOneLine) !== null && _t !== void 0 ? _t : false;
72
- this.whenOneLine = (_u = options === null || options === void 0 ? void 0 : options.whenOneLine) !== null && _u !== void 0 ? _u : false;
64
+ this.withClauseStyle = (_g = options === null || options === void 0 ? void 0 : options.withClauseStyle) !== null && _g !== void 0 ? _g : 'standard';
65
+ this.commentStyle = (_h = options === null || options === void 0 ? void 0 : options.commentStyle) !== null && _h !== void 0 ? _h : 'block';
66
+ this.parenthesesOneLine = (_j = options === null || options === void 0 ? void 0 : options.parenthesesOneLine) !== null && _j !== void 0 ? _j : false;
67
+ this.betweenOneLine = (_k = options === null || options === void 0 ? void 0 : options.betweenOneLine) !== null && _k !== void 0 ? _k : false;
68
+ this.valuesOneLine = (_l = options === null || options === void 0 ? void 0 : options.valuesOneLine) !== null && _l !== void 0 ? _l : false;
69
+ this.inOneLine = (_m = options === null || options === void 0 ? void 0 : options.inOneLine) !== null && _m !== void 0 ? _m : false;
70
+ this.joinOneLine = (_o = options === null || options === void 0 ? void 0 : options.joinOneLine) !== null && _o !== void 0 ? _o : false;
71
+ this.caseOneLine = (_p = options === null || options === void 0 ? void 0 : options.caseOneLine) !== null && _p !== void 0 ? _p : false;
72
+ this.subqueryOneLine = (_q = options === null || options === void 0 ? void 0 : options.subqueryOneLine) !== null && _q !== void 0 ? _q : false;
73
+ this.indentNestedParentheses = (_r = options === null || options === void 0 ? void 0 : options.indentNestedParentheses) !== null && _r !== void 0 ? _r : false;
74
+ this.insertColumnsOneLine = (_s = options === null || options === void 0 ? void 0 : options.insertColumnsOneLine) !== null && _s !== void 0 ? _s : false;
75
+ this.whenOneLine = (_t = options === null || options === void 0 ? void 0 : options.whenOneLine) !== null && _t !== void 0 ? _t : false;
73
76
  this.oneLineMaxLength = this.normalizeOneLineMaxLength(options === null || options === void 0 ? void 0 : options.oneLineMaxLength);
74
- this.joinConditionContinuationIndent = (_v = options === null || options === void 0 ? void 0 : options.joinConditionContinuationIndent) !== null && _v !== void 0 ? _v : false;
77
+ this.joinConditionContinuationIndent = (_u = options === null || options === void 0 ? void 0 : options.joinConditionContinuationIndent) !== null && _u !== void 0 ? _u : false;
75
78
  const onelineOptions = {
76
79
  parenthesesOneLine: this.parenthesesOneLine,
77
80
  betweenOneLine: this.betweenOneLine,
78
81
  valuesOneLine: this.valuesOneLine,
82
+ inOneLine: this.inOneLine,
79
83
  joinOneLine: this.joinOneLine,
80
84
  caseOneLine: this.caseOneLine,
81
85
  subqueryOneLine: this.subqueryOneLine,
@@ -85,7 +89,7 @@ export class SqlPrinter {
85
89
  this.onelineHelper = new OnelineFormattingHelper(onelineOptions);
86
90
  this.linePrinter = new LinePrinter(this.indentChar, this.indentSize, this.newline, this.commaBreak);
87
91
  // Initialize
88
- this.indentIncrementContainers = new Set((_w = options === null || options === void 0 ? void 0 : options.indentIncrementContainerTypes) !== null && _w !== void 0 ? _w : [
92
+ this.indentIncrementContainers = new Set((_v = options === null || options === void 0 ? void 0 : options.indentIncrementContainerTypes) !== null && _v !== void 0 ? _v : [
89
93
  SqlPrintTokenContainerType.SelectClause,
90
94
  SqlPrintTokenContainerType.ReturningClause,
91
95
  SqlPrintTokenContainerType.FromClause,
@@ -135,6 +139,7 @@ export class SqlPrinter {
135
139
  this.pendingLineCommentBreak = null;
136
140
  this.smartCommentBlockBuilder = null;
137
141
  this.expandedOneLineFallbackTokens = new WeakSet();
142
+ this.commentedFunctionCallDepth = 0;
138
143
  if (this.linePrinter.lines.length > 0 && level !== this.linePrinter.lines[0].level) {
139
144
  this.linePrinter.lines[0].level = level;
140
145
  }
@@ -177,6 +182,7 @@ export class SqlPrinter {
177
182
  }
178
183
  }
179
184
  appendToken(token, level, parentContainerType, caseContextDepth = 0, indentParentActive = false, commentContext, previousSiblingWasOpenParen = false) {
185
+ var _a;
180
186
  // Track WITH clause context for full-oneline formatting
181
187
  const wasInsideWithClause = this.insideWithClause;
182
188
  if (token.containerType === SqlPrintTokenContainerType.WithClause && this.withClauseStyle === 'full-oneline') {
@@ -205,12 +211,16 @@ export class SqlPrinter {
205
211
  }
206
212
  }
207
213
  const hasRenderableLeadingComment = leadingCommentContexts.some(item => item.shouldRender);
214
+ const leadingCommentFollowsLogicalOperator = hasRenderableLeadingComment && this.currentLineEndsWithLogicalOperator();
208
215
  const leadingCommentIndentLevel = hasRenderableLeadingComment
209
- ? this.getLeadingCommentIndentLevel(parentContainerType, level)
216
+ ? this.getLeadingCommentIndentLevel(token.containerType === SqlPrintTokenContainerType.SelectItem
217
+ ? token.containerType
218
+ : parentContainerType, level)
210
219
  : null;
211
220
  if (hasRenderableLeadingComment
212
221
  && !this.isOnelineMode()
213
- && this.shouldAddNewlineBeforeLeadingComments(parentContainerType)) {
222
+ && this.shouldAddNewlineBeforeLeadingComments(parentContainerType)
223
+ && !this.shouldKeepLeadingCommentOnCommaLine()) {
214
224
  const currentLine = this.linePrinter.getCurrentLine();
215
225
  if (currentLine.text.trim().length > 0) {
216
226
  // Align the newline before leading comments with the intended comment indentation.
@@ -223,13 +233,38 @@ export class SqlPrinter {
223
233
  }
224
234
  // Keep leading comment processing aligned with its computed indentation level.
225
235
  this.appendToken(leading.token, leadingCommentIndentLevel !== null && leadingCommentIndentLevel !== void 0 ? leadingCommentIndentLevel : level, token.containerType, caseContextDepth, indentParentActive, leading.context, false);
236
+ if (token.containerType === SqlPrintTokenContainerType.SelectItem &&
237
+ ((_a = this.smartCommentBlockBuilder) === null || _a === void 0 ? void 0 : _a.mode) === 'line') {
238
+ this.flushSmartCommentBlockBuilder();
239
+ }
240
+ if (containerIsTopLevel &&
241
+ !this.isOnelineMode() &&
242
+ this.pendingLineCommentBreak === null &&
243
+ this.linePrinter.getCurrentLine().text.trim() !== '') {
244
+ this.linePrinter.appendNewline(level);
245
+ }
246
+ }
247
+ if (hasRenderableLeadingComment &&
248
+ containerIsTopLevel &&
249
+ !this.isOnelineMode() &&
250
+ this.linePrinter.getCurrentLine().text.trim() !== '') {
251
+ this.linePrinter.appendNewline(level);
252
+ }
253
+ if (leadingCommentFollowsLogicalOperator &&
254
+ this.pendingLineCommentBreak === null &&
255
+ !this.isOnelineMode() &&
256
+ this.linePrinter.getCurrentLine().text.trim().endsWith('*/')) {
257
+ this.linePrinter.appendNewline(level + 1);
226
258
  }
227
259
  if (this.smartCommentBlockBuilder && token.containerType !== SqlPrintTokenContainerType.CommentBlock && token.type !== SqlPrintTokenType.commentNewline) {
228
260
  this.flushSmartCommentBlockBuilder();
229
261
  }
230
262
  if (this.pendingLineCommentBreak !== null) {
231
263
  if (!this.isOnelineMode()) {
232
- this.linePrinter.appendNewline(this.pendingLineCommentBreak);
264
+ const pendingBreakLevel = leadingCommentFollowsLogicalOperator
265
+ ? this.pendingLineCommentBreak + 1
266
+ : this.pendingLineCommentBreak;
267
+ this.linePrinter.appendNewline(pendingBreakLevel);
233
268
  }
234
269
  const shouldSkipToken = token.type === SqlPrintTokenType.commentNewline;
235
270
  this.pendingLineCommentBreak = null;
@@ -246,7 +281,15 @@ export class SqlPrinter {
246
281
  if (!this.shouldRenderComment(token, effectiveCommentContext)) {
247
282
  return;
248
283
  }
284
+ if (this.shouldKeepLeadingCommentOnCommaLine()) {
285
+ this.ensureTrailingSpace();
286
+ }
249
287
  const commentLevel = this.getCommentBaseIndentLevel(level, parentContainerType);
288
+ if (parentContainerType === SqlPrintTokenContainerType.SelectItem &&
289
+ effectiveCommentContext.position === 'leading' &&
290
+ this.linePrinter.getCurrentLine().text.trim() === '') {
291
+ this.linePrinter.getCurrentLine().level = commentLevel;
292
+ }
250
293
  this.handleCommentBlockContainer(token, commentLevel, effectiveCommentContext);
251
294
  return;
252
295
  }
@@ -261,6 +304,9 @@ export class SqlPrinter {
261
304
  else if (token.type === SqlPrintTokenType.comma) {
262
305
  this.handleCommaToken(token, level, parentContainerType);
263
306
  }
307
+ else if (token.type === SqlPrintTokenType.argumentSplitter) {
308
+ this.handleArgumentSplitterToken(token, level, parentContainerType);
309
+ }
264
310
  else if (token.type === SqlPrintTokenType.parenthesis) {
265
311
  this.handleParenthesisToken(token, level, indentParentActive, parentContainerType);
266
312
  }
@@ -294,6 +340,9 @@ export class SqlPrinter {
294
340
  return; // Return early to avoid processing innerTokens
295
341
  }
296
342
  }
343
+ else if (this.inOneLine && this.isInValueListExpression(token) && this.tryHandleInOneLineToken(token, level)) {
344
+ return; // Return early to avoid processing innerTokens
345
+ }
297
346
  else if (this.shouldFormatContainerAsOneline(token, shouldIndentNested) && this.tryHandleOnelineToken(token, level)) {
298
347
  return; // Return early to avoid processing innerTokens
299
348
  }
@@ -381,6 +430,11 @@ export class SqlPrinter {
381
430
  if (enteredJoinOnClause) {
382
431
  this.joinOnClauseDepth++;
383
432
  }
433
+ const enteredCommentedFunctionCall = this.shouldExpandCommentedFunctionCall(token);
434
+ if (enteredCommentedFunctionCall) {
435
+ this.commentedFunctionCallDepth++;
436
+ }
437
+ const enteredLogicalOperatorWithComment = this.isLogicalOperatorWithComment(token);
384
438
  for (let i = leadingCommentCount; i < token.innerTokens.length; i++) {
385
439
  const child = token.innerTokens[i];
386
440
  const nextChild = token.innerTokens[i + 1];
@@ -431,7 +485,10 @@ export class SqlPrinter {
431
485
  const childCommentContext = child.containerType === SqlPrintTokenContainerType.CommentBlock
432
486
  ? { position: 'inline', isTopLevelContainer: containerIsTopLevel }
433
487
  : undefined;
434
- this.appendToken(child, innerLevel, token.containerType, nextCaseContextDepth, childIndentParentActive, childCommentContext, previousChildWasOpenParen);
488
+ const childLevel = enteredCommentedFunctionCall && child.containerType === SqlPrintTokenContainerType.ValueList
489
+ ? innerLevel + 1
490
+ : innerLevel;
491
+ this.appendToken(child, childLevel, token.containerType, nextCaseContextDepth, childIndentParentActive, childCommentContext, previousChildWasOpenParen);
435
492
  if (inMergePredicate) {
436
493
  this.mergeWhenPredicateDepth--;
437
494
  }
@@ -439,12 +496,24 @@ export class SqlPrinter {
439
496
  mergePredicateActive = false;
440
497
  }
441
498
  }
499
+ if (enteredLogicalOperatorWithComment && !this.isOnelineMode()) {
500
+ const currentLine = this.linePrinter.getCurrentLine();
501
+ if (currentLine.text.trim() === '') {
502
+ currentLine.level = level + 1;
503
+ }
504
+ else {
505
+ this.linePrinter.appendNewline(level + 1);
506
+ }
507
+ }
442
508
  if (this.smartCommentBlockBuilder && this.smartCommentBlockBuilder.mode === 'line') {
443
509
  this.flushSmartCommentBlockBuilder();
444
510
  }
445
511
  if (enteredJoinOnClause) {
446
512
  this.joinOnClauseDepth--;
447
513
  }
514
+ if (enteredCommentedFunctionCall) {
515
+ this.commentedFunctionCallDepth--;
516
+ }
448
517
  // Exit WITH clause context when we finish processing WithClause container
449
518
  if (token.containerType === SqlPrintTokenContainerType.WithClause && this.withClauseStyle === 'full-oneline') {
450
519
  this.insideWithClause = false;
@@ -472,6 +541,33 @@ export class SqlPrinter {
472
541
  return false;
473
542
  }
474
543
  }
544
+ shouldExpandCommentedFunctionCall(token) {
545
+ return this.commentExportMode !== 'none' &&
546
+ token.containerType === SqlPrintTokenContainerType.FunctionCall &&
547
+ token.innerTokens.some(child => child.containerType === SqlPrintTokenContainerType.ValueList &&
548
+ this.containsCommentBlock(child));
549
+ }
550
+ containsCommentBlock(token) {
551
+ if (token.containerType === SqlPrintTokenContainerType.CommentBlock) {
552
+ return true;
553
+ }
554
+ return token.innerTokens.some(child => this.containsCommentBlock(child));
555
+ }
556
+ containsRenderableCommentBlock(token, context) {
557
+ if (token.containerType === SqlPrintTokenContainerType.CommentBlock) {
558
+ return this.shouldRenderComment(token, context);
559
+ }
560
+ return token.innerTokens.some(child => this.containsRenderableCommentBlock(child, context));
561
+ }
562
+ isLogicalOperatorWithComment(token) {
563
+ return this.containsCommentBlock(token) &&
564
+ token.type === SqlPrintTokenType.operator &&
565
+ ['and', 'or'].includes(token.text.toLowerCase());
566
+ }
567
+ currentLineEndsWithLogicalOperator() {
568
+ const trimmed = this.linePrinter.getCurrentLine().text.trim().toLowerCase();
569
+ return trimmed === 'and' || trimmed === 'or';
570
+ }
475
571
  isCaseContext(containerType) {
476
572
  switch (containerType) {
477
573
  case SqlPrintTokenContainerType.CaseExpression:
@@ -631,6 +727,14 @@ export class SqlPrinter {
631
727
  this.linePrinter.appendText(text);
632
728
  }
633
729
  }
730
+ handleArgumentSplitterToken(token, level, parentContainerType) {
731
+ this.linePrinter.appendText(token.text);
732
+ if (this.commentedFunctionCallDepth > 0 &&
733
+ parentContainerType === SqlPrintTokenContainerType.ValueList &&
734
+ !this.isOnelineMode()) {
735
+ this.linePrinter.appendNewline(level);
736
+ }
737
+ }
634
738
  handleAndOperatorToken(token, level, parentContainerType, caseContextDepth = 0) {
635
739
  const text = this.applyKeywordCase(token.text);
636
740
  if (caseContextDepth > 0) {
@@ -663,6 +767,12 @@ export class SqlPrinter {
663
767
  handleParenthesisToken(token, level, indentParentActive, parentContainerType) {
664
768
  if (token.text === '(') {
665
769
  this.linePrinter.appendText(token.text);
770
+ if (this.commentedFunctionCallDepth > 0 &&
771
+ parentContainerType === SqlPrintTokenContainerType.FunctionCall &&
772
+ !this.isOnelineMode()) {
773
+ this.linePrinter.appendNewline(level + 1);
774
+ return;
775
+ }
666
776
  if ((parentContainerType === SqlPrintTokenContainerType.InsertClause ||
667
777
  parentContainerType === SqlPrintTokenContainerType.MergeInsertAction) &&
668
778
  this.insertColumnsOneLine) {
@@ -681,6 +791,12 @@ export class SqlPrinter {
681
791
  return;
682
792
  }
683
793
  if (token.text === ')' && !this.isOnelineMode()) {
794
+ if (this.commentedFunctionCallDepth > 0 &&
795
+ parentContainerType === SqlPrintTokenContainerType.FunctionCall) {
796
+ this.linePrinter.appendNewline(level);
797
+ this.linePrinter.appendText(token.text);
798
+ return;
799
+ }
684
800
  if (this.shouldBreakBeforeClosingParen(parentContainerType)) {
685
801
  this.linePrinter.appendNewline(Math.max(level, 0));
686
802
  this.linePrinter.appendText(token.text);
@@ -780,6 +896,32 @@ export class SqlPrinter {
780
896
  shouldFormatContainerAsOneline(token, shouldIndentNested) {
781
897
  return this.onelineHelper.shouldFormatContainer(token, shouldIndentNested);
782
898
  }
899
+ isInValueListExpression(token) {
900
+ const parts = this.getInValueListParts(token);
901
+ return parts !== null;
902
+ }
903
+ getInValueListParts(token) {
904
+ if (token.containerType !== SqlPrintTokenContainerType.BinaryExpression) {
905
+ return null;
906
+ }
907
+ const significant = token.innerTokens.filter(child => child.type !== SqlPrintTokenType.space);
908
+ if (significant.length !== 3) {
909
+ return null;
910
+ }
911
+ const [left, operator, right] = significant;
912
+ const operatorText = operator.text.trim().toLowerCase();
913
+ if (operatorText !== 'in' && operatorText !== 'not in') {
914
+ return null;
915
+ }
916
+ if (right.containerType !== SqlPrintTokenContainerType.ParenExpression) {
917
+ return null;
918
+ }
919
+ const valueList = right.innerTokens.find(child => child.containerType === SqlPrintTokenContainerType.ValueList);
920
+ if (!valueList) {
921
+ return null;
922
+ }
923
+ return { left, operator, valueList };
924
+ }
783
925
  /**
784
926
  * Detects an INSERT column list that must stay on a single line.
785
927
  */
@@ -794,6 +936,12 @@ export class SqlPrinter {
794
936
  if (this.smartCommentBlockBuilder && this.smartCommentBlockBuilder.mode === 'line') {
795
937
  this.flushSmartCommentBlockBuilder();
796
938
  }
939
+ if (this.commentedFunctionCallDepth > 0 &&
940
+ parentContainerType === SqlPrintTokenContainerType.ValueList &&
941
+ (previousToken === null || previousToken === void 0 ? void 0 : previousToken.type) === SqlPrintTokenType.argumentSplitter &&
942
+ !this.isOnelineMode()) {
943
+ return;
944
+ }
797
945
  const currentLineText = this.linePrinter.getCurrentLine().text;
798
946
  if (this.onelineHelper.shouldSkipInsertClauseSpace(parentContainerType, nextToken, currentLineText)) {
799
947
  return;
@@ -921,7 +1069,12 @@ export class SqlPrinter {
921
1069
  }
922
1070
  else {
923
1071
  const content = normalized.lines[0];
924
- const lineText = content ? `-- ${content}` : '--';
1072
+ if (this.shouldFallbackSmartLineCommentToBlock()) {
1073
+ this.linePrinter.appendText(this.buildBlockComment([content], level));
1074
+ this.ensureTrailingSpace();
1075
+ return;
1076
+ }
1077
+ const lineText = this.buildSmartLineComment(content);
925
1078
  if (parentContainerType === SqlPrintTokenContainerType.CommentBlock) {
926
1079
  this.linePrinter.appendText(lineText);
927
1080
  this.pendingLineCommentBreak = this.resolveCommentIndentLevel(level, parentContainerType);
@@ -1002,11 +1155,26 @@ export class SqlPrinter {
1002
1155
  if (this.commentStyle !== 'smart') {
1003
1156
  const rawLines = this.extractRawCommentBlockLines(token);
1004
1157
  if (rawLines.length > 0) {
1158
+ if (context.position === 'leading' && context.isTopLevelContainer && !this.isOnelineMode()) {
1159
+ for (const line of rawLines) {
1160
+ this.linePrinter.appendText(`/* ${line} */`);
1161
+ this.linePrinter.appendNewline(level);
1162
+ }
1163
+ return;
1164
+ }
1005
1165
  const normalizedBlocks = rawLines.map(line => `/* ${line} */`).join(' ');
1006
1166
  const hasTrailingSpace = (_a = token.innerTokens) === null || _a === void 0 ? void 0 : _a.some(child => child.type === SqlPrintTokenType.space && child.text.includes(' '));
1007
1167
  this.linePrinter.appendText(hasTrailingSpace ? `${normalizedBlocks} ` : normalizedBlocks);
1008
1168
  return;
1009
1169
  }
1170
+ const directBlockComments = this.extractDirectBlockCommentTexts(token);
1171
+ if (directBlockComments.length > 1 && context.position === 'leading' && context.isTopLevelContainer && !this.isOnelineMode()) {
1172
+ for (const comment of directBlockComments) {
1173
+ this.linePrinter.appendText(comment);
1174
+ this.linePrinter.appendNewline(level);
1175
+ }
1176
+ return;
1177
+ }
1010
1178
  for (const child of token.innerTokens) {
1011
1179
  // Force inner comment tokens to render once the block is approved.
1012
1180
  const childContext = {
@@ -1018,6 +1186,11 @@ export class SqlPrinter {
1018
1186
  }
1019
1187
  return;
1020
1188
  }
1189
+ const emptyBlockCommentText = this.getEmptyBlockCommentText(token);
1190
+ if (emptyBlockCommentText !== null) {
1191
+ this.linePrinter.appendText(emptyBlockCommentText);
1192
+ return;
1193
+ }
1021
1194
  const lines = this.collectCommentBlockLines(token);
1022
1195
  if (lines.length === 0 && !this.smartCommentBlockBuilder) {
1023
1196
  // No meaningful content; treat as empty line comment to preserve spacing
@@ -1039,6 +1212,18 @@ export class SqlPrinter {
1039
1212
  this.smartCommentBlockBuilder.lines.push(...lines);
1040
1213
  }
1041
1214
  }
1215
+ getEmptyBlockCommentText(token) {
1216
+ var _a;
1217
+ const commentTokens = ((_a = token.innerTokens) !== null && _a !== void 0 ? _a : []).filter(child => child.type === SqlPrintTokenType.comment);
1218
+ if (commentTokens.length !== 1) {
1219
+ return null;
1220
+ }
1221
+ const trimmed = commentTokens[0].text.trim();
1222
+ if (!trimmed.startsWith('/*') || !trimmed.endsWith('*/')) {
1223
+ return null;
1224
+ }
1225
+ return trimmed.slice(2, -2).trim() === '' ? trimmed : null;
1226
+ }
1042
1227
  normalizeSmartBlockLine(raw) {
1043
1228
  // Remove trailing whitespace that only carries formatting artifacts
1044
1229
  let line = raw.replace(/\s+$/g, '');
@@ -1077,14 +1262,16 @@ export class SqlPrinter {
1077
1262
  const { lines, level, mode } = this.smartCommentBlockBuilder;
1078
1263
  if (mode === 'line') {
1079
1264
  const meaningfulLineCount = lines.filter(line => line.trim() !== '').length;
1080
- if (meaningfulLineCount > 1) {
1265
+ if (meaningfulLineCount > 1 || this.shouldFallbackSmartLineCommentToBlock()) {
1081
1266
  const blockText = this.buildBlockComment(lines, level);
1082
1267
  this.linePrinter.appendText(blockText);
1268
+ if (this.shouldFallbackSmartLineCommentToBlock()) {
1269
+ this.ensureTrailingSpace();
1270
+ }
1083
1271
  }
1084
1272
  else {
1085
1273
  const content = (_a = lines[0]) !== null && _a !== void 0 ? _a : '';
1086
- const lineText = content ? `-- ${content}` : '--';
1087
- this.linePrinter.appendText(lineText);
1274
+ this.linePrinter.appendText(this.buildSmartLineComment(content));
1088
1275
  }
1089
1276
  if (!this.isOnelineMode()) {
1090
1277
  this.linePrinter.appendNewline(level);
@@ -1093,6 +1280,12 @@ export class SqlPrinter {
1093
1280
  }
1094
1281
  this.smartCommentBlockBuilder = null;
1095
1282
  }
1283
+ shouldFallbackSmartLineCommentToBlock() {
1284
+ return this.isOnelineMode();
1285
+ }
1286
+ buildSmartLineComment(content) {
1287
+ return content ? `-- ${content}` : '--';
1288
+ }
1096
1289
  collectCommentBlockLines(token) {
1097
1290
  var _a;
1098
1291
  const lines = [];
@@ -1114,7 +1307,12 @@ export class SqlPrinter {
1114
1307
  }
1115
1308
  const content = this.extractLineCommentContent(trimmed);
1116
1309
  if (content !== null) {
1117
- lines.push(content);
1310
+ if (!content && trimmed.startsWith('/*') && trimmed.endsWith('*/')) {
1311
+ lines.push(this.sanitizeCommentLine(this.escapeCommentDelimiters(trimmed)));
1312
+ }
1313
+ else {
1314
+ lines.push(content);
1315
+ }
1118
1316
  }
1119
1317
  }
1120
1318
  }
@@ -1146,6 +1344,20 @@ export class SqlPrinter {
1146
1344
  }
1147
1345
  return lines;
1148
1346
  }
1347
+ extractDirectBlockCommentTexts(token) {
1348
+ var _a;
1349
+ const comments = [];
1350
+ for (const child of (_a = token.innerTokens) !== null && _a !== void 0 ? _a : []) {
1351
+ if (child.type !== SqlPrintTokenType.comment) {
1352
+ continue;
1353
+ }
1354
+ const trimmed = child.text.trim();
1355
+ if (trimmed.startsWith('/*') && trimmed.endsWith('*/')) {
1356
+ comments.push(trimmed);
1357
+ }
1358
+ }
1359
+ return comments;
1360
+ }
1149
1361
  normalizeCommentForSmart(text) {
1150
1362
  const trimmed = text.trim();
1151
1363
  let source = trimmed;
@@ -1300,6 +1512,10 @@ export class SqlPrinter {
1300
1512
  if (parentType === SqlPrintTokenContainerType.SelectClause) {
1301
1513
  return true;
1302
1514
  }
1515
+ if (parentType === SqlPrintTokenContainerType.OrderByItem ||
1516
+ parentType === SqlPrintTokenContainerType.GroupByClause) {
1517
+ return true;
1518
+ }
1303
1519
  if (parentType === SqlPrintTokenContainerType.ExplainStatement) {
1304
1520
  // Ensure EXPLAIN targets print header comments on a dedicated line.
1305
1521
  return true;
@@ -1310,6 +1526,13 @@ export class SqlPrinter {
1310
1526
  return false;
1311
1527
  }
1312
1528
  getLeadingCommentIndentLevel(parentType, currentLevel) {
1529
+ if (parentType === SqlPrintTokenContainerType.SelectItem) {
1530
+ return currentLevel;
1531
+ }
1532
+ if (parentType === SqlPrintTokenContainerType.OrderByItem ||
1533
+ parentType === SqlPrintTokenContainerType.GroupByClause) {
1534
+ return currentLevel;
1535
+ }
1313
1536
  if (parentType === SqlPrintTokenContainerType.TupleExpression) {
1314
1537
  return currentLevel + 1;
1315
1538
  }
@@ -1324,6 +1547,12 @@ export class SqlPrinter {
1324
1547
  }
1325
1548
  return currentLevel;
1326
1549
  }
1550
+ shouldKeepLeadingCommentOnCommaLine() {
1551
+ if (this.commaBreak !== 'before') {
1552
+ return false;
1553
+ }
1554
+ return this.linePrinter.getCurrentLine().text.trim() === ',';
1555
+ }
1327
1556
  /**
1328
1557
  * Determines if the printer is in oneliner mode.
1329
1558
  * Oneliner mode uses single spaces instead of actual newlines.
@@ -1332,12 +1561,18 @@ export class SqlPrinter {
1332
1561
  return this.newline === ' ';
1333
1562
  }
1334
1563
  normalizeOneLineMaxLength(value) {
1335
- if (value === undefined || !Number.isFinite(value) || value <= 0) {
1564
+ if (value == null || !Number.isFinite(value) || value <= 0) {
1336
1565
  return undefined;
1337
1566
  }
1338
1567
  const normalized = Math.floor(value);
1339
1568
  return normalized > 0 ? normalized : undefined;
1340
1569
  }
1570
+ normalizeKeywordCase(value) {
1571
+ return value === 'preserve' ? 'none' : value !== null && value !== void 0 ? value : 'none';
1572
+ }
1573
+ normalizeJoinOnBreak(value) {
1574
+ return value === 'before' ? 'before' : 'none';
1575
+ }
1341
1576
  fitsOneLineMaxLength(text) {
1342
1577
  if (this.oneLineMaxLength === undefined) {
1343
1578
  return true;
@@ -1351,9 +1586,13 @@ export class SqlPrinter {
1351
1586
  * Creates a nested SqlPrinter instance for proper CTE oneline formatting.
1352
1587
  */
1353
1588
  tryHandleCteOnelineToken(token, level) {
1589
+ if (this.shouldPreserveCteComments(token)) {
1590
+ return false;
1591
+ }
1354
1592
  const onelinePrinter = this.createCteOnelinePrinter();
1355
1593
  const onelineResult = onelinePrinter.print(token, level);
1356
1594
  let cleanedResult = this.cleanDuplicateSpaces(onelineResult);
1595
+ // TODO(rawsql-ts#890): replace this CTE-oneline whitespace fallback with a token-level rewrite.
1357
1596
  cleanedResult = cleanedResult.replace(/\(\s+/g, '(').replace(/\s+\)/g, ' )');
1358
1597
  const trimmedResult = cleanedResult.trim();
1359
1598
  if (!this.fitsOneLineMaxLength(trimmedResult)) {
@@ -1362,6 +1601,12 @@ export class SqlPrinter {
1362
1601
  this.linePrinter.appendText(trimmedResult);
1363
1602
  return true;
1364
1603
  }
1604
+ shouldPreserveCteComments(token) {
1605
+ return this.containsRenderableCommentBlock(token, {
1606
+ position: 'inline',
1607
+ isTopLevelContainer: false,
1608
+ });
1609
+ }
1365
1610
  /**
1366
1611
  * Creates a SqlPrinter instance configured for CTE oneline formatting.
1367
1612
  */
@@ -1399,6 +1644,59 @@ export class SqlPrinter {
1399
1644
  this.linePrinter.appendText(cleanedResult);
1400
1645
  return true;
1401
1646
  }
1647
+ tryHandleInOneLineToken(token, level) {
1648
+ const parts = this.getInValueListParts(token);
1649
+ if (!parts) {
1650
+ return false;
1651
+ }
1652
+ if (this.containsRenderableCommentBlock(parts.valueList, { position: 'inline', isTopLevelContainer: false })) {
1653
+ this.appendExpandedInValueList(parts, token, level);
1654
+ return true;
1655
+ }
1656
+ const onelinePrinter = this.createOnelinePrinter();
1657
+ const onelineResult = onelinePrinter.print(token, level);
1658
+ const cleanedResult = this.cleanDuplicateSpaces(onelineResult);
1659
+ if (this.fitsOneLineMaxLength(cleanedResult)) {
1660
+ this.linePrinter.appendText(cleanedResult);
1661
+ return true;
1662
+ }
1663
+ this.appendExpandedInValueList(parts, token, level);
1664
+ return true;
1665
+ }
1666
+ appendExpandedInValueList(parts, token, level) {
1667
+ this.appendToken(parts.left, level, token.containerType);
1668
+ this.ensureTrailingSpace();
1669
+ this.linePrinter.appendText(this.applyKeywordCase(parts.operator.text));
1670
+ this.ensureTrailingSpace();
1671
+ this.linePrinter.appendText('(');
1672
+ const valueGroups = this.splitValueListItems(parts.valueList);
1673
+ for (let i = 0; i < valueGroups.length; i++) {
1674
+ this.linePrinter.appendNewline(level + 1);
1675
+ if (i > 0) {
1676
+ this.linePrinter.appendText(',');
1677
+ this.ensureTrailingSpace();
1678
+ }
1679
+ for (const valueToken of valueGroups[i]) {
1680
+ this.appendToken(valueToken, level + 1, SqlPrintTokenContainerType.ValueList);
1681
+ }
1682
+ }
1683
+ this.linePrinter.appendNewline(level);
1684
+ this.linePrinter.appendText(')');
1685
+ }
1686
+ splitValueListItems(valueList) {
1687
+ const groups = [[]];
1688
+ for (const child of valueList.innerTokens) {
1689
+ if (child.type === SqlPrintTokenType.argumentSplitter) {
1690
+ groups.push([]);
1691
+ continue;
1692
+ }
1693
+ if (child.type === SqlPrintTokenType.space && groups[groups.length - 1].length === 0) {
1694
+ continue;
1695
+ }
1696
+ groups[groups.length - 1].push(child);
1697
+ }
1698
+ return groups.filter(group => group.length > 0);
1699
+ }
1402
1700
  isBooleanParenExpression(token) {
1403
1701
  if (token.containerType !== SqlPrintTokenContainerType.ParenExpression) {
1404
1702
  return false;
@@ -1546,6 +1844,7 @@ export class SqlPrinter {
1546
1844
  parenthesesOneLine: false, // Prevent recursive processing (avoid infinite loops)
1547
1845
  betweenOneLine: false, // Prevent recursive processing (avoid infinite loops)
1548
1846
  valuesOneLine: false, // Prevent recursive processing (avoid infinite loops)
1847
+ inOneLine: false, // Prevent recursive processing (avoid infinite loops)
1549
1848
  joinOneLine: false, // Prevent recursive processing (avoid infinite loops)
1550
1849
  caseOneLine: false, // Prevent recursive processing (avoid infinite loops)
1551
1850
  subqueryOneLine: false, // Prevent recursive processing (avoid infinite loops)