diff --git a/src/Fantomas.Tests/AttributeTests.fs b/src/Fantomas.Tests/AttributeTests.fs index 0cd22877a3..77ebde57e3 100644 --- a/src/Fantomas.Tests/AttributeTests.fs +++ b/src/Fantomas.Tests/AttributeTests.fs @@ -451,3 +451,126 @@ type Commenter = DisplayName: string } """ + +[] +let ``assembly attributes remain on own line, 629`` () = + formatSourceString false """ +namespace AltCover.Visualizer + +open System +open System.Reflection +open System.Runtime.InteropServices + +[] +[] +[] +[] +[] +() +""" config + |> prepend newline + |> should equal """ +namespace AltCover.Visualizer + +open System +open System.Reflection +open System.Runtime.InteropServices + +[] +[] +[] +[] +[] +() +""" + +[] +let ``multiple attributes inside SynAttributes that exceeds max line length, 629`` () = + formatSourceString false """ +//[] +[] +type RoleAdminImportController(akkaService: AkkaService) = + inherit Controller() + + [, 200); + ProducesResponseType(404); + Authorize(AuthorizationScopePolicies.Read)>] + member _.ListJobs(): Task = + task { + return! + akkaService.ImporterSystem.ApiMaster , 200); + ProducesResponseType(404); + Authorize(AuthorizationScopePolicies.Write)>] + member _.StartJob(file: IFormFile, [] args: ImporterJobArgs) = + let importer = akkaService.ImporterSystem + + ActionResult.ofAsyncResult <| asyncResult { + let! state = + (LowerCaseString.create args.State, file) + |> pipeObjectThroughValidation [ (fst, [stateIsValid]); (snd, [(fun s -> Ok s)]) ] + + let! filePath = FormFile.downloadAsTemp file + + let job = + { JobType = EsriBoundaryImport + FileToImport = filePath + State = state + DryRun = args.DryRun } + + importer.ApiMaster prepend newline + |> should equal """ +//[] +[] +type RoleAdminImportController(akkaService: AkkaService) = + inherit Controller() + + [, 200); + ProducesResponseType(404); + Authorize(AuthorizationScopePolicies.Read)>] + member _.ListJobs(): Task = + task { + return! akkaService.ImporterSystem.ApiMaster + , 200); + ProducesResponseType(404); + Authorize(AuthorizationScopePolicies.Write)>] + member _.StartJob(file: IFormFile, [] args: ImporterJobArgs) = + let importer = akkaService.ImporterSystem + + ActionResult.ofAsyncResult + <| asyncResult { + let! state = + (LowerCaseString.create args.State, file) + |> pipeObjectThroughValidation [ (fst, [ stateIsValid ]) + (snd, [ (fun s -> Ok s) ]) ] + + let! filePath = FormFile.downloadAsTemp file + + let job = + { JobType = EsriBoundaryImport + FileToImport = filePath + State = state + DryRun = args.DryRun } + + importer.ApiMaster ] -extern int AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [] System.Object [] rgvarChildren, int* pcObtained) +extern int AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [] System.Object [] rgvarChildren, int* pcObtained) """ [] diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index be90c8a4b0..0c3775a1b8 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -218,6 +218,8 @@ and genModuleDeclList astContext e = let newlineAfterMultiline ctx = let expr = match List.tryHead rest with + | Some (SynModuleDecl.DoExpr(_, SynExpr.Const(SynConst.Unit,_),rm)) when (match m with SynModuleDecl.Attributes(a,_) -> List.length a > 1) -> + sepNlnConsideringTriviaContentBeforeWithAttributes rm attrs | Some rm -> let attrs = getRangesFromAttributesFromModuleDeclaration rm sepNln +> sepNlnConsideringTriviaContentBeforeWithAttributes rm.Range attrs @@ -414,7 +416,13 @@ and genAttributesCore astContext (ats: SynAttribute seq) = if SourceTransformer.hasParenthesis e then id else sepSpace opt sepColonFixed target (!-) -- s +> argSpacing +> genExpr astContext e |> genTrivia attr.Range - ifElse (Seq.isEmpty ats) sepNone (!- "[<" +> atCurrentColumn (col sepSemi ats (genAttributeExpr astContext)) -- ">]") + + let shortExpression = !- "[<" +> atCurrentColumn (col sepSemi ats (genAttributeExpr astContext)) -- ">]" + let longExpression = !- "[<" +> atCurrentColumn (col (sepSemi +> sepNln) ats (genAttributeExpr astContext)) -- ">]" + + ifElse (Seq.isEmpty ats) + sepNone + (expressionFitsOnRestOfLine shortExpression longExpression) and genOnelinerAttributes astContext ats = let ats = List.collect (fun a -> a.Attributes) ats