diff --git a/README.md b/README.md index 67c463daba..bc2d248814 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ We use [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) to collect d This project aims at formatting F# source files based on a given configuration. Fantomas will ensure correct indentation and consistent spacing between elements in the source files. We assume that the source files are *parsable by F# compiler* before feeding into the tool. -Fantomas follows the formatting guideline being described in [A comprehensive guide to F# Formatting Conventions](docs/FormattingConventions.md). +Fantomas follows two F# style guides: the [F# code formatting guidelines](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting) from Microsoft by default and the [G-Research F# code formatting guidelines](https://github.com/G-Research/fsharp-formatting-conventions) via various [settings](https://github.com/G-Research/fsharp-formatting-conventions/blob/master/.editorconfig). ## Use cases The project is developed with the following use cases in mind: diff --git a/docs/Documentation.md b/docs/Documentation.md index 2caf8a5941..8515682e28 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -329,22 +329,22 @@ type Person() = ### fsharp_space_before_colon -Add a space before `:`. +Add a space before `:`. Please note that not every `:` is controlled by this setting. Default = false. `defaultConfig` ```fsharp type Point = { x: int; y: int } - -let update (msg: Msg) (model: Model): Model = model +let myValue: int = 42 // See https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting#right-pad-value-and-function-argument-type-annotations +let update (msg: Msg) (model: Model) : Model = model // See https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting#surround-return-type-annotations-with-white-space ``` `{ defaultConfig with SpaceBeforeColon = true }` ```fsharp type Point = { x : int; y : int } - +let myValue : int = 42 let update (msg : Msg) (model : Model) : Model = model ``` diff --git a/src/Fantomas.Tests/ClassTests.fs b/src/Fantomas.Tests/ClassTests.fs index 46c8bb6737..664c310e83 100644 --- a/src/Fantomas.Tests/ClassTests.fs +++ b/src/Fantomas.Tests/ClassTests.fs @@ -265,7 +265,7 @@ and File(filename: string, containingFolder: Folder) = """ type Folder(pathIn: string) = let path = pathIn - let filenameArray : string array = System.IO.Directory.GetFiles(path) + let filenameArray: string array = System.IO.Directory.GetFiles(path) member this.FileArray = Array.map (fun elem -> new File(elem, this)) filenameArray and File(filename: string, containingFolder: Folder) = diff --git a/src/Fantomas.Tests/CommentTests.fs b/src/Fantomas.Tests/CommentTests.fs index dd3cd4b592..20e85bfd8f 100644 --- a/src/Fantomas.Tests/CommentTests.fs +++ b/src/Fantomas.Tests/CommentTests.fs @@ -51,7 +51,7 @@ let print_30_permut() = let print_30_permut () = /// declare and initialize - let permutation : int array = + let permutation: int array = Array.init n (fun i -> @@ -80,7 +80,7 @@ let print_30_permut() = let print_30_permut () = /// declare and initialize - let permutation : int array = + let permutation: int array = Array.init n (fun (i, j) -> diff --git a/src/Fantomas.Tests/CompilerDirectivesTests.fs b/src/Fantomas.Tests/CompilerDirectivesTests.fs index 941a5d7113..76511191a4 100644 --- a/src/Fantomas.Tests/CompilerDirectivesTests.fs +++ b/src/Fantomas.Tests/CompilerDirectivesTests.fs @@ -936,11 +936,11 @@ let ``module with nested directives`` () = #else [] #endif - let ReactDom : IReactDom = jsNative + let ReactDom: IReactDom = jsNative #if !FABLE_REPL_LIB [] - let ReactDomServer : IReactDomServer = jsNative + let ReactDomServer: IReactDomServer = jsNative #endif """ @@ -969,11 +969,11 @@ let ``module with nested directives, no defines`` () = #else [] #endif - let ReactDom : IReactDom = jsNative + let ReactDom: IReactDom = jsNative #if !FABLE_REPL_LIB [] - let ReactDomServer : IReactDomServer = jsNative + let ReactDomServer: IReactDomServer = jsNative #endif """ @@ -1002,7 +1002,7 @@ let ``module with nested directives, FABLE_REPL_LIB`` () = #else #endif - let ReactDom : IReactDom = jsNative + let ReactDom: IReactDom = jsNative #if !FABLE_REPL_LIB diff --git a/src/Fantomas.Tests/ComputationExpressionTests.fs b/src/Fantomas.Tests/ComputationExpressionTests.fs index 4c4b668102..590b0e88be 100644 --- a/src/Fantomas.Tests/ComputationExpressionTests.fs +++ b/src/Fantomas.Tests/ComputationExpressionTests.fs @@ -1932,7 +1932,7 @@ let create: Highlighter = |> should equal """ -let create : Highlighter = +let create: Highlighter = fun searchTerm -> let regex = searchTerm |> SearchTerm.toRegex @@ -2024,7 +2024,7 @@ let result = ResultBuilder() let run r1 r2 r3 = // And here is our applicative! - let res1 : Result = + let res1: Result = result { let! a = r1 and! b = r2 diff --git a/src/Fantomas.Tests/ControlStructureTests.fs b/src/Fantomas.Tests/ControlStructureTests.fs index 269cdd5a0f..13b352f09c 100644 --- a/src/Fantomas.Tests/ControlStructureTests.fs +++ b/src/Fantomas.Tests/ControlStructureTests.fs @@ -734,7 +734,7 @@ let foldi (folder: 'State -> int -> 'T -> 'State) (state: 'State) (array: 'T []) let folder = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt folder - let mutable state : 'State = state + let mutable state: 'State = state let len = array.Length for i = 0 to len - 1 do diff --git a/src/Fantomas.Tests/Fantomas.Tests.fsproj b/src/Fantomas.Tests/Fantomas.Tests.fsproj index b184c7fad9..fba693f909 100644 --- a/src/Fantomas.Tests/Fantomas.Tests.fsproj +++ b/src/Fantomas.Tests/Fantomas.Tests.fsproj @@ -88,6 +88,7 @@ + diff --git a/src/Fantomas.Tests/FunctionDefinitionTests.fs b/src/Fantomas.Tests/FunctionDefinitionTests.fs index 605050d4c3..a2e590df65 100644 --- a/src/Fantomas.Tests/FunctionDefinitionTests.fs +++ b/src/Fantomas.Tests/FunctionDefinitionTests.fs @@ -154,7 +154,7 @@ let ``=foo hoo`` () = () let ``-foo hoo`` () = () let ``!foo hoo`` () : unit = () let ``@foo hoo`` = () -let ``$foo hoo`` : unit = () +let ``$foo hoo``: unit = () """ [] @@ -178,8 +178,8 @@ let ``let bindings with return types`` () = equal """ let divide x y = - let stream : System.IO.FileStream = System.IO.File.Create("test.txt") - let writer : System.IO.StreamWriter = new System.IO.StreamWriter(stream) + let stream: System.IO.FileStream = System.IO.File.Create("test.txt") + let writer: System.IO.StreamWriter = new System.IO.StreamWriter(stream) try writer.WriteLine("test1") @@ -1027,12 +1027,12 @@ let e<'t> : int = 8 |> should equal """ -let a : int = 7 -let private b : string = "" -let internal c : int = 8 +let a: int = 7 +let private b: string = "" +let internal c: int = 8 [] -let d : int = 9 +let d: int = 9 let e<'t> : int = 8 """ @@ -1111,20 +1111,20 @@ let e<'t> : int = |> should equal """ -let a : int = +let a: int = // a comment makes things multiline 7 -let private b : string = +let private b: string = // a comment makes things multiline "" -let internal c : int = +let internal c: int = // a comment makes things multiline 8 [] -let d : int = +let d: int = // a comment makes things multiline 9 diff --git a/src/Fantomas.Tests/HashDirectiveTests.fs b/src/Fantomas.Tests/HashDirectiveTests.fs index d3f3651d74..2d6258992d 100644 --- a/src/Fantomas.Tests/HashDirectiveTests.fs +++ b/src/Fantomas.Tests/HashDirectiveTests.fs @@ -81,7 +81,7 @@ open ASTViewer.Shared open FantomasTools.Client.ASTViewer.Model open Thoth.Json -let decodeKeyValue : Decoder = fun _key jsonValue -> Ok jsonValue +let decodeKeyValue: Decoder = fun _key jsonValue -> Ok jsonValue #nowarn "40" """ diff --git a/src/Fantomas.Tests/IfThenElseTests.fs b/src/Fantomas.Tests/IfThenElseTests.fs index ba409b43e7..4c59b0cffe 100644 --- a/src/Fantomas.Tests/IfThenElseTests.fs +++ b/src/Fantomas.Tests/IfThenElseTests.fs @@ -2203,7 +2203,7 @@ type internal Foo2 private () = equal """ type internal Foo private () = - static member Bar : int option = + static member Bar: int option = if thing = 1 then printfn "hi" else if veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong @@ -2214,7 +2214,7 @@ type internal Foo private () = failwith "" type internal Foo2 private () = - static member Bar : int option = + static member Bar: int option = if thing = 1 then printfn "hi" else if veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong diff --git a/src/Fantomas.Tests/InterfaceTests.fs b/src/Fantomas.Tests/InterfaceTests.fs index 51ea2381f0..c2aa7c3211 100644 --- a/src/Fantomas.Tests/InterfaceTests.fs +++ b/src/Fantomas.Tests/InterfaceTests.fs @@ -586,7 +586,7 @@ and Bar = member this.Value = () [] - member this.ValueWithReturnType : unit = () + member this.ValueWithReturnType: unit = () [] member this.SomeFunction(a: int) = 4 + a diff --git a/src/Fantomas.Tests/LetBindingTests.fs b/src/Fantomas.Tests/LetBindingTests.fs index e8f11497dc..5ce31a030d 100644 --- a/src/Fantomas.Tests/LetBindingTests.fs +++ b/src/Fantomas.Tests/LetBindingTests.fs @@ -350,8 +350,8 @@ let someFun someReallyLoooooooooooooooongValue = [] let ``should keep space before :`` () = - formatSourceString false "let refl<'a> : Teq<'a, 'a> = Teq(id, id)" config - |> fun formatted -> formatSourceString false formatted config + formatSourceString false "let refl<'a> : Teq<'a, 'a> = Teq(id, id)" { config with SpaceBeforeColon = true } + |> fun formatted -> formatSourceString false formatted { config with SpaceBeforeColon = true } |> should equal "let refl<'a> : Teq<'a, 'a> = Teq(id, id) @@ -1620,7 +1620,7 @@ let myFun (a: decimal) b c : decimal = a + b + c |> should equal """ -let expensiveToCompute : int = 0 +let expensiveToCompute: int = 0 let myFun (a: decimal) b c : decimal = a + b + c """ @@ -1772,7 +1772,7 @@ type Viewport = |> should equal """ -let useGeolocation : unit +let useGeolocation: unit -> {| latitude: float longitude: float loading: bool diff --git a/src/Fantomas.Tests/ListTests.fs b/src/Fantomas.Tests/ListTests.fs index 20b72768d7..bef627f81b 100644 --- a/src/Fantomas.Tests/ListTests.fs +++ b/src/Fantomas.Tests/ListTests.fs @@ -1588,7 +1588,7 @@ let nestedList: obj list = [ |> should equal """ -let nestedList : obj list = +let nestedList: obj list = [ "11111111aaaaaaaaa" "22222222aaaaaaaaa" "33333333aaaaaaaaa" @@ -1620,7 +1620,7 @@ let nestedList: obj list = [| |> should equal """ -let nestedList : obj list = +let nestedList: obj list = [| "11111111aaaaaaaaa" "22222222aaaaaaaaa" "33333333aaaaaaaaa" @@ -1651,7 +1651,7 @@ let nestedList: obj list = [| |> should equal """ -let nestedList : obj list = +let nestedList: obj list = [| "11111111aaaaaaaaa" "22222222aaaaaaaaa" "33333333aaaaaaaaa" @@ -1683,7 +1683,7 @@ let nestedList: obj list = [ |> should equal """ -let nestedList : obj list = +let nestedList: obj list = [ "11111111aaaaaaaaa" "22222222aaaaaaaaa" "33333333aaaaaaaaa" @@ -1716,7 +1716,7 @@ let nestedList: obj list = [| |> should equal """ -let nestedList : obj list = +let nestedList: obj list = [| "11111111aaaaaaaaa" "22222222aaaaaaaaa" "33333333aaaaaaaaa" @@ -1956,7 +1956,7 @@ let choices : Foo list = |> should equal """ -let choices : Foo list = +let choices: Foo list = [ yield! getMore 9 yield // Test @@ -1980,7 +1980,7 @@ let choices : Foo list = |> should equal """ -let choices : Foo list = +let choices: Foo list = [ yield! // Test [ Foo 2 ] ] diff --git a/src/Fantomas.Tests/RecordTests.fs b/src/Fantomas.Tests/RecordTests.fs index 33eedb3c23..eca22b9f5b 100644 --- a/src/Fantomas.Tests/RecordTests.fs +++ b/src/Fantomas.Tests/RecordTests.fs @@ -357,8 +357,8 @@ let ``anon record`` () = |> should equal """ -let r : {| Foo: int - Bar: string |} = +let r: {| Foo: int + Bar: string |} = {| Foo = 123 Bar = "" |} """ @@ -376,8 +376,8 @@ let ``anon record - struct`` () = |> should equal """ -let r : struct {| Foo: int - Bar: string |} = +let r: struct {| Foo: int + Bar: string |} = struct {| Foo = 123 Bar = "" |} """ @@ -1205,7 +1205,7 @@ type XX = type XX = { a: int b: int } - static member foo : int = 30 + static member foo: int = 30 """ [] @@ -1228,7 +1228,7 @@ type XX = { a: int b: int } - static member private foo : int = 30 + static member private foo: int = 30 """ [] diff --git a/src/Fantomas.Tests/SpaceBeforeColonTests.fs b/src/Fantomas.Tests/SpaceBeforeColonTests.fs new file mode 100644 index 0000000000..c38742c7de --- /dev/null +++ b/src/Fantomas.Tests/SpaceBeforeColonTests.fs @@ -0,0 +1,83 @@ +module Fantomas.Tests.SpaceBeforeColonTests + +open NUnit.Framework +open FsUnit +open Fantomas.Tests.TestHelper + +// ref: https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting#right-pad-value-and-function-argument-type-annotations + +[] +let ``right-pad value and function argument type annotations`` () = + formatSourceString + false + """ +// OK +let complexFunction (a: int) (b: int) c = a + b + c +let expensiveToCompute: int = 0 // Type annotation for let-bound value + +type C() = + member _.Property: int = 1 + +// Bad +let complexFunctionBad (a :int) (b :int) (c:int) = a + b + c +let expensiveToComputeBad1:int = 1 +let expensiveToComputeBad2 :int = 2 +""" + config + |> prepend newline + |> should + equal + """ +// OK +let complexFunction (a: int) (b: int) c = a + b + c +let expensiveToCompute: int = 0 // Type annotation for let-bound value + +type C() = + member _.Property: int = 1 + +// Bad +let complexFunctionBad (a: int) (b: int) (c: int) = a + b + c +let expensiveToComputeBad1: int = 1 +let expensiveToComputeBad2: int = 2 +""" + +// ref: https://github.com/G-Research/fsharp-formatting-conventions#type-annotations + +[] +let ``when defining arguments with type annotations, use white space before and after the : symbol:`` () = + formatSourceString + false + """ +// OK +let complexFunction (a : int) (b : int) c = a + b + c + +// Bad +let complexFunctionBad (a :int) (b: int) (c:int) = a + b + c + +// OK +let expensiveToCompute : int = 0 // Type annotation for let-bound value +let myFun (a : decimal) b c : decimal = a + b + c // Type annotation for the return type of a function +// Bad +let expensiveToComputeBad1:int = 1 +let expensiveToComputeBad2 :int = 2 +let myFunBad (a: decimal) b c:decimal = a + b + c +""" + { config with SpaceBeforeColon = true } + |> prepend newline + |> should + equal + """ +// OK +let complexFunction (a : int) (b : int) c = a + b + c + +// Bad +let complexFunctionBad (a : int) (b : int) (c : int) = a + b + c + +// OK +let expensiveToCompute : int = 0 // Type annotation for let-bound value +let myFun (a : decimal) b c : decimal = a + b + c // Type annotation for the return type of a function +// Bad +let expensiveToComputeBad1 : int = 1 +let expensiveToComputeBad2 : int = 2 +let myFunBad (a : decimal) b c : decimal = a + b + c +""" diff --git a/src/Fantomas.Tests/SpecialConstructsTests.fs b/src/Fantomas.Tests/SpecialConstructsTests.fs index 0d58d08140..73c0479659 100644 --- a/src/Fantomas.Tests/SpecialConstructsTests.fs +++ b/src/Fantomas.Tests/SpecialConstructsTests.fs @@ -39,7 +39,7 @@ x.G[].TryFind 3 type F = abstract G : int list -> Map -let x : F = +let x: F = {new F with member __.G _ = Map.empty} diff --git a/src/Fantomas.Tests/StructTests.fs b/src/Fantomas.Tests/StructTests.fs index b8fd7a05b9..ac1e219cb7 100644 --- a/src/Fantomas.Tests/StructTests.fs +++ b/src/Fantomas.Tests/StructTests.fs @@ -94,7 +94,7 @@ match t with equal """ type S = S of struct (int * int) -let g : struct (int * int) = struct (1, 1) +let g: struct (int * int) = struct (1, 1) let z = fun (S (struct (u, v)): S) -> u + v let t = struct (1, 2) diff --git a/src/Fantomas.Tests/SynConstTests.fs b/src/Fantomas.Tests/SynConstTests.fs index 370fee500e..ee2003fb6b 100644 --- a/src/Fantomas.Tests/SynConstTests.fs +++ b/src/Fantomas.Tests/SynConstTests.fs @@ -452,7 +452,7 @@ triple quotes string thing |> should equal " -let foo : string = +let foo: string = \"\"\"moo, long triple quotes string thing diff --git a/src/Fantomas.Tests/TypeDeclarationTests.fs b/src/Fantomas.Tests/TypeDeclarationTests.fs index b9d5388126..3f155c1a2f 100644 --- a/src/Fantomas.Tests/TypeDeclarationTests.fs +++ b/src/Fantomas.Tests/TypeDeclarationTests.fs @@ -2181,7 +2181,7 @@ type Box() = equal """ type Box() = - let mutable color : string = null + let mutable color: string = null // A Box has a color property with get and set. member x.Color diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index a937bc7c6d..85c3d9a955 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -5201,7 +5201,12 @@ and genSynBindingValue let genReturnType = match returnType with | Some rt -> - sepColonWithSpacesFixed + let hasGenerics = + match valueName with + | SynPat.LongIdent (_, _, Some _, _, _, _) -> true + | _ -> false + + ifElse hasGenerics sepColonWithSpacesFixed sepColon +> genType astContext false rt | None -> sepNone