diff --git a/src/Fantomas.Tests/AttributeTests.fs b/src/Fantomas.Tests/AttributeTests.fs index 2f7e9bc692..f5c141014d 100644 --- a/src/Fantomas.Tests/AttributeTests.fs +++ b/src/Fantomas.Tests/AttributeTests.fs @@ -988,3 +988,58 @@ let x: int = 99 // bar again, cuz why not let x: int = 99 """ + +[] +let ``comment between attribute and nested module, 2016`` () = + formatSourceString + false + """ +[] +// Having members as extensions gives them lower priority in +// overload resolution and allows skipping more type annotations. +module AsyncOptionCEExtensions = + + type AsyncOptionBuilder with + member inline __.Source(s: #seq<_>) = s +""" + config + |> prepend newline + |> should + equal + """ +[] +// Having members as extensions gives them lower priority in +// overload resolution and allows skipping more type annotations. +module AsyncOptionCEExtensions = + + type AsyncOptionBuilder with + member inline __.Source(s: #seq<_>) = s +""" + +[] +let ``comment between attribute and nested module, signature file`` () = + formatSourceString + true + """ +[] +// Having members as extensions gives them lower priority in +// overload resolution and allows skipping more type annotations. +module AsyncOptionCEExtensions = + + type AsyncOptionBuilder with + member inline Source : string -> string + +""" + config + |> prepend newline + |> should + equal + """ +[] +// Having members as extensions gives them lower priority in +// overload resolution and allows skipping more type annotations. +module AsyncOptionCEExtensions = + + type AsyncOptionBuilder with + member inline Source: string -> string +""" diff --git a/src/Fantomas/AstExtensions.fs b/src/Fantomas/AstExtensions.fs index 6169ed65a2..26c5764272 100644 --- a/src/Fantomas/AstExtensions.fs +++ b/src/Fantomas/AstExtensions.fs @@ -46,6 +46,22 @@ type SynField with | SynField (attributes = _ :: _; idOpt = None) -> None | SynField (attributes = attrs; idOpt = Some ident) -> hasLinesBetweenAttributesAndFirstNode attrs ident.idRange +type SynModuleDecl with + member this.AfterAttributesBeforeNestedModuleName: Range option = + match this with + | SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(attributes = [])) -> None + | SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo (attributes = attrs; longId = lid :: _)) -> + hasLinesBetweenAttributesAndFirstNode attrs lid.idRange + | _ -> None + +type SynModuleSigDecl with + member this.AfterAttributesBeforeNestedModuleName: Range option = + match this with + | SynModuleSigDecl.NestedModule(moduleInfo = SynComponentInfo(attributes = [])) -> None + | SynModuleSigDecl.NestedModule(moduleInfo = SynComponentInfo (attributes = attrs; longId = lid :: _)) -> + hasLinesBetweenAttributesAndFirstNode attrs lid.idRange + | _ -> None + // TODO: Remove when https://github.com/dotnet/fsharp/pull/12441 is part of FCS type SynExceptionDefnRepr with member this.FullRange: range = diff --git a/src/Fantomas/AstTransformer.fs b/src/Fantomas/AstTransformer.fs index 96341656e4..691400fd23 100644 --- a/src/Fantomas/AstTransformer.fs +++ b/src/Fantomas/AstTransformer.fs @@ -40,7 +40,13 @@ module private Ast = decls |> List.map visit let finalContinuation (nodes: TriviaNodeAssigner list list) : TriviaNodeAssigner list = + let afterAttributesBeforeNestedModule = + ast.AfterAttributesBeforeNestedModuleName + |> Option.map (mkNode SynModuleDecl_NestedModule_AfterAttributesBeforeModuleName) + |> Option.toList + [ mkNode SynModuleDecl_NestedModule range + yield! afterAttributesBeforeNestedModule yield! visitSynComponentInfo sci yield! (List.collect id nodes) ] |> finalContinuation @@ -1349,7 +1355,13 @@ module private Ast = List.map visit decls let finalContinuation (nodes: TriviaNodeAssigner list list) : TriviaNodeAssigner list = + let afterAttributesBeforeNestedModule = + ast.AfterAttributesBeforeNestedModuleName + |> Option.map (mkNode SynModuleSigDecl_NestedModule_AfterAttributesBeforeModuleName) + |> Option.toList + [ yield mkNode SynModuleSigDecl_NestedModule range + yield! afterAttributesBeforeNestedModule yield! visitSynComponentInfo sci yield! (List.collect id nodes) ] |> finalContinuation diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index 0457bfb643..772ae59305 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -383,6 +383,9 @@ and genModuleDecl astContext (node: SynModuleDecl) = | NestedModule (ats, px, ao, s, isRecursive, mds) -> genPreXmlDoc px +> genAttributes astContext ats + +> genAfterAttributesBefore + SynModuleDecl_NestedModule_AfterAttributesBeforeModuleName + node.AfterAttributesBeforeNestedModuleName +> (!- "module ") +> opt sepSpace ao genAccess +> ifElse isRecursive (!- "rec ") sepNone @@ -419,7 +422,11 @@ and genSigModuleDecl astContext node = | SigModuleAbbrev (s1, s2) -> !- "module " -- s1 +> sepEq +> sepSpace -- s2 | SigNamespaceFragment m -> failwithf "NamespaceFragment is not supported yet: %O" m | SigNestedModule (ats, px, ao, s, mds) -> - genPreXmlDoc px +> genAttributes astContext ats + genPreXmlDoc px + +> genAttributes astContext ats + +> genAfterAttributesBefore + SynModuleSigDecl_NestedModule_AfterAttributesBeforeModuleName + node.AfterAttributesBeforeNestedModuleName -- "module " +> opt sepSpace ao genAccess -- s diff --git a/src/Fantomas/TriviaTypes.fs b/src/Fantomas/TriviaTypes.fs index b4ef4445f7..978ea069c1 100644 --- a/src/Fantomas/TriviaTypes.fs +++ b/src/Fantomas/TriviaTypes.fs @@ -105,6 +105,7 @@ type FsAstType = // | SynModuleOrNamespace_NamedModule | SynModuleDecl_ModuleAbbrev | SynModuleDecl_NestedModule + | SynModuleDecl_NestedModule_AfterAttributesBeforeModuleName | SynModuleDecl_Let | SynModuleDecl_DoExpr | SynModuleDecl_Types @@ -343,6 +344,7 @@ type FsAstType = // | SynModuleOrNamespaceSig_NamedModule | SynModuleSigDecl_ModuleAbbrev | SynModuleSigDecl_NestedModule + | SynModuleSigDecl_NestedModule_AfterAttributesBeforeModuleName | SynModuleSigDecl_Types | SynModuleSigDecl_Open | SynModuleSigDecl_OpenType