Skip to content

Add MultilineCaseExpression script generator option - #233

Open
trg-alasdair wants to merge 2 commits into
microsoft:mainfrom
TRG-Arts:multiline-case-expression
Open

trg-alasdair wants to merge 2 commits into
microsoft:mainfrom
TRG-Arts:multiline-case-expression

Conversation

@trg-alasdair

Copy link
Copy Markdown

Description

Fixes #226.

SqlScriptGenerator always writes a CASE expression on one line. A multi-branch CASE becomes one long line, and when a WHEN predicate contains AND, MultilineWherePredicatesList breaks it and appends the next WHEN after the continuation, which produces the staircase shown in the issue.

This adds MultilineCaseExpression (default false). When it is true, each WHEN clause and the ELSE are written on their own line, indented one level from CASE, and END is written on its own line aligned with CASE:

SELECT CASE
           WHEN o.status = 'P'
                AND o.paid_amount >= o.total_amount THEN 'settled'
           WHEN o.status = 'C' THEN 'cancelled'
           ELSE 'open'
       END AS settlement_state
  • It applies to simple and searched CASE, wherever the expression appears. A nested CASE aligns with its own CASE keyword.
  • The input expression of a simple CASE stays on the CASE line.
  • WHEN predicates keep their existing behavior under MultilineWherePredicatesList. Because each WHEN now starts its own line, the continuations no longer compound.
  • A trailing single-line comment on a branch stays on that branch's line. Today it moves to the end of the statement, because the deferred comment is written out at the next line break.
  • With the option off, the output is unchanged: the alignment point is pushed only when the option is on.

The setting is in SqlScriptGeneratorOptions.xml in a new "CASE expressions" group, and the generator change is confined to SqlScriptGeneratorVisitor.CaseExpression.cs.

This change was written with the help of an AI coding assistant, and I reviewed and tested it.

Code Changes

A CASE expression is always generated on one line, so a multi-branch
CASE becomes a single long line, and a WHEN predicate joined by AND
breaks under MultilineWherePredicatesList with the next WHEN appended
after its continuation, producing a staircase.

MultilineCaseExpression (default false) writes each WHEN clause and the
ELSE on its own line, indented one level from CASE, and END on its own
line aligned with CASE. It applies to simple and searched CASE. The
alignment point is pushed only when the option is on, so the default
output is unchanged. A trailing single-line comment on a branch now
stays on that branch's line, because the deferred comment is flushed at
the next line break.
@trg-alasdair

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree [company="{TRG Arts}"]

@trg-alasdair

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="TRG Arts"

@allancascante

Copy link
Copy Markdown
Contributor

With MultilineCaseExpression and PreserveComments enabled, a comment before a WHEN causes that branch to lose its indentation. For example:

SELECT CASE
           -- branch explanation
       WHEN @v = 1 THEN 'a'
           WHEN @v = 2 THEN 'b'
       END; 

I reproduced this with both line and block comments, for simple and searched CASE. The comment is emitted during GenerateFragmentIfNotNull(when); its newline restores the outer CASE alignment point, so the following WHEN starts at the wrong column. Could you preserve the branch indentation across comment-generated newlines and add regression tests for these cases?

With MultilineCaseExpression and PreserveComments enabled:

- A comment before a WHEN ended in a newline that restored the CASE
  column, so the WHEN lost its branch indentation. Each WHEN now pushes
  its own alignment point.
- A comment before ELSE was written after the ELSE keyword, which
  dropped the else expression from the output.
- A comment before END was dropped.

Comments before ELSE and END are now written on their own lines at the
branch column. Regression tests cover line and block comments for
simple and searched CASE.

EmitPendingCommentsOnOwnLines is the third scan of the trivia run, after
EmitPendingGapComments and EmitCommentsUntilNextNonTriviaToken. It is
a separate method so that the existing ones stay unchanged. The scans
could be merged by adding an optional startLine parameter to
EmitPendingGapComments.
@trg-alasdair
trg-alasdair requested a review from mhyon as a code owner October 21, 2026 21:50
@trg-alasdair

Copy link
Copy Markdown
Author

Should be fixed now, also caught another situation where comments case the layout to break.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a Multiline option for CASE expressions (WHEN/THEN/ELSE on their own lines)

2 participants