Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [Unreleased]

### Added

- New `fsharp_keep_single_case_union_multiline` setting to keep single-case discriminated union declarations on a separate line instead of collapsing them onto the type name. [#3377](https://github.com/fsprojects/fantomas/pull/3377)

## [8.0.0-alpha-012] - 2026-04-16

### Changed
Expand Down
25 changes: 24 additions & 1 deletion docs/docs/end-users/Configuration.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ printfn
(*** include-output ***)

formatCode
"""
"""
type MyDU = Short of int
"""
"""
Expand All @@ -1029,6 +1029,29 @@ fsharp_bar_before_discriminated_union_declaration = true

(*** include-output ***)

(**
<fantomas-setting green></fantomas-setting>
### fsharp_keep_single_case_union_multiline
<copy-to-clipboard text="fsharp_keep_single_case_union_multiline = true"></copy-to-clipboard>

Keep the declaration of a single-case discriminated union on a separate line, instead of collapsing it onto the same line as the type name when it is short enough to fit.
*)

(*** hide ***)
printfn
$"# Default\n{toEditorConfigName (nameof FormatConfig.Default.KeepSingleCaseUnionMultiline)} = {FormatConfig.Default.KeepSingleCaseUnionMultiline.ToString().ToLower()}"
(*** include-output ***)

formatCode
"""
type MyDU = Short of int
"""
"""
fsharp_keep_single_case_union_multiline = true
"""

(*** include-output ***)

(**
## Other

Expand Down
1 change: 1 addition & 0 deletions src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<Compile Include="BlankLinesAroundNestedMultilineExpressions.fs" />
<Compile Include="ColMultilineItemTests.fs" />
<Compile Include="BarBeforeDiscriminatedUnionDeclarationTests.fs" />
<Compile Include="KeepSingleCaseUnionMultilineTests.fs" />
<Compile Include="SpaceBeforeColonTests.fs" />
<Compile Include="LibraryOnlySynExprTests.fs" />
<Compile Include="IndexSyntaxTests.fs" />
Expand Down
141 changes: 141 additions & 0 deletions src/Fantomas.Core.Tests/KeepSingleCaseUnionMultilineTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
module Fantomas.Core.Tests.KeepSingleCaseUnionMultilineTests

open NUnit.Framework
open FsUnit
open Fantomas.Core.Tests.TestHelpers

let config =
{ config with
KeepSingleCaseUnionMultiline = true }

[<Test>]
let ``single case union with field is kept multiline`` () =
formatSourceString
"""
type MyDU = Short of int
"""
config
|> prepend newline
|> should
equal
"""
type MyDU =
| Short of int
"""

[<Test>]
let ``single case union without field is kept multiline`` () =
formatSourceString
"""
type A = | A
"""
config
|> prepend newline
|> should
equal
"""
type A =
| A
"""

[<Test>]
let ``single case union with access modifier is kept multiline`` () =
formatSourceString
"""
type Foo = private Foo of int
"""
config
|> prepend newline
|> should
equal
"""
type Foo =
private | Foo of int
"""

[<Test>]
let ``result is idempotent`` () =
let source =
"""
type MyDU =
| Short of int
"""

formatSourceString source config
|> fun formatted -> formatSourceString formatted config
|> prepend newline
|> should
equal
"""
type MyDU =
| Short of int
"""

[<Test>]
let ``combines with bar before discriminated union declaration`` () =
formatSourceString
"""
type MyDU = Short of int
"""
{ config with
BarBeforeDiscriminatedUnionDeclaration = true }
|> prepend newline
|> should
equal
"""
type MyDU =
| Short of int
"""

[<Test>]
let ``multi case union is unaffected`` () =
formatSourceString
"""
type MyDU =
| Short of int
| Long of string
"""
config
|> prepend newline
|> should
equal
"""
type MyDU =
| Short of int
| Long of string
"""

[<Test>]
let ``single case union with member stays multiline`` () =
formatSourceString
"""
type MyDU =
| Short of int
member this.Value = 1
"""
config
|> prepend newline
|> should
equal
"""
type MyDU =
| Short of int

member this.Value = 1
"""

[<Test>]
let ``single case union in signature file is kept multiline`` () =
formatSignatureString
"""namespace meh

type Foo = Bar of int
"""
config
|> should
equal
"""namespace meh

type Foo =
| Bar of int
"""
11 changes: 7 additions & 4 deletions src/Fantomas.Core/CodePrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3549,10 +3549,13 @@ let genTypeDefn (td: TypeDefn) =
genSingleTextNode vis +> onlyIfNot singleCase.XmlDoc.IsNone sepNln)
+> genUnionCase hasVerticalBar singleCase

expressionFitsOnRestOfLine
(sepSpace +> genCase hasVerticalBar)
(indentSepNlnUnindent (genCase true))
ctx
if ctx.Config.KeepSingleCaseUnionMultiline then
indentSepNlnUnindent (genCase true) ctx
else
expressionFitsOnRestOfLine
(sepSpace +> genCase hasVerticalBar)
(indentSepNlnUnindent (genCase true))
ctx
| xs ->
indentSepNlnUnindent
(opt sepNln node.Accessibility genSingleTextNode
Expand Down
6 changes: 6 additions & 0 deletions src/Fantomas.Core/FormatConfig.fs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ type FormatConfig =
[<DisplayName("Add a bar before Discriminated Union declarations")>]
BarBeforeDiscriminatedUnionDeclaration: bool

[<Category("Convention")>]
[<DisplayName("Keep single-case Discriminated Union declarations multiline")>]
[<Description("Do not collapse a short single-case union onto the same line as the type name.")>]
KeepSingleCaseUnionMultiline: bool

[<Category("Convention")>]
[<DisplayName("How to format bracket expressions (arrays, objects, etc.) that span multiple lines")>]
[<Description("Possible options include cramped (default), aligned, and stroustrup")>]
Expand Down Expand Up @@ -273,6 +278,7 @@ type FormatConfig =
ExperimentalKeepIndentInBranch = false
BlankLinesAroundNestedMultilineExpressions = true
BarBeforeDiscriminatedUnionDeclaration = false
KeepSingleCaseUnionMultiline = false
MultilineBracketStyle = Aligned
KeepMaxNumberOfBlankLines = 100
NewlineBeforeMultilineComputationExpression = true
Expand Down