Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature BlankLinesAroundNestedMultilineExpressions #1587

Merged
merged 3 commits into from
Apr 5, 2021
Merged
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
43 changes: 43 additions & 0 deletions docs/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ fsharp_alternative_long_member_definitions=false
fsharp_multi_line_lambda_closing_newline=false
fsharp_disable_elmish_syntax=false
fsharp_keep_indent_in_branch=false
fsharp_blank_lines_around_nested_multiline_expressions=true
fsharp_strict_mode=false
```

Expand Down Expand Up @@ -1060,6 +1061,48 @@ let main argv =
0
```

### fsharp_blank_lines_around_nested_multiline_expressions

Surround **nested** multi-line expressions with blank lines.
Existing blank lines are always preserved (via trivia).
Top level expressions will always follow the [2020 blank lines revision](https://github.com/fsprojects/fantomas/blob/master/docs/FormattingConventions.md#2020-revision) principle.
Default = true.

`defaultConfig`

```fsharp
let topLevelFunction () =
printfn "Something to print"

try
nothing ()
with
| ex ->
splash ()
()

let secondTopLevelFunction () =
// ...
()
```

`{ defaultConfig with BlankLinesAroundNestedMultilineExpressions = false }`

```fsharp
let topLevelFunction () =
printfn "Something to print"
try
nothing ()
with
| ex ->
splash ()
()

let secondTopLevelFunction () =
// ...
()
```

### fsharp_strict_mode

If being set, pretty printing is only done via ASTs. Compiler directives, inline comments and block comments will be ignored.
Expand Down
245 changes: 245 additions & 0 deletions src/Fantomas.Tests/BlankLinesAroundNestedMultilineExpressions.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
module Fantomas.Tests.BlankLinesAroundNestedMultilineExpressions

open NUnit.Framework
open FsUnit
open Fantomas.Tests.TestHelper

let config =
{ config with
BlankLinesAroundNestedMultilineExpressions = false }

[<Test>]
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"
()
"""

[<Test>]
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"
()
"""

[<Test>]
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 ()
()
"""

[<Test>]
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
"""

[<Test>]
let ``disable blank lines inside type constructor`` () =
formatSourceString
false
"""
type MNIST(path:string, ?urls:seq<string>, ?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<string>,
?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
"""

[<Test>]
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
}
"""
1 change: 1 addition & 0 deletions src/Fantomas.Tests/Fantomas.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
<Compile Include="DotIndexedSetTests.fs" />
<Compile Include="MultilineFunctionApplicationsInConditionExpressionsTests.fs" />
<Compile Include="KeepIndentInBranch.fs" />
<Compile Include="BlankLinesAroundNestedMultilineExpressions.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Fantomas.Extras\Fantomas.Extras.fsproj" />
Expand Down
48 changes: 48 additions & 0 deletions src/Fantomas.Tests/KeepIndentInBranch.fs
Original file line number Diff line number Diff line change
Expand Up @@ -859,3 +859,51 @@ module Foo =
Thing.execute bar baz (thing, instructions)
0
"""

[<Test>]
let ``in combination with NewlinesAroundInnerMultilineExpressions`` () =
formatSourceString
false
"""
module Foo =
let main (args: _) =
let thing1 = ()
printfn ""
if hasInstructions () then
printfn ""
2
else
log.LogInformation("")
match Something.foo args with
| DryRunMode.Dry ->
printfn ""
0
| DryRunMode.Wet ->
Thing.execute bar baz (thing, instructions)
0
"""
{ config with
BlankLinesAroundNestedMultilineExpressions = false }
|> prepend newline
|> should
equal
"""
module Foo =
let main (args: _) =
let thing1 = ()
printfn ""
if hasInstructions () then
printfn ""
2
else

log.LogInformation("")
match Something.foo args with
| DryRunMode.Dry ->
printfn ""
0
| DryRunMode.Wet ->

Thing.execute bar baz (thing, instructions)
0
"""
Loading