Skip to content

Commit cda1c47

Browse files
authored
Revisit long function definitions (#947)
* Revert default style to Microsoft style guide. * Add setting for G-Research long function definition style. * Added test for recursive function. * Correct position of equal sign according to the Microsoft Style guide. * Corrected typo
1 parent 34cdd61 commit cda1c47

File tree

4 files changed

+186
-62
lines changed

4 files changed

+186
-62
lines changed

src/Fantomas.Tests/FunctionDefinitionTests.fs

Lines changed: 119 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,9 @@ let fold (funcs : ResultFunc<'Input, 'Output, 'TError> seq) (input : 'Input) : R
384384
MaxInfixOperatorExpression = 70 })
385385
|> prepend newline
386386
|> should equal """
387-
let fold
388-
(funcs : ResultFunc<'Input, 'Output, 'TError> seq)
389-
(input : 'Input)
390-
: Result<'Output list, 'TError list>
391-
=
387+
let fold (funcs : ResultFunc<'Input, 'Output, 'TError> seq)
388+
(input : 'Input)
389+
: Result<'Output list, 'TError list> =
392390
let mutable anyErrors = false
393391
let mutable collectedOutputs = []
394392
let mutable collectedErrors = []
@@ -430,10 +428,9 @@ let ``internal keyword included in function signature length check`` () =
430428
""" ({ config with MaxLineLength = 90; SpaceBeforeColon = true })
431429
|> prepend newline
432430
|> should equal """
433-
let internal UpdateStrongNaming
434-
(assembly : AssemblyDefinition)
435-
(key : StrongNameKeyPair option)
436-
=
431+
let internal UpdateStrongNaming (assembly : AssemblyDefinition)
432+
(key : StrongNameKeyPair option)
433+
=
437434
assembly.Name
438435
439436
let UpdateStrongNamingX (assembly : AssemblyDefinition) (key : StrongNameKeyPair option) =
@@ -469,10 +466,9 @@ module FormatCode =
469466
CodeFormatter.FormatDocumentAsync("tmp.fsx", source, config, options, checker)
470467
471468
[<FunctionName("FormatCode")>]
472-
let run
473-
([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{*any}")>] req: HttpRequest)
474-
(log: ILogger)
475-
=
469+
let run ([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{*any}")>] req: HttpRequest)
470+
(log: ILogger)
471+
=
476472
Http.main CodeFormatter.GetVersion format FormatConfig.FormatConfig.Default log req
477473
"""
478474

@@ -505,11 +501,9 @@ module FormatCode =
505501
CodeFormatter.FormatDocumentAsync("tmp.fsx", source, config, options, checker)
506502
507503
[<FunctionName("FormatCode")>]
508-
let run
509-
([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{*any}")>] req: HttpRequest)
510-
(log: ILogger)
511-
: HttpResponse
512-
=
504+
let run ([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{*any}")>] req: HttpRequest)
505+
(log: ILogger)
506+
: HttpResponse =
513507
Http.main CodeFormatter.GetVersion format FormatConfig.FormatConfig.Default log req
514508
"""
515509

@@ -526,14 +520,13 @@ let private addTaskToScheduler (scheduler : IScheduler) taskName taskCron prio (
526520
""" ({ config with MaxLineLength = 100 })
527521
|> prepend newline
528522
|> should equal """
529-
let private addTaskToScheduler
530-
(scheduler: IScheduler)
531-
taskName
532-
taskCron
533-
prio
534-
(task: unit -> unit)
535-
groupName
536-
=
523+
let private addTaskToScheduler (scheduler: IScheduler)
524+
taskName
525+
taskCron
526+
prio
527+
(task: unit -> unit)
528+
groupName
529+
=
537530
let mutable jobDataMap = JobDataMap()
538531
jobDataMap.["task"] <- task
539532
@@ -551,11 +544,9 @@ let ``long function signature should align with equal sign, 883`` () =
551544
""" { config with IndentSize = 2; SpaceBeforeColon = true }
552545
|> prepend newline
553546
|> should equal """
554-
let readModel
555-
(updateState : 'State -> EventEnvelope<'Event> list -> 'State)
556-
(initState : 'State)
557-
: ReadModel<'Event, 'State>
558-
=
547+
let readModel (updateState : 'State -> EventEnvelope<'Event> list -> 'State)
548+
(initState : 'State)
549+
: ReadModel<'Event, 'State> =
559550
()
560551
"""
561552

@@ -566,9 +557,105 @@ let ``long function signature should align with equal sign, no return type`` ()
566557
""" { config with IndentSize = 2; SpaceBeforeColon = true; MaxLineLength = 80 }
567558
|> prepend newline
568559
|> should equal """
560+
let readModel (updateState : 'State -> EventEnvelope<'Event> list -> 'State)
561+
(initState : 'State)
562+
=
563+
()
564+
"""
565+
566+
[<Test>]
567+
let ``long function signature with single tuple parameter and no return type`` () =
568+
formatSourceString false """
569+
let fold (funcs: ResultFunc<'Input, 'Output, 'TError> seq, input: 'Input, input2: 'Input, input3: 'Input) =
570+
()
571+
""" { config with MaxLineLength = 90 }
572+
|> prepend newline
573+
|> should equal """
574+
let fold (funcs: ResultFunc<'Input, 'Output, 'TError> seq,
575+
input: 'Input,
576+
input2: 'Input,
577+
input3: 'Input) =
578+
()
579+
"""
580+
581+
[<Test>]
582+
let ``long function signature with single tuple parameter and return type`` () =
583+
formatSourceString false """
584+
let fold (funcs: ResultFunc<'Input, 'Output, 'TError> seq, input: 'Input, input2: 'Input, input3: 'Input) : Result<'Output list, 'TError list> =
585+
()
586+
""" { config with MaxLineLength = 90 }
587+
|> prepend newline
588+
|> should equal """
589+
let fold (funcs: ResultFunc<'Input, 'Output, 'TError> seq,
590+
input: 'Input,
591+
input2: 'Input,
592+
input3: 'Input)
593+
: Result<'Output list, 'TError list> =
594+
()
595+
"""
596+
597+
[<Test>]
598+
let ``align long function signature to indentation without return type `` () =
599+
formatSourceString false """
600+
let fold (funcs: ResultFunc<'Input, 'Output, 'TError> seq) (input: 'Input) (input2: 'Input) (input3: 'Input) = ()
601+
""" { config with MaxLineLength = 60; AlignFunctionSignatureToIndentation = true }
602+
|> prepend newline
603+
|> should equal """
604+
let fold
605+
(funcs: ResultFunc<'Input, 'Output, 'TError> seq)
606+
(input: 'Input)
607+
(input2: 'Input)
608+
(input3: 'Input)
609+
=
610+
()
611+
"""
612+
613+
[<Test>]
614+
let ``align long function signature to indentation with return type`` () =
615+
formatSourceString false """let readModel (updateState : 'State -> EventEnvelope<'Event> list -> 'State) (initState : 'State) : ReadModel<'Event, 'State> =
616+
()
617+
""" { config with IndentSize = 2; SpaceBeforeColon = true; AlignFunctionSignatureToIndentation = true }
618+
|> prepend newline
619+
|> should equal """
569620
let readModel
570621
(updateState : 'State -> EventEnvelope<'Event> list -> 'State)
571622
(initState : 'State)
623+
: ReadModel<'Event, 'State>
572624
=
573625
()
574-
"""
626+
"""
627+
628+
[<Test>]
629+
let ``align long function signature to indentation that are recursive`` () =
630+
formatSourceString false """
631+
let rec run ([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{*any}")>] req: HttpRequest) (log: ILogger) : HttpResponse =
632+
logAnalyticsForRequest log req
633+
Http.main CodeFormatter.GetVersion format FormatConfig.FormatConfig.Default log req
634+
635+
and logAnalyticsForRequest (log:ILogger) (httpRequest: HttpRequest) =
636+
log.Info (sprintf "Meh: %A" httpRequest)
637+
""" { config with MaxLineLength = 60; AlignFunctionSignatureToIndentation = true }
638+
|> prepend newline
639+
|> should equal """
640+
let rec run
641+
([<HttpTrigger(AuthorizationLevel.Anonymous,
642+
"get",
643+
"post",
644+
Route = "{*any}")>] req: HttpRequest)
645+
(log: ILogger)
646+
: HttpResponse
647+
=
648+
logAnalyticsForRequest log req
649+
Http.main
650+
CodeFormatter.GetVersion
651+
format
652+
FormatConfig.FormatConfig.Default
653+
log
654+
req
655+
656+
and logAnalyticsForRequest
657+
(log: ILogger)
658+
(httpRequest: HttpRequest)
659+
=
660+
log.Info(sprintf "Meh: %A" httpRequest)
661+
"""

src/Fantomas.Tests/LetBindingTests.fs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,32 @@ let ``line comment before return type info should indent before colon, 565`` ()
341341
|> should equal """
342342
module Bar =
343343
let f a
344-
// foo
345-
: int
346-
=
344+
// foo
345+
: int =
347346
0
348347
"""
349348

349+
[<Test>]
350+
let ``line comment before return type with AlignFunctionSignatureToIndentation`` () =
351+
formatSourceString false """
352+
let functionName a b c
353+
// foo
354+
: int
355+
=
356+
0
357+
""" { config with AlignFunctionSignatureToIndentation = true }
358+
|> prepend newline
359+
|> should equal """
360+
let functionName
361+
a
362+
b
363+
c
364+
// foo
365+
: int
366+
=
367+
0
368+
"""
369+
350370
[<Test>]
351371
let ``has symbol in signature requires paren, 564`` () =
352372
formatSourceString false """module Bar =
@@ -510,12 +530,11 @@ let ``handle hash directives before equals, 728`` () =
510530
()
511531
512532
""" config
513-
|> should equal """let Baz
514-
(firstParam: string)
533+
|> should equal """let Baz (firstParam: string)
515534
#if DEBUG
516-
(_: int)
535+
(_: int)
517536
#else
518-
(secndParam: int)
537+
(secndParam: int)
519538
#endif
520539
=
521540
()
@@ -535,12 +554,11 @@ let ``multiple empty lines between equals and expression`` () =
535554
()
536555
537556
""" config
538-
|> should equal """let Baz
539-
(firstParam: string)
557+
|> should equal """let Baz (firstParam: string)
540558
#if DEBUG
541-
(_: int)
559+
(_: int)
542560
#else
543-
(secndParam: int)
561+
(secndParam: int)
544562
#endif
545563
=
546564

src/Fantomas/CodePrinter.fs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,15 @@ and genExprSepEqPrependType astContext (pat:SynPat) (e: SynExpr) (valInfo:SynVal
468468
fun ctx ->
469469
let alreadyHasNewline = lastWriteEventIsNewline ctx
470470
if alreadyHasNewline then
471-
(rep ctx.Config.IndentSize (!- " ") +> !- "=") ctx
472-
else
471+
// Column could be 0 when a hash directive was just written
472+
if ctx.Column = 0 then
473+
(rep ctx.Config.IndentSize (!- " ") +> !- "=") ctx
474+
else
475+
!- "=" ctx
476+
elif ctx.Config.AlignFunctionSignatureToIndentation then
473477
(indent +> sepNln +> !- "=" +> unindent) ctx
478+
else
479+
(sepEq +> sepSpace) ctx
474480
else
475481
(sepEq +> sepSpace)
476482

@@ -486,10 +492,7 @@ and genExprSepEqPrependType astContext (pat:SynPat) (e: SynExpr) (valInfo:SynVal
486492

487493
let hasLineCommentBeforeColon = TriviaHelpers.``has line comment before`` t.Range ctx.Trivia
488494

489-
let genCommentBeforeColon ctx =
490-
(ifElse hasLineCommentBeforeColon indent sepNone
491-
+> enterNode t.Range
492-
+> ifElse hasLineCommentBeforeColon unindent sepNone) ctx
495+
let genCommentBeforeColon = atCurrentColumn (enterNode t.Range)
493496

494497
let genMetadataAttributes =
495498
match valInfo with
@@ -538,7 +541,7 @@ and genLetBinding astContext pref b =
538541
| LetBinding(ats, px, ao, isInline, isMutable, p, e, valInfo) ->
539542
let genPat =
540543
match e, p with
541-
| TypedExpr(Typed, _, t), PatLongIdent(ao, s, ps, tpso) when (List.length ps > 1)->
544+
| TypedExpr(Typed, _, t), PatLongIdent(ao, s, ps, tpso) when (List.isNotEmpty ps)->
542545
genPatWithReturnType ao s ps tpso (Some t) astContext
543546
| _, PatLongIdent(ao, s, ps, tpso) when (List.length ps > 1)->
544547
genPatWithReturnType ao s ps tpso None astContext
@@ -2075,9 +2078,9 @@ and genTypeDefn astContext (TypeDef(ats, px, ao, tds, tcs, tdr, ms, s, preferPos
20752078
+> unindent)
20762079

20772080
| Simple(TDSRUnion(ao', xs) as unionNode) ->
2078-
let hasLeadingTrivia (t : TriviaNode) =
2081+
let hasLeadingTrivia (t : TriviaNode) =
20792082
t.Range = unionNode.Range && not (List.isEmpty t.ContentBefore)
2080-
2083+
20812084
let sepNlnBasedOnTrivia =
20822085
fun (ctx: Context) ->
20832086
let trivia =
@@ -2092,11 +2095,11 @@ and genTypeDefn astContext (TypeDef(ats, px, ao, tds, tcs, tdr, ms, s, preferPos
20922095
match xs with
20932096
| [] -> ctx
20942097
| [UnionCase(attrs, _,_,_,(UnionCaseType fields)) as x] when List.isEmpty ms ->
2095-
2096-
let hasVerticalBar =
2097-
(Option.isSome ao' && List.length fields <> 1) ||
2098-
ctx.Trivia |> List.exists hasLeadingTrivia ||
2099-
not (List.isEmpty attrs) ||
2098+
2099+
let hasVerticalBar =
2100+
(Option.isSome ao' && List.length fields <> 1) ||
2101+
ctx.Trivia |> List.exists hasLeadingTrivia ||
2102+
not (List.isEmpty attrs) ||
21002103
List.isEmpty fields
21012104

21022105
indent +> sepSpace +> sepNlnBasedOnTrivia
@@ -2955,10 +2958,24 @@ and genPatWithReturnType ao s ps tpso (t:SynType option) (astContext: ASTContext
29552958
let genReturnType, newlineBeforeReturnType =
29562959
match t with
29572960
| Some t -> genType astContext false t, sepNln
2958-
| None -> sepNone, sepNone
2961+
| None ->
2962+
let genReturnType = sepNone
2963+
2964+
let newline ctx =
2965+
if ctx.Config.AlignFunctionSignatureToIndentation then
2966+
ctx
2967+
else
2968+
match ps with
2969+
| [(_, PatTuple(_))] -> ctx
2970+
| _ -> sepNln ctx
29592971

2960-
let genParametersWithNewlines =
2961-
(sepNln +> col sepNln ps (genPatWithIdent astContext) +> newlineBeforeReturnType)
2972+
genReturnType, newline
2973+
2974+
let genParametersWithNewlines ctx =
2975+
if ctx.Config.AlignFunctionSignatureToIndentation then
2976+
(indent +> sepNln +> col sepNln ps (genPatWithIdent astContext) +> newlineBeforeReturnType +> unindent) ctx
2977+
else
2978+
atCurrentColumn (col sepNln ps (genPatWithIdent astContext) +> newlineBeforeReturnType) ctx
29622979

29632980
let isLongFunctionSignature (ctx: Context) =
29642981
let space = 1
@@ -2981,7 +2998,7 @@ and genPatWithReturnType ao s ps tpso (t:SynType option) (astContext: ASTContext
29812998
let expr =
29822999
genName
29833000
+> ifElse hasBracket sepOpenT sepNone
2984-
+> ifElse isLong (indent +> genParametersWithNewlines +> unindent) genParametersInitial
3001+
+> ifElse isLong genParametersWithNewlines genParametersInitial
29853002
+> ifElse hasBracket sepCloseT sepNone
29863003

29873004
expr ctx

src/Fantomas/FormatConfig.fs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type FormatConfig =
3838
KeepIfThenInSameLine : bool
3939
MaxElmishWidth: Num
4040
SingleArgumentWebMode: bool
41+
AlignFunctionSignatureToIndentation: bool
4142
/// Pretty printing based on ASTs only
4243
StrictMode : bool }
4344

@@ -63,8 +64,9 @@ type FormatConfig =
6364
MaxValueBindingWidth = 40
6465
MaxFunctionBindingWidth = 40
6566
MultilineBlockBracketsOnSameColumn = false
67+
NewlineBetweenTypeDefinitionAndMembers = false
6668
KeepIfThenInSameLine = false
6769
MaxElmishWidth = 40
6870
SingleArgumentWebMode = false
69-
NewlineBetweenTypeDefinitionAndMembers = false
71+
AlignFunctionSignatureToIndentation = false
7072
StrictMode = false }

0 commit comments

Comments
 (0)