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

Open
#226 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
56/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
csharp, sql
Domain
tooling

Research direction

Start by reading SqlScriptGeneratorOptions and the Sql170ScriptGenerator behavior for the existing Multiline* options, then compare how CASE expressions are emitted. Trace the relevant generator tests if present and verify that the default preserves inline output while MultilineCaseExpression places WHEN and ELSE branches on separate indented lines.

Written by the indexing model from the issue text.

Description

Is your feature request related to a problem? Please describe

When Sql170ScriptGenerator regenerates a script, a CASE expression is always emitted inline — every
WHEN/THEN/ELSE is joined onto one logical line. For multi-branch CASE expressions this makes the
output substantially harder to read than the input, and where a WHEN predicate has multiple conjuncts
the hanging indent compounds: each AND is indented to the width of the preceding WHEN, and the next
WHEN is then appended after that indented continuation, producing a staircase.

Input (longest line 81 chars):

SELECT CASE
           WHEN o.status = 'P' AND o.paid_amount >= o.total_amount THEN 'settled'
           WHEN o.status = 'P' AND o.paid_amount > 0 THEN 'part-paid'
           WHEN o.status = 'C' THEN 'cancelled'
           ELSE 'open'
       END AS settlement_state,
       CASE WHEN o.due_date < SYSUTCDATETIME() THEN 1 ELSE 0 END AS is_overdue
FROM orders AS o
WHERE o.tenant_id = 42;

Output with MultilineSelectElementsList = true, IndentationSize = 4 (longest line 185 chars):

SELECT CASE WHEN o.status = 'P'
                 AND o.paid_amount >= o.total_amount THEN 'settled' WHEN o.status = 'P'
                                                                         AND o.paid_amount > 0 THEN 'part-paid' WHEN o.status = 'C' THEN 'cancelled' ELSE 'open' END AS settlement_state,
       CASE WHEN o.due_date < SYSUTCDATETIME() THEN 1 ELSE 0 END AS is_overdue
FROM   orders AS o
WHERE  o.tenant_id = 42;

An 81-character input becomes a 185-character line. There is currently no option that affects this:
of the 46 properties on SqlScriptGeneratorOptions, none matches Case or When, and none provides a
maximum line width. AlignClauseBodies, ClauseBodyAlignment and the Multiline* options do not reach
inside a CASE.

Describe the solution you'd like

A MultilineCaseExpression option (default false, preserving current output) that places each WHEN
branch and the ELSE on its own line, indented one level from the CASE:

SELECT CASE
           WHEN o.status = 'P' AND o.paid_amount >= o.total_amount THEN 'settled'
           WHEN o.status = 'P' AND o.paid_amount > 0 THEN 'part-paid'
           WHEN o.status = 'C' THEN 'cancelled'
           ELSE 'open'
       END AS settlement_state,

This fits the existing naming and behaviour of the Multiline* family —
MultilineSelectElementsList, MultilineWherePredicatesList, MultilineInValuesList,
MultilineViewColumnsList, MultilineSetClauseItems, MultilineInsertTargetsList,
MultilineInsertSourcesList, MultilineProcedureParametersList — so it should need no new concepts.

A single-branch CASE staying inline when the option is off (as
CASE WHEN … THEN 1 ELSE 0 END above) seems the right default; whether a one-branch CASE should also
break under the option is a judgement call I have no strong view on.

Describe alternatives you've considered
  • Existing optionsAlignClauseBodies, ClauseBodyAlignment = Indented,
    MultilineSelectElementsList, and reduced IndentationSize. None affects the interior of a CASE;
    the best combination I found did not shorten these lines.
  • A general maximum-line-width option. More broadly useful, but a much larger change and a different
    model from the current per-construct toggles, so I have not requested it here.
  • Post-processing the generated text. Re-wrapping CASE with a regex outside the generator defeats
    the point of using an AST-based formatter and is not reliable in the presence of string literals.
Additional context
  • Microsoft.SqlServer.TransactSql.ScriptDom 180.78.1 (assembly 18.0.0.0), net8.0; behaviour is the
    same on Sql160ScriptGenerator, Sql170ScriptGenerator and Sql180ScriptGenerator.
  • Related open requests of the same shape, both asking for a construct to be broken across lines:
    #189 (INSERT columns) and #21 (stored-procedure parameters).
  • Happy to test a change against a large real-world T-SQL corpus if that is useful.
Dominant language
GAP
Stars
277
Forks
43
Avg merge
6d 17h
Merged PRs (30d)
3

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/SqlScriptDOM

All issues in microsoft/SqlScriptDOM

Similar issues

More DevTools issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.