diff --git a/src/Fantomas.Tests/Fantomas.Tests.fsproj b/src/Fantomas.Tests/Fantomas.Tests.fsproj
index c6242bc4ff..0fab0d3623 100644
--- a/src/Fantomas.Tests/Fantomas.Tests.fsproj
+++ b/src/Fantomas.Tests/Fantomas.Tests.fsproj
@@ -85,6 +85,7 @@
+
diff --git a/src/Fantomas.Tests/NewlinesAroundInnerMultilineExpressions.fs b/src/Fantomas.Tests/NewlinesAroundInnerMultilineExpressions.fs
new file mode 100644
index 0000000000..3a4828dfc2
--- /dev/null
+++ b/src/Fantomas.Tests/NewlinesAroundInnerMultilineExpressions.fs
@@ -0,0 +1,245 @@
+module Fantomas.Tests.NewlinesAroundInnerMultilineExpressions
+
+open NUnit.Framework
+open FsUnit
+open Fantomas.Tests.TestHelper
+
+let config =
+ { config with
+ NewlinesAroundInnerMultilineExpressions = false }
+
+[]
+let ``basic behavior`` () =
+ formatSourceString
+ false
+ """
+let topLevelFunction () =
+ let innerValue = 23
+ let innerMultilineFunction () =
+ // some comment
+ printfn "foo"
+ ()
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+let topLevelFunction () =
+ let innerValue = 23
+ let innerMultilineFunction () =
+ // some comment
+ printfn "foo"
+ ()
+"""
+
+[]
+let ``existing newlines are preserved`` () =
+ formatSourceString
+ false
+ """
+let topLevelFunction () =
+ let innerValue = 23
+
+ let innerMultilineFunction () =
+ // some comment
+ printfn "foo"
+ ()
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+let topLevelFunction () =
+ let innerValue = 23
+
+ let innerMultilineFunction () =
+ // some comment
+ printfn "foo"
+ ()
+"""
+
+[]
+let ``with sequential expressions`` () =
+ formatSourceString
+ false
+ """
+let topLevelFunction () =
+ printfn "Something to print"
+ try
+ nothing ()
+ with
+ | ex ->
+ splash ()
+ ()
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+let topLevelFunction () =
+ printfn "Something to print"
+ try
+ nothing ()
+ with ex -> splash ()
+ ()
+"""
+
+[]
+let ``disable blank lines around multiline expressions inside let binding, 1370`` () =
+ formatSourceString
+ false
+ """
+let emit nodes =
+ let an = AssemblyName("a")
+ let ab =
+ AppDomain.CurrentDomain.DefineDynamicAssembly(
+ an,
+ AssemblyBuilderAccess.RunAndCollect
+ )
+ let mb = ab.DefineDynamicModule("a")
+ let tb = mb.DefineType("Program")
+ nodes
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+let emit nodes =
+ let an = AssemblyName("a")
+ let ab =
+ AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndCollect)
+ let mb = ab.DefineDynamicModule("a")
+ let tb = mb.DefineType("Program")
+ nodes
+"""
+
+[]
+let ``disable blank lines inside type constructor`` () =
+ formatSourceString
+ false
+ """
+type MNIST(path:string, ?urls:seq, ?train:bool, ?transform:Tensor->Tensor, ?targetTransform:Tensor->Tensor) =
+ inherit Dataset()
+ let path = Path.Combine(path, "mnist") |> Path.GetFullPath
+ let train = defaultArg train true
+ let transform = defaultArg transform (fun t -> (t - 0.1307) / 0.3081)
+ let targetTransform = defaultArg targetTransform id
+ let urls = List.ofSeq <| defaultArg urls (Seq.ofList
+ ["http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz";
+ "http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz";
+ "http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz";
+ "http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz"])
+ let files = [for url in urls do Path.Combine(path, Path.GetFileName(url))]
+ let filesProcessed = [for file in files do Path.ChangeExtension(file, ".tensor")]
+ let data, target =
+ Directory.CreateDirectory(path) |> ignore
+ let mutable data = dsharp.zero()
+ let mutable target = dsharp.zero()
+ if train then
+ if not (File.Exists(files.[0])) then download urls.[0] files.[0]
+ if not (File.Exists(files.[1])) then download urls.[1] files.[1]
+ if File.Exists(filesProcessed.[0]) then data <- dsharp.load(filesProcessed.[0]) else data <- MNIST.LoadMNISTImages(files.[0]); dsharp.save(data, filesProcessed.[0])
+ if File.Exists(filesProcessed.[1]) then target <- dsharp.load(filesProcessed.[1]) else target <- MNIST.LoadMNISTLabels(files.[1]); dsharp.save(target, filesProcessed.[1])
+ else
+ if not (File.Exists(files.[2])) then download urls.[2] files.[2]
+ if not (File.Exists(files.[3])) then download urls.[3] files.[3]
+ if File.Exists(filesProcessed.[2]) then data <- dsharp.load(filesProcessed.[2]) else data <- MNIST.LoadMNISTImages(files.[2]); dsharp.save(data, filesProcessed.[2])
+ if File.Exists(filesProcessed.[3]) then target <- dsharp.load(filesProcessed.[3]) else target <- MNIST.LoadMNISTLabels(files.[3]); dsharp.save(target, filesProcessed.[3])
+ data, target
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+type MNIST
+ (
+ path: string,
+ ?urls: seq,
+ ?train: bool,
+ ?transform: Tensor -> Tensor,
+ ?targetTransform: Tensor -> Tensor
+ ) =
+ inherit Dataset()
+ let path =
+ Path.Combine(path, "mnist") |> Path.GetFullPath
+ let train = defaultArg train true
+ let transform =
+ defaultArg transform (fun t -> (t - 0.1307) / 0.3081)
+ let targetTransform = defaultArg targetTransform id
+ let urls =
+ List.ofSeq
+ <| defaultArg
+ urls
+ (Seq.ofList [ "http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz"
+ "http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz"
+ "http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz"
+ "http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz" ])
+ let files =
+ [ for url in urls do
+ Path.Combine(path, Path.GetFileName(url)) ]
+ let filesProcessed =
+ [ for file in files do
+ Path.ChangeExtension(file, ".tensor") ]
+ let data, target =
+ Directory.CreateDirectory(path) |> ignore
+ let mutable data = dsharp.zero ()
+ let mutable target = dsharp.zero ()
+ if train then
+ if not (File.Exists(files.[0])) then
+ download urls.[0] files.[0]
+ if not (File.Exists(files.[1])) then
+ download urls.[1] files.[1]
+ if File.Exists(filesProcessed.[0]) then
+ data <- dsharp.load (filesProcessed.[0])
+ else
+ data <- MNIST.LoadMNISTImages(files.[0])
+ dsharp.save (data, filesProcessed.[0])
+ if File.Exists(filesProcessed.[1]) then
+ target <- dsharp.load (filesProcessed.[1])
+ else
+ target <- MNIST.LoadMNISTLabels(files.[1])
+ dsharp.save (target, filesProcessed.[1])
+ else
+ if not (File.Exists(files.[2])) then
+ download urls.[2] files.[2]
+ if not (File.Exists(files.[3])) then
+ download urls.[3] files.[3]
+ if File.Exists(filesProcessed.[2]) then
+ data <- dsharp.load (filesProcessed.[2])
+ else
+ data <- MNIST.LoadMNISTImages(files.[2])
+ dsharp.save (data, filesProcessed.[2])
+ if File.Exists(filesProcessed.[3]) then
+ target <- dsharp.load (filesProcessed.[3])
+ else
+ target <- MNIST.LoadMNISTLabels(files.[3])
+ dsharp.save (target, filesProcessed.[3])
+ data, target
+"""
+
+[]
+let ``computation expressions`` () =
+ formatSourceString
+ false
+ """
+let comp =
+ eventually { for x in 1 .. 2 do
+ printfn " x = %d" x
+ return 3 + 4 }"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+let comp =
+ eventually {
+ for x in 1 .. 2 do
+ printfn " x = %d" x
+ return 3 + 4
+ }
+"""
diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs
index d6cdc43c85..e291787f8d 100644
--- a/src/Fantomas/CodePrinter.fs
+++ b/src/Fantomas/CodePrinter.fs
@@ -1461,7 +1461,7 @@ and genExpr astContext synExpr ctx =
let r = getRangeOfCompExprStatement ces
let sepNln = getSepNln ces r
ColMultilineItem(expr, sepNln, r))
- |> colWithNlnWhenItemIsMultiline
+ |> colWithNlnWhenItemIsMultilineBasedOnSetting
| ArrayOrListOfSeqExpr (isArray, e) as alNode ->
let astContext = { astContext with IsNakedRange = true }
@@ -2077,7 +2077,8 @@ and genExpr astContext synExpr ctx =
) ]
let items = letBindings bs @ synExpr e
- atCurrentColumn (colWithNlnWhenItemIsMultiline items) ctx
+
+ atCurrentColumn (colWithNlnWhenItemIsMultilineBasedOnSetting items) ctx
// Could customize a bit if e is single line
| TryWith (e, cs) ->
@@ -2154,7 +2155,7 @@ and genExpr astContext synExpr ctx =
ColMultilineItem(expr, sepNln, r))
- atCurrentColumn (colWithNlnWhenItemIsMultiline items)
+ atCurrentColumn (colWithNlnWhenItemIsMultilineBasedOnSetting items)
// A generalization of IfThenElse
| ElIf ((e1, e2, _, _, _) :: es, enOpt) ->
@@ -4466,7 +4467,7 @@ and genMemberDefnList astContext nodes =
:: (collectItems rest)
collectItems nodes
- |> colWithNlnWhenItemIsMultiline
+ |> colWithNlnWhenItemIsMultilineBasedOnSetting
and genMemberDefn astContext node =
match node with
diff --git a/src/Fantomas/Context.fs b/src/Fantomas/Context.fs
index 3e4d3dbf0d..39dfb5a99a 100644
--- a/src/Fantomas/Context.fs
+++ b/src/Fantomas/Context.fs
@@ -1507,6 +1507,12 @@ let internal colWithNlnWhenItemIsMultiline (items: ColMultilineItem list) =
impl items
+let internal colWithNlnWhenItemIsMultilineBasedOnSetting (items: ColMultilineItem list) (ctx: Context) =
+ if ctx.Config.NewlinesAroundInnerMultilineExpressions then
+ colWithNlnWhenItemIsMultiline items ctx
+ else
+ col sepNln items (fun (ColMultilineItem (expr, _, _)) -> expr) ctx
+
let internal genTriviaBeforeClausePipe (rangeOfClause: Range) ctx =
(Map.tryFindOrEmptyList BAR ctx.TriviaTokenNodes)
|> List.tryFind
diff --git a/src/Fantomas/FormatConfig.fs b/src/Fantomas/FormatConfig.fs
index 822f71868e..1d41944ee4 100644
--- a/src/Fantomas/FormatConfig.fs
+++ b/src/Fantomas/FormatConfig.fs
@@ -94,6 +94,7 @@ type FormatConfig =
DisableElmishSyntax: bool
EndOfLine: EndOfLineStyle
KeepIndentInBranch: bool
+ NewlinesAroundInnerMultilineExpressions: bool
/// Pretty printing based on ASTs only
StrictMode: bool }
@@ -134,4 +135,5 @@ type FormatConfig =
DisableElmishSyntax = false
EndOfLine = EndOfLineStyle.FromEnvironment
KeepIndentInBranch = false
+ NewlinesAroundInnerMultilineExpressions = true
StrictMode = false }