Repository: sql-formatter-org/sql-formatter
Pull request #559 (2023-02-03 19:26:16)

Improve formatting of block comments that aren't doc-comments
Distinguish between normal multi-line comments and doc-comments. Only add extra leading spaces to lines in doc-comments.

Refs #558


Patch:
diff --git a/src/formatter/ExpressionFormatter.ts b/src/formatter/ExpressionFormatter.ts
index 15e686ee..f17ff359 100644
--- a/src/formatter/ExpressionFormatter.ts
+++ b/src/formatter/ExpressionFormatter.ts
@@ -1,5 +1,5 @@
 import { FormatOptions } from '../FormatOptions.js';
-import { equalizeWhitespace, isMultiline } from '../utils.js';
+import { equalizeWhitespace, isMultiline, last } from '../utils.js';
 
 import Params from './Params.js';
 import { isTabularStyle } from './config.js';
@@ -356,8 +356,20 @@ export default class ExpressionFormatter {
     return isMultiline(node.text) || isMultiline(node.precedingWhitespace || '');
   }
 
+  private isDocComment(comment: string): boolean {
+    const lines = comment.split(/\n/);
+    return (
+      // first line starts with /* or /**
+      /^\/\*\*?$/.test(lines[0]) &&
+      // intermediate lines start with *
+      lines.slice(1, lines.length - 1).every(line => /^\s*\*/.test(line)) &&
+      // last line ends with */
+      /^\s*\*\/$/.test(last(lines) as string)
+    );
+  }
+
   // Breaks up block comment to multiple lines.
-  // For example this comment (dots representing leading whitespace):
+  // For example this doc-comment (dots representing leading whitespace):
   //
   //   ..../**
   //   .....* Some description here
@@ -371,14 +383,30 @@ export default class ExpressionFormatter {
   //     '.* and here too',
   //     '.*/' ]
   //
+  // However, a normal comment (non-doc-comment) like this:
+  //
+  //   ..../*
+  //   ....Some description here
+  //   ....*/
+  //
+  // gets broken to this array (no leading spaces):
+  //
+  //   [ '/*',
+  //     'Some description here',
+  //     '*/' ]
+  //
   private splitBlockComment(comment: string): string[] {
-    return comment.split(/\n/).map(line => {
-      if (/^\s*\*/.test(line)) {
-        return ' ' + line.replace(/^\s*/, '');
-      } else {
-        return line.replace(/^\s*/, '');
-      }
-    });
+    if (this.isDocComment(comment)) {
+      return comment.split(/\n/).map(line => {
+        if (/^\s*\*/.test(line)) {
+          return ' ' + line.replace(/^\s*/, '');
+        } else {
+          return line;
+        }
+      });
+    } else {
+      return comment.split(/\n/).map(line => line.replace(/^\s*/, ''));
+    }
   }
 
   private formatSubExpression(nodes: AstNode[]): Layout {


Test patch:
diff --git a/test/features/comments.ts b/test/features/comments.ts
index a5064290..47b51ac9 100644
--- a/test/features/comments.ts
+++ b/test/features/comments.ts
@@ -211,6 +211,23 @@ export default function supportsComments(format: FormatFn, opts: CommentsConfig
     `);
   });
 
+  // Regression test for #558
+  it('indents multiline block comment that is not a doc-comment', () => {
+    const result = format(dedent`
+      SELECT 1
+      /*
+      comment line
+      */
+    `);
+    expect(result).toBe(dedent`
+      SELECT
+        1
+        /*
+        comment line
+        */
+    `);
+  });
+
   it('formats comments between qualified.names (after dot)', () => {
     const result = format(`
       SELECT foo. /* com1 */ bar, foo. /* com2 */ *;