From fc3d0661046cfa45ebf74364de39da46eb71aa5b Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 15 Jun 2020 22:27:55 +0200 Subject: [PATCH 1/4] =?UTF-8?q?Formatting=20Elmish=20code=20=F0=9F=99=88?= =?UTF-8?q?=F0=9F=99=89=F0=9F=99=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Fantomas.Tests/ElmishTests.fs | 465 +++++++++++++++++++++++++++++- src/Fantomas/CodePrinter.fs | 56 ++++ src/Fantomas/SourceParser.fs | 31 +- 3 files changed, 549 insertions(+), 3 deletions(-) diff --git a/src/Fantomas.Tests/ElmishTests.fs b/src/Fantomas.Tests/ElmishTests.fs index c08d21817a..f085cc2354 100644 --- a/src/Fantomas.Tests/ElmishTests.fs +++ b/src/Fantomas.Tests/ElmishTests.fs @@ -119,4 +119,467 @@ let loginPage = (isRunning = true, heightRequest = 30.0, isVisible = model.IsSigningIn) ])) ]))) -""" \ No newline at end of file +""" + +(* +Formatting Elmish Code ~ An attempt by Florian Verdonck + +So people have been asking for alternative way of formatting Elmish code. +At best, they dictate that is should be better. While agreeing it should, the main question remains: how? + +In the following unit tests, I tried to come up with something that makes sense. +This is by all means not the final outcome, this merely serves as a starting point. +And I hope it triggers the correct conversation about the topic. + +To keep things focused, I limited the scope to the Fable.React bindings: +- https://github.com/fable-compiler/fable-react/blob/master/src/Fable.React.Standard.fs +- https://github.com/fable-compiler/fable-react/blob/master/src/Fable.React.Props.fs + +I would also like to tackle variants like Feliz and Fabulous, however, all in good time. +Initially, I want to tackle Fable.React style and see where it goes. + +*) + +(* + +In SourceParser, I created two active patterns to capture html element with and without children. + +With: + +identifier [list of attributes] [list of children] + +Without: + +identifier [list of attributes] + +These are then printed out in genExpr of CodePrinter. +In the following unit tests I will try to explain the reasoning behind certain decisions. +Again nothing is set in stone, this is just an initial attempt. + +Some general rules: + +- If things are short (short being determined by a setting) put them on one line +- If things are multiline, align attributes and children so they are easy to distinguish. +- Multiline attributes are formatted: + [ a1 + a2 + a3 ] + While multiline children are formatted: + [ c1 + c2 + c3 + ] + + Again to quickly identify what is what. +- A lot of the test start with something like: + div [] [ + ... + ] + + Why wasn't this style considered? + Well that would break stylistic significantly with how lists are formatted in Fantomas + Lists now are either: + [ e1 + e2 ] + + or + [ + e1 + e2 + ] + + so I chose those two constructs to format both attributes and children. + It remains a bit closer to the rest. +*) + +// everything is short, so single line + +[] +let ``input without attributes`` () = + formatSourceString false """let i = input [] +""" config + |> prepend newline + |> should equal """ +let i = input [] +""" + +// everything is short, so single line + +[] +let ``short input with single attribute`` () = + formatSourceString false """let i = input [ Type "text" ] +""" config + |> prepend newline + |> should equal """ +let i = input [ Type "text" ] +""" + +// the attributes are multiline, they follow the natural way of how multiline arrays are formatted by default in Fantomas +// one difference there is that will always but place next to the identifier (input, in this case) + +[] +let ``multiline input with multiple attributes`` () = + formatSourceString false """let i = input [ Type "text"; Required "required" ] +""" config + |> prepend newline + |> should equal """ +let i = + input [ Type "text" + Required "required" ] +""" + +// everything is short, so single line + +[] +let ``div without children or attributes`` () = + formatSourceString false """let d = div [] [] +""" config + |> prepend newline + |> should equal """ +let d = div [] [] +""" + +// everything is short, so single line + +[] +let ``div with short attributes`` () = + formatSourceString false """let d = div [ ClassName "mt-4" ] [] +""" config + |> prepend newline + |> should equal """ +let d = div [ ClassName "mt-4" ] [] +""" + +// here, it is immediately visible that the div has no children +// another option was to put the empty list [] right after the attributes +// however, that felt like an unnecessary exception + +[] +let ``div with multiline attributes`` () = + formatSourceString false """let d = div [ ClassName "container"; OnClick (fun _ -> printfn "meh") ] [] +""" config + |> prepend newline + |> should equal """ +let d = + div [ ClassName "container" + OnClick(fun _ -> printfn "meh") ] + [] +""" + +// everything is short, so single line + +[] +let ``div with no attributes and short no-elmish children`` () = + formatSourceString false """let d = div [] [ str "meh" ] +""" config + |> prepend newline + |> should equal """ +let d = div [] [ str "meh" ] +""" + +// multiple children, the fact the the opening [ and closing ] of the children list align prevents some confusion. +// it is clear where the second span stops and where the div stops. + +[] +let ``div with not attributes and multiple elmish children`` () = + formatSourceString false """let d = + div [] [ + span [] [ str "a" ] + span [] [ str "b" ] + ] +""" config + |> prepend newline + |> should equal """ +let d = + div [] + [ span [] [ str "a" ] + span [] [ str "b" ] + ] +""" + +// same example as above but with short attributes + +[] +let ``div with single attribute and children`` () = + formatSourceString false """let view = + div [ ClassName "container" ] [ + h1 [] [ str "A heading 1" ] + p [] [ str "A paragraph" ] + ] +""" config + |> prepend newline + |> should equal """ +let view = + div [ ClassName "container" ] + [ h1 [] [ str "A heading 1" ] + p [] [ str "A paragraph" ] + ] +""" + +// long attributes and long children +// the fact that the attributes have a different closing as to the children helps identify which is what. + +[] +let ``div with multiple attributes and children`` () = + formatSourceString false """let d = +div [ ClassName "container"; OnClick (fun _ -> printfn "meh") ] [ + span [] [str "foo"] + code [] [str "bar"] +] +""" config + |> prepend newline + |> should equal """ +let d = + div [ ClassName "container" + OnClick(fun _ -> printfn "meh") ] + [ span [] [ str "foo" ] + code [] [ str "bar" ] + ] +""" + +// if there is a single child that is short, keep it on one line + +[] +let ``short div with short p`` () = + formatSourceString false """let d = + div [] [ p [] [ str "meh" ] ] +""" config + |> prepend newline + |> should equal """ +let d = div [] [ p [] [ str "meh" ] ] +""" + +// again, as long as thing are short, keep them in one line + +[] +let ``short div with multiple short children`` () = + formatSourceString false """let d = + div [] [ + br [] ; br [] + ] +""" config + |> prepend newline + |> should equal """ +let d = div [] [ br []; br [] ] +""" + +[] +let ``div with long children but a long setting`` () = + formatSourceString false """let d = + div [] [ + p [] [ str "fooooooooo" ] + p [] [ str "baaaaaaaar" ] + ] +""" { config with MaxArrayOrListWidth = 150 } + |> prepend newline + |> should equal """ +let d = + div [] [ p [] [ str "fooooooooo" ]; p [] [ str "baaaaaaaar" ] ] +""" + +// here the p is 38 characters +// this makes the div multiline +// but here the is only one child that is short, so the closing ] of list says on the same line + +[] +let ``short div with slightly longer p`` () = + formatSourceString false """let d = + div [] [ p [] [ str "meeeeeeeeeeeeeeeeeeeeeh" ] ] +""" config + |> prepend newline + |> should equal """ +let d = + div [] + [ p [] [ str "meeeeeeeeeeeeeeeeeeeeeh" ] ] +""" + +// here is it is easier to spot there the div ends because the closing ] aligns with the opening ] + +[] +let ``short div with longer p`` () = + formatSourceString false """let d = + div [] [ p [] [ str "meeeeeeeeeeeeeeeeeeeeehhhh" ] ] +""" config + |> prepend newline + |> should equal """ +let d = + div [] + [ p [] + [ str "meeeeeeeeeeeeeeeeeeeeehhhh" ] + ] +""" + +[] +let counter () = + formatSourceString false """ +let view model dispatch = + div [] [ + button [ OnClick (fun _ -> dispatch Decrement) ] [ + str "-" + ] + div [] [ + str (sprintf "%A" model) + ] + button [ OnClick (fun _ -> dispatch Increment) ] [ + str "+" + ] + ] +""" config + |> prepend newline + |> should equal """ +let view model dispatch = + div [] + [ button [ OnClick(fun _ -> dispatch Decrement) ] + [ str "-" ] + div [] [ str (sprintf "%A" model) ] + button [ OnClick(fun _ -> dispatch Increment) ] + [ str "+" ] + ] +""" + +[] +let ``view entry`` () = + formatSourceString false """ +let viewEntry todo dispatch = + li [ classList [ ("completed", todo.completed); ("editing", todo.editing) ] ] + [ div [ ClassName "view" ] + [ input [ ClassName "toggle" + Type "checkbox" + Checked todo.completed + OnChange (fun _ -> Check (todo.id,(not todo.completed)) |> dispatch) ] + label [ OnDoubleClick (fun _ -> EditingEntry (todo.id,true) |> dispatch) ] + [ str todo.description ] + button [ ClassName "destroy" + OnClick (fun _-> Delete todo.id |> dispatch) ] + [] + ] + input [ ClassName "edit" + valueOrDefault todo.description + Name "title" + Id ("todo-" + (string todo.id)) + OnInput (fun ev -> UpdateEntry (todo.id, !!ev.target?value) |> dispatch) + OnBlur (fun _ -> EditingEntry (todo.id,false) |> dispatch) + onEnter (EditingEntry (todo.id,false)) dispatch ] + ] +""" config + |> prepend newline + |> should equal """ +let viewEntry todo dispatch = + li [ classList [ ("completed", todo.completed) + ("editing", todo.editing) ] ] + [ div [ ClassName "view" ] + [ input [ ClassName "toggle" + Type "checkbox" + Checked todo.completed + OnChange(fun _ -> Check(todo.id, (not todo.completed)) |> dispatch) ] + label [ OnDoubleClick(fun _ -> EditingEntry(todo.id, true) |> dispatch) ] + [ str todo.description ] + button [ ClassName "destroy" + OnClick(fun _ -> Delete todo.id |> dispatch) ] + [] + ] + input [ ClassName "edit" + valueOrDefault todo.description + Name "title" + Id("todo-" + (string todo.id)) + OnInput(fun ev -> + UpdateEntry(todo.id, !!ev.target?value) + |> dispatch) + OnBlur(fun _ -> EditingEntry(todo.id, false) |> dispatch) + onEnter (EditingEntry(todo.id, false)) dispatch ] + ] +""" + +[] +let ``multiline attributes, no children`` () = + formatSourceString false """let a = + button [ ClassName "destroy" + OnClick(fun _-> Delete todo.id |> dispatch) ] + [] +""" config + |> prepend newline + |> should equal """ +let a = + button [ ClassName "destroy" + OnClick(fun _ -> Delete todo.id |> dispatch) ] + [] +""" + +[] +let ``table and tbody`` () = + formatSourceString false """ +table [ ClassName "table table-striped table-hover mb-0" ] + [ tbody [] + [ tokenDetailRow "TokenName" (str tokenName) + tokenDetailRow "LeftColumn" (ofInt leftColumn) + tokenDetailRow "RightColumn" (ofInt rightColumn) + tokenDetailRow "Content" (pre [] [ code [] [ str token.Content ] ]) + tokenDetailRow "ColorClass" (str colorClass) + tokenDetailRow "CharClass" (str charClass) + tokenDetailRow "Tag" (ofInt tag) + tokenDetailRow "FullMatchedLength" + (span [ ClassName "has-text-weight-semibold" ] [ ofInt fullMatchedLength ]) ] ] +""" config + |> prepend newline + |> should equal """ +table [ ClassName "table table-striped table-hover mb-0" ] + [ tbody [] + [ tokenDetailRow "TokenName" (str tokenName) + tokenDetailRow "LeftColumn" (ofInt leftColumn) + tokenDetailRow "RightColumn" (ofInt rightColumn) + tokenDetailRow "Content" (pre [] [ code [] [ str token.Content ] ]) + tokenDetailRow "ColorClass" (str colorClass) + tokenDetailRow "CharClass" (str charClass) + tokenDetailRow "Tag" (ofInt tag) + tokenDetailRow "FullMatchedLength" + (span [ ClassName "has-text-weight-semibold" ] + [ ofInt fullMatchedLength ]) + ] + ] +""" + +[] +let ``div with single child that is short`` () = + formatSourceString false """ +div [] [ p [] [ str "meh" ] ] +""" { config with MaxArrayOrListWidth = 5 } + |> prepend newline + |> should equal """ +div [] + [ p [] + [ str "meh" ] + ] +""" + +[] +let ``short expression without attributes`` () = + formatSourceString false """p [] [ str "meh" ] +""" config + |> prepend newline + |> should equal """ +p [] [ str "meh" ] +""" + +[] +let ``child with empty children`` () = + formatSourceString false """ +let commands dispatch = + Button.button + [ Button.Color Primary + Button.Custom + [ ClassName "rounded-0" + OnClick(fun _ -> dispatch GetTrivia) ] ] + [ i [ ClassName "fas fa-code mr-1" ] [] + str "Get trivia" ] +""" config + |> prepend newline + |> should equal """ +let commands dispatch = + Button.button [ Button.Color Primary + Button.Custom [ ClassName "rounded-0" + OnClick(fun _ -> dispatch GetTrivia) ] ] + [ i [ ClassName "fas fa-code mr-1" ] + [] + str "Get trivia" + ] +""" diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index 0d8e2de768..e901c2969a 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -825,6 +825,62 @@ and genExpr astContext synExpr = let sepCloseT = tokN synExpr.Range "RPAREN" sepCloseT match synExpr with + | ElmishReactWithoutChildren(identifier, attributes) -> + fun ctx -> + // TODO consider max elmish oneliner setting + let maxRemainingArrayLength = ctx.Config.MaxArrayOrListWidth - identifier.Length + let ctx' = + { ctx with Config = { ctx.Config with MaxArrayOrListWidth = maxRemainingArrayLength } } + (!- identifier +> sepSpace +> genExpr astContext attributes) ctx' + + | ElmishReactWithChildren((identifier,_,_), attributes, (_isArray,children)) -> + let genChildren isShort = + match children with + | [] -> onlyIfNot isShort sepNln +> sepOpenLFixed +> sepCloseLFixed + | [ElmishReactWithoutChildren(_) as singleChild] + | [ElmishReactWithChildren(_) as singleChild] -> + fun ctx -> + leadingExpressionLong + // TODO consider max elmish oneliner setting + ctx.Config.MaxArrayOrListWidth + (sepOpenL +> genExpr astContext singleChild) + (fun isLong -> ifElse isLong (sepNln +> sepCloseLFixed) sepCloseL) + ctx + | [singleChild] -> + sepOpenL +> genExpr astContext singleChild +> sepCloseL + | children -> + if isShort then + sepOpenL + +> col sepSemi children (genExpr astContext) + +> sepCloseL + else + sepOpenL + +> atCurrentColumn (col sepNln children (genExpr astContext)) + +> sepNln + +> sepCloseLFixed + + let shortExpression = + !- identifier + +> sepSpace + +> genExpr astContext attributes + +> sepSpace + +> genChildren true + + let longExpression = + !- identifier + +> sepSpace + +> atCurrentColumn (genExpr astContext attributes + +> ifElse (List.isEmpty children) sepSpace sepNln + +> genChildren false) + + fun ctx -> + isShortExpression + // TODO consider max elmish oneliner setting + ctx.Config.MaxArrayOrListWidth + shortExpression + longExpression + ctx + | SingleExpr(Lazy, e) -> // Always add braces when dealing with lazy let hasParenthesis = hasParenthesis e diff --git a/src/Fantomas/SourceParser.fs b/src/Fantomas/SourceParser.fs index 66c30a3814..bd92409ed0 100644 --- a/src/Fantomas/SourceParser.fs +++ b/src/Fantomas/SourceParser.fs @@ -653,7 +653,6 @@ let (|Indexer|) = function | SynIndexerArg.Two(e1,e1FromEnd,e2,e2FromEnd,_,_) -> Pair((e1, e1FromEnd), (e2, e2FromEnd)) | SynIndexerArg.One(e,fromEnd,_) -> Single(e, fromEnd) -// hier ergens let (|OptVar|_|) = function | SynExpr.Ident(IdentOrKeyword(OpNameFull (s,r))) -> Some(s, false, r) @@ -1424,4 +1423,32 @@ let rec (|UppercaseSynExpr|LowercaseSynExpr|) (synExpr:SynExpr) = let isFunctionBinding (p: SynPat) = match p with | PatLongIdent(_, _, ps, _) when (List.isNotEmpty ps) -> true - | _ -> false \ No newline at end of file + | _ -> false + +let (|ElmishReactWithoutChildren|_|) e = + match e with + | App(OptVar(ident,_,_), [ArrayOrList(_) as attributes]) -> + Some(ident, attributes) + | App(OptVar(ident,_,_), [ArrayOrListOfSeqExpr(_, CompExpr(_, Sequentials _)) as attributes]) -> + Some(ident, attributes) + | _ -> + None + +let (|ElmishReactWithChildren|_|) e = + match e with + | App(OptVar(ident), [ArrayOrList(_) as attributes; ArrayOrList(isArray, children, _)]) -> + Some(ident, attributes, (isArray, children)) + | App(OptVar(ident), [ArrayOrListOfSeqExpr(_) as attributes + ArrayOrListOfSeqExpr(isArray, CompExpr(_, Sequentials children))]) -> + Some(ident, attributes, (isArray,children)) + | App(OptVar(ident), [ArrayOrListOfSeqExpr(_) as attributes + ArrayOrListOfSeqExpr(isArray, CompExpr(_, singleChild))]) + | App(OptVar(ident), [ArrayOrList(_) as attributes + ArrayOrListOfSeqExpr(isArray, CompExpr(_, singleChild))]) -> + Some(ident, attributes, (isArray,[singleChild])) + | App(OptVar(ident), [ArrayOrListOfSeqExpr(_) as attributes + ArrayOrList(isArray, [], _) ]) -> + Some(ident, attributes, (isArray, [])) + + | _ -> + None \ No newline at end of file From 05cb7753ae64060125ae18629a7ac8541ac75d4d Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 16 Jun 2020 21:08:42 +0200 Subject: [PATCH 2/4] Elmish code take two --- src/Fantomas.Tests/ElmishTests.fs | 259 ++++++++++++++++++------------ src/Fantomas.Tests/LambdaTests.fs | 16 +- src/Fantomas/CodePrinter.fs | 54 ++++--- 3 files changed, 205 insertions(+), 124 deletions(-) diff --git a/src/Fantomas.Tests/ElmishTests.fs b/src/Fantomas.Tests/ElmishTests.fs index f085cc2354..6670ccc2e9 100644 --- a/src/Fantomas.Tests/ElmishTests.fs +++ b/src/Fantomas.Tests/ElmishTests.fs @@ -165,31 +165,13 @@ Some general rules: a2 a3 ] While multiline children are formatted: - [ c1 - c2 - c3 + identifier attributes [ c1 + c2 + c3 ] - Again to quickly identify what is what. -- A lot of the test start with something like: - div [] [ - ... - ] - - Why wasn't this style considered? - Well that would break stylistic significantly with how lists are formatted in Fantomas - Lists now are either: - [ e1 - e2 ] + the closing ] aligns with the identifier, children have one indent. - or - [ - e1 - e2 - ] - - so I chose those two constructs to format both attributes and children. - It remains a bit closer to the rest. *) // everything is short, so single line @@ -251,8 +233,7 @@ let d = div [ ClassName "mt-4" ] [] """ // here, it is immediately visible that the div has no children -// another option was to put the empty list [] right after the attributes -// however, that felt like an unnecessary exception +// the empty list is placed next to the closing ] of the attributes [] let ``div with multiline attributes`` () = @@ -262,8 +243,7 @@ let ``div with multiline attributes`` () = |> should equal """ let d = div [ ClassName "container" - OnClick(fun _ -> printfn "meh") ] - [] + OnClick(fun _ -> printfn "meh") ] [] """ // everything is short, so single line @@ -277,8 +257,8 @@ let ``div with no attributes and short no-elmish children`` () = let d = div [] [ str "meh" ] """ -// multiple children, the fact the the opening [ and closing ] of the children list align prevents some confusion. -// it is clear where the second span stops and where the div stops. +// multiple children +// one indent further than the start of the parent identifier [] let ``div with not attributes and multiple elmish children`` () = @@ -291,10 +271,10 @@ let ``div with not attributes and multiple elmish children`` () = |> prepend newline |> should equal """ let d = - div [] - [ span [] [ str "a" ] - span [] [ str "b" ] - ] + div [] [ + span [] [ str "a" ] + span [] [ str "b" ] + ] """ // same example as above but with short attributes @@ -310,14 +290,13 @@ let ``div with single attribute and children`` () = |> prepend newline |> should equal """ let view = - div [ ClassName "container" ] - [ h1 [] [ str "A heading 1" ] - p [] [ str "A paragraph" ] - ] + div [ ClassName "container" ] [ + h1 [] [ str "A heading 1" ] + p [] [ str "A paragraph" ] + ] """ // long attributes and long children -// the fact that the attributes have a different closing as to the children helps identify which is what. [] let ``div with multiple attributes and children`` () = @@ -331,10 +310,10 @@ div [ ClassName "container"; OnClick (fun _ -> printfn "meh") ] [ |> should equal """ let d = div [ ClassName "container" - OnClick(fun _ -> printfn "meh") ] - [ span [] [ str "foo" ] - code [] [ str "bar" ] - ] + OnClick(fun _ -> printfn "meh") ] [ + span [] [ str "foo" ] + code [] [ str "bar" ] + ] """ // if there is a single child that is short, keep it on one line @@ -389,11 +368,12 @@ let ``short div with slightly longer p`` () = |> prepend newline |> should equal """ let d = - div [] - [ p [] [ str "meeeeeeeeeeeeeeeeeeeeeh" ] ] + div [] [ + p [] [ str "meeeeeeeeeeeeeeeeeeeeeh" ] + ] """ -// here is it is easier to spot there the div ends because the closing ] aligns with the opening ] +// here is it is easier to spot there the div ends and where the p ends because of the extra indent. [] let ``short div with longer p`` () = @@ -403,10 +383,11 @@ let ``short div with longer p`` () = |> prepend newline |> should equal """ let d = - div [] - [ p [] - [ str "meeeeeeeeeeeeeeeeeeeeehhhh" ] + div [] [ + p [] [ + str "meeeeeeeeeeeeeeeeeeeeehhhh" ] + ] """ [] @@ -428,13 +409,15 @@ let view model dispatch = |> prepend newline |> should equal """ let view model dispatch = - div [] - [ button [ OnClick(fun _ -> dispatch Decrement) ] - [ str "-" ] - div [] [ str (sprintf "%A" model) ] - button [ OnClick(fun _ -> dispatch Increment) ] - [ str "+" ] + div [] [ + button [ OnClick(fun _ -> dispatch Decrement) ] [ + str "-" + ] + div [] [ str (sprintf "%A" model) ] + button [ OnClick(fun _ -> dispatch Increment) ] [ + str "+" ] + ] """ [] @@ -466,28 +449,28 @@ let viewEntry todo dispatch = |> should equal """ let viewEntry todo dispatch = li [ classList [ ("completed", todo.completed) - ("editing", todo.editing) ] ] - [ div [ ClassName "view" ] - [ input [ ClassName "toggle" - Type "checkbox" - Checked todo.completed - OnChange(fun _ -> Check(todo.id, (not todo.completed)) |> dispatch) ] - label [ OnDoubleClick(fun _ -> EditingEntry(todo.id, true) |> dispatch) ] - [ str todo.description ] - button [ ClassName "destroy" - OnClick(fun _ -> Delete todo.id |> dispatch) ] - [] - ] - input [ ClassName "edit" - valueOrDefault todo.description - Name "title" - Id("todo-" + (string todo.id)) - OnInput(fun ev -> - UpdateEntry(todo.id, !!ev.target?value) - |> dispatch) - OnBlur(fun _ -> EditingEntry(todo.id, false) |> dispatch) - onEnter (EditingEntry(todo.id, false)) dispatch ] - ] + ("editing", todo.editing) ] ] [ + div [ ClassName "view" ] [ + input [ ClassName "toggle" + Type "checkbox" + Checked todo.completed + OnChange(fun _ -> Check(todo.id, (not todo.completed)) |> dispatch) ] + label [ OnDoubleClick(fun _ -> EditingEntry(todo.id, true) |> dispatch) ] [ + str todo.description + ] + button [ ClassName "destroy" + OnClick(fun _ -> Delete todo.id |> dispatch) ] [] + ] + input [ ClassName "edit" + valueOrDefault todo.description + Name "title" + Id("todo-" + (string todo.id)) + OnInput(fun ev -> + UpdateEntry(todo.id, !!ev.target?value) + |> dispatch) + OnBlur(fun _ -> EditingEntry(todo.id, false) |> dispatch) + onEnter (EditingEntry(todo.id, false)) dispatch ] + ] """ [] @@ -501,8 +484,7 @@ let ``multiline attributes, no children`` () = |> should equal """ let a = button [ ClassName "destroy" - OnClick(fun _ -> Delete todo.id |> dispatch) ] - [] + OnClick(fun _ -> Delete todo.id |> dispatch) ] [] """ [] @@ -522,20 +504,21 @@ table [ ClassName "table table-striped table-hover mb-0" ] """ config |> prepend newline |> should equal """ -table [ ClassName "table table-striped table-hover mb-0" ] - [ tbody [] - [ tokenDetailRow "TokenName" (str tokenName) - tokenDetailRow "LeftColumn" (ofInt leftColumn) - tokenDetailRow "RightColumn" (ofInt rightColumn) - tokenDetailRow "Content" (pre [] [ code [] [ str token.Content ] ]) - tokenDetailRow "ColorClass" (str colorClass) - tokenDetailRow "CharClass" (str charClass) - tokenDetailRow "Tag" (ofInt tag) - tokenDetailRow "FullMatchedLength" - (span [ ClassName "has-text-weight-semibold" ] - [ ofInt fullMatchedLength ]) - ] - ] +table [ ClassName "table table-striped table-hover mb-0" ] [ + tbody [] [ + tokenDetailRow "TokenName" (str tokenName) + tokenDetailRow "LeftColumn" (ofInt leftColumn) + tokenDetailRow "RightColumn" (ofInt rightColumn) + tokenDetailRow "Content" (pre [] [ code [] [ str token.Content ] ]) + tokenDetailRow "ColorClass" (str colorClass) + tokenDetailRow "CharClass" (str charClass) + tokenDetailRow "Tag" (ofInt tag) + tokenDetailRow "FullMatchedLength" + (span [ ClassName "has-text-weight-semibold" ] [ + ofInt fullMatchedLength + ]) + ] +] """ [] @@ -545,10 +528,11 @@ div [] [ p [] [ str "meh" ] ] """ { config with MaxArrayOrListWidth = 5 } |> prepend newline |> should equal """ -div [] - [ p [] - [ str "meh" ] +div [] [ + p [] [ + str "meh" ] +] """ [] @@ -577,9 +561,86 @@ let commands dispatch = let commands dispatch = Button.button [ Button.Color Primary Button.Custom [ ClassName "rounded-0" - OnClick(fun _ -> dispatch GetTrivia) ] ] - [ i [ ClassName "fas fa-code mr-1" ] - [] - str "Get trivia" - ] + OnClick(fun _ -> dispatch GetTrivia) ] ] [ + i [ ClassName "fas fa-code mr-1" ] [] + str "Get trivia" + ] """ + +[] +let ``clock with two spaces`` () = + formatSourceString false """ +let view (CurrentTime time) dispatch = + svg + [ ViewBox "0 0 100 100" + SVG.Width "350px" ] + [ circle + [ Cx "50" + Cy "50" + R "45" + SVG.Fill "#0B79CE" ] [] + // Hours + clockHand (Hour time.Hour) "lightgreen" "2" 25.0 + handTop time.Hour "lightgreen" 25.0 12.0 + // Minutes + clockHand (Minute time.Minute) "white" "2" 35.0 + handTop time.Minute "white" 35.0 60.0 + // Seconds + clockHand (Second time.Second) "#023963" "1" 40.0 + handTop time.Second "#023963" 40.0 60.0 + // circle in the center + circle + [ Cx "50" + Cy "50" + R "3" + SVG.Fill "#0B79CE" + SVG.Stroke "#023963" + SVG.StrokeWidth 1.0 ] [] + ] +""" { config with IndentSpaceNum = 2 } + |> prepend newline + |> should equal """ +let view (CurrentTime time) dispatch = + svg [ ViewBox "0 0 100 100" + SVG.Width "350px" ] [ + circle [ Cx "50" + Cy "50" + R "45" + SVG.Fill "#0B79CE" ] [] + // Hours + clockHand (Hour time.Hour) "lightgreen" "2" 25.0 + handTop time.Hour "lightgreen" 25.0 12.0 + // Minutes + clockHand (Minute time.Minute) "white" "2" 35.0 + handTop time.Minute "white" 35.0 60.0 + // Seconds + clockHand (Second time.Second) "#023963" "1" 40.0 + handTop time.Second "#023963" 40.0 60.0 + // circle in the center + circle [ Cx "50" + Cy "50" + R "3" + SVG.Fill "#0B79CE" + SVG.Stroke "#023963" + SVG.StrokeWidth 1.0 ] [] + ] +""" + +[] +let ``mehh`` () = + formatSourceString false """let x = +Opts.oneOf + (Optional, + [ Opt.flag [ "third"; "f" ] + Opt.valueWith "new value" [ "fourth"; "ssssssssssssssssssssssssssssssssssssssssssssssssssss" ] ]) +""" config + |> prepend newline + |> should equal """ +let x = + Opts.oneOf + (Optional, + [ Opt.flag [ "third"; "f" ] + Opt.valueWith "new value" + [ "fourth" + "ssssssssssssssssssssssssssssssssssssssssssssssssssss" ] ]) +""" \ No newline at end of file diff --git a/src/Fantomas.Tests/LambdaTests.fs b/src/Fantomas.Tests/LambdaTests.fs index 1c5c10cf87..a4f771a55f 100644 --- a/src/Fantomas.Tests/LambdaTests.fs +++ b/src/Fantomas.Tests/LambdaTests.fs @@ -65,11 +65,17 @@ open Reactstrap let private badgeSample = FunctionComponent.Of ((fun _ -> - fragment [] - [ h3 [] - [ str "Heading " - Badge.badge [ Badge.Color Secondary ] [ str "New" ] ] - Badge.badge [ Badge.Color Warning ] [ str "oh my" ] ]), + fragment [] [ + h3 [] [ + str "Heading " + Badge.badge [ Badge.Color Secondary ] [ + str "New" + ] + ] + Badge.badge [ Badge.Color Warning ] [ + str "oh my" + ] + ]), "BadgeSample") exportDefault badgeSample diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index e901c2969a..19dfbe23ce 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -830,24 +830,36 @@ and genExpr astContext synExpr = // TODO consider max elmish oneliner setting let maxRemainingArrayLength = ctx.Config.MaxArrayOrListWidth - identifier.Length let ctx' = - { ctx with Config = { ctx.Config with MaxArrayOrListWidth = maxRemainingArrayLength } } - (!- identifier +> sepSpace +> genExpr astContext attributes) ctx' + { ctx with Config = { ctx.Config with + MaxArrayOrListWidth = maxRemainingArrayLength + // override user setting to get original fantomas formatting + MultilineBlockBracketsOnSameColumn = false } } + (!- identifier + +> sepSpace + +> genExpr astContext attributes + +> fun cty -> + // reset the config with original values + { cty with Config = { cty.Config with + MaxArrayOrListWidth = ctx.Config.MaxArrayOrListWidth + MultilineBlockBracketsOnSameColumn = ctx.Config.MultilineBlockBracketsOnSameColumn } }) ctx' + | ElmishReactWithChildren((identifier,_,_), attributes, (_isArray,children)) -> let genChildren isShort = match children with - | [] -> onlyIfNot isShort sepNln +> sepOpenLFixed +> sepCloseLFixed - | [ElmishReactWithoutChildren(_) as singleChild] - | [ElmishReactWithChildren(_) as singleChild] -> - fun ctx -> - leadingExpressionLong - // TODO consider max elmish oneliner setting - ctx.Config.MaxArrayOrListWidth - (sepOpenL +> genExpr astContext singleChild) - (fun isLong -> ifElse isLong (sepNln +> sepCloseLFixed) sepCloseL) - ctx + | [] -> sepOpenLFixed +> sepCloseLFixed | [singleChild] -> - sepOpenL +> genExpr astContext singleChild +> sepCloseL + if isShort then + sepOpenL +> genExpr astContext singleChild +> sepCloseL + else + sepOpenL + +> indent + +> sepNln + +> genExpr astContext singleChild + +> unindent + +> sepNln + +> sepCloseLFixed + | children -> if isShort then sepOpenL @@ -855,7 +867,10 @@ and genExpr astContext synExpr = +> sepCloseL else sepOpenL - +> atCurrentColumn (col sepNln children (genExpr astContext)) + +> indent + +> sepNln + +> col sepNln children (genExpr astContext) + +> unindent +> sepNln +> sepCloseLFixed @@ -867,12 +882,11 @@ and genExpr astContext synExpr = +> genChildren true let longExpression = - !- identifier - +> sepSpace - +> atCurrentColumn (genExpr astContext attributes - +> ifElse (List.isEmpty children) sepSpace sepNln - +> genChildren false) - + atCurrentColumn(!- identifier + +> sepSpace + +> atCurrentColumn (genExpr astContext attributes) + +> sepSpace + +> genChildren false) fun ctx -> isShortExpression // TODO consider max elmish oneliner setting From c3a5cf1ac54125bfc5fef7c24cf4aebe99438a90 Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 17 Jun 2020 20:05:15 +0200 Subject: [PATCH 3/4] Extracted the documentation from the unit tests. Clean up the code. Support for arrays. --- docs/Documentation.md | 1 + docs/Formatting-Elmish-code.md | 74 ++++++++++++ src/Fantomas.Tests/ElmishTests.fs | 181 +++++++++--------------------- src/Fantomas/CodePrinter.fs | 27 ++--- src/Fantomas/FormatConfig.fs | 6 +- src/Fantomas/schema.json | 3 + 6 files changed, 149 insertions(+), 143 deletions(-) create mode 100644 docs/Formatting-Elmish-code.md diff --git a/docs/Documentation.md b/docs/Documentation.md index 1eb12fab2b..4f0d004080 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -272,6 +272,7 @@ A default configuration file would look like "MaxFunctionBindingWidth":40, "MultilineBlockBracketsOnSameColumn":false, "NewlineBetweenTypeDefinitionAndMembers":false, + "MaxElmishWidth": 40, "StrictMode":false } ``` diff --git a/docs/Formatting-Elmish-code.md b/docs/Formatting-Elmish-code.md new file mode 100644 index 0000000000..31f5357758 --- /dev/null +++ b/docs/Formatting-Elmish-code.md @@ -0,0 +1,74 @@ +# Formatting Elmish style guide + +This guide explains the main reasoning of how Fantomas formats "Elmish" inspired code. + +## Scope + +To keep things focused, the scope is currently limited to the Fable.React bindings: +- https://github.com/fable-compiler/fable-react/blob/master/src/Fable.React.Standard.fs +- https://github.com/fable-compiler/fable-react/blob/master/src/Fable.React.Props.fs + +Variants like Feliz and Fabulous might be covered in the future as well. + +## Key concepts + +There are two active patterns for [SynExpr](https://fsharp.github.io/FSharp.Compiler.Service/reference/fsharp-compiler-syntaxtree-synexpr.html) that capture the shapes in the Elmish DSL. + +See [SourceParser.fs](../src/Fantomas/SourceParser.fs) + +```fsharp +let (|ElmishReactWithoutChildren|_|) e = ... + +let (|ElmishReactWithChildren|_|) e = +```` + +`ElmishReactWithoutChildren` captures unary tags like `` or `
`. +Translated in the F# DSL they match a function that takes a single list as arguments. + +```fsharp +let i = input [ Type "hidden" ] +``` + +The props or attributes parameter is formatted like a normal list or array would be in default Fantomas. + +```fsharp +// short +myTag [ a1; a2 ] + +// long +myTag [ a1 + a2 + a3 ] +``` + +The tag and attributes will always align. + +`ElmishReactWithChildren` captures the non-unary tags like `

...

` or `
...
`. +Translated in the F# DSL they match a function that takes two lists as arguments. +The first argument matches the same rules as the unary tag. + +While the second argument starts its opening bracket right after the closing of the attributes. +The closing bracket of the children matches the start column of the tag. +Unless the entire expression is short. + +Children have one extra indent starting from the parent tag column. + + +```fsharp +// short +let myParagraph = p [] [ str "short" ] + +// long +let myContainer = + div [ ClassName "container" ] [ + h1 [] [ str "my title" ] + ] +``` + +When the children argument is empty, it is place right after the attributes. + +```fsharp +let x = + div [ OnClick (fun _ -> prinftn "meh" + ClassName "container" ] [] +``` diff --git a/src/Fantomas.Tests/ElmishTests.fs b/src/Fantomas.Tests/ElmishTests.fs index 6670ccc2e9..e3a7fa728e 100644 --- a/src/Fantomas.Tests/ElmishTests.fs +++ b/src/Fantomas.Tests/ElmishTests.fs @@ -121,61 +121,6 @@ let loginPage = isVisible = model.IsSigningIn) ])) ]))) """ -(* -Formatting Elmish Code ~ An attempt by Florian Verdonck - -So people have been asking for alternative way of formatting Elmish code. -At best, they dictate that is should be better. While agreeing it should, the main question remains: how? - -In the following unit tests, I tried to come up with something that makes sense. -This is by all means not the final outcome, this merely serves as a starting point. -And I hope it triggers the correct conversation about the topic. - -To keep things focused, I limited the scope to the Fable.React bindings: -- https://github.com/fable-compiler/fable-react/blob/master/src/Fable.React.Standard.fs -- https://github.com/fable-compiler/fable-react/blob/master/src/Fable.React.Props.fs - -I would also like to tackle variants like Feliz and Fabulous, however, all in good time. -Initially, I want to tackle Fable.React style and see where it goes. - -*) - -(* - -In SourceParser, I created two active patterns to capture html element with and without children. - -With: - -identifier [list of attributes] [list of children] - -Without: - -identifier [list of attributes] - -These are then printed out in genExpr of CodePrinter. -In the following unit tests I will try to explain the reasoning behind certain decisions. -Again nothing is set in stone, this is just an initial attempt. - -Some general rules: - -- If things are short (short being determined by a setting) put them on one line -- If things are multiline, align attributes and children so they are easy to distinguish. -- Multiline attributes are formatted: - [ a1 - a2 - a3 ] - While multiline children are formatted: - identifier attributes [ c1 - c2 - c3 - ] - - the closing ] aligns with the identifier, children have one indent. - -*) - -// everything is short, so single line - [] let ``input without attributes`` () = formatSourceString false """let i = input [] @@ -185,8 +130,6 @@ let ``input without attributes`` () = let i = input [] """ -// everything is short, so single line - [] let ``short input with single attribute`` () = formatSourceString false """let i = input [ Type "text" ] @@ -196,9 +139,6 @@ let ``short input with single attribute`` () = let i = input [ Type "text" ] """ -// the attributes are multiline, they follow the natural way of how multiline arrays are formatted by default in Fantomas -// one difference there is that will always but place next to the identifier (input, in this case) - [] let ``multiline input with multiple attributes`` () = formatSourceString false """let i = input [ Type "text"; Required "required" ] @@ -210,8 +150,6 @@ let i = Required "required" ] """ -// everything is short, so single line - [] let ``div without children or attributes`` () = formatSourceString false """let d = div [] [] @@ -221,8 +159,6 @@ let ``div without children or attributes`` () = let d = div [] [] """ -// everything is short, so single line - [] let ``div with short attributes`` () = formatSourceString false """let d = div [ ClassName "mt-4" ] [] @@ -232,34 +168,27 @@ let ``div with short attributes`` () = let d = div [ ClassName "mt-4" ] [] """ -// here, it is immediately visible that the div has no children -// the empty list is placed next to the closing ] of the attributes [] -let ``div with multiline attributes`` () = - formatSourceString false """let d = div [ ClassName "container"; OnClick (fun _ -> printfn "meh") ] [] +let ``div with no attributes and short children`` () = + formatSourceString false """let d = div [] [ str "meh" ] """ config |> prepend newline |> should equal """ -let d = - div [ ClassName "container" - OnClick(fun _ -> printfn "meh") ] [] +let d = div [] [ str "meh" ] """ -// everything is short, so single line - [] -let ``div with no attributes and short no-elmish children`` () = - formatSourceString false """let d = div [] [ str "meh" ] +let ``div with multiline attributes`` () = + formatSourceString false """let d = div [ ClassName "container"; OnClick (fun _ -> printfn "meh") ] [] """ config |> prepend newline |> should equal """ -let d = div [] [ str "meh" ] +let d = + div [ ClassName "container" + OnClick(fun _ -> printfn "meh") ] [] """ -// multiple children -// one indent further than the start of the parent identifier - [] let ``div with not attributes and multiple elmish children`` () = formatSourceString false """let d = @@ -277,8 +206,6 @@ let d = ] """ -// same example as above but with short attributes - [] let ``div with single attribute and children`` () = formatSourceString false """let view = @@ -296,8 +223,6 @@ let view = ] """ -// long attributes and long children - [] let ``div with multiple attributes and children`` () = formatSourceString false """let d = @@ -316,7 +241,6 @@ let d = ] """ -// if there is a single child that is short, keep it on one line [] let ``short div with short p`` () = @@ -328,7 +252,6 @@ let ``short div with short p`` () = let d = div [] [ p [] [ str "meh" ] ] """ -// again, as long as thing are short, keep them in one line [] let ``short div with multiple short children`` () = @@ -349,7 +272,7 @@ let ``div with long children but a long setting`` () = p [] [ str "fooooooooo" ] p [] [ str "baaaaaaaar" ] ] -""" { config with MaxArrayOrListWidth = 150 } +""" { config with MaxElmishWidth = 150 } |> prepend newline |> should equal """ let d = @@ -357,8 +280,7 @@ let d = """ // here the p is 38 characters -// this makes the div multiline -// but here the is only one child that is short, so the closing ] of list says on the same line +// this makes the div multiline but the p not. [] let ``short div with slightly longer p`` () = @@ -373,10 +295,8 @@ let d = ] """ -// here is it is easier to spot there the div ends and where the p ends because of the extra indent. - [] -let ``short div with longer p`` () = +let ``div with longer p`` () = formatSourceString false """let d = div [] [ p [] [ str "meeeeeeeeeeeeeeeeeeeeehhhh" ] ] """ config @@ -521,29 +441,6 @@ table [ ClassName "table table-striped table-hover mb-0" ] [ ] """ -[] -let ``div with single child that is short`` () = - formatSourceString false """ -div [] [ p [] [ str "meh" ] ] -""" { config with MaxArrayOrListWidth = 5 } - |> prepend newline - |> should equal """ -div [] [ - p [] [ - str "meh" - ] -] -""" - -[] -let ``short expression without attributes`` () = - formatSourceString false """p [] [ str "meh" ] -""" config - |> prepend newline - |> should equal """ -p [] [ str "meh" ] -""" - [] let ``child with empty children`` () = formatSourceString false """ @@ -627,20 +524,48 @@ let view (CurrentTime time) dispatch = """ [] -let ``mehh`` () = - formatSourceString false """let x = -Opts.oneOf - (Optional, - [ Opt.flag [ "third"; "f" ] - Opt.valueWith "new value" [ "fourth"; "ssssssssssssssssssssssssssssssssssssssssssssssssssss" ] ]) +let ``input with attribute array`` () = + formatSourceString false """let ia = input [| Type "hidden"; Name "code"; Required "required" |] """ config |> prepend newline |> should equal """ -let x = - Opts.oneOf - (Optional, - [ Opt.flag [ "third"; "f" ] - Opt.valueWith "new value" - [ "fourth" - "ssssssssssssssssssssssssssssssssssssssssssssssssssss" ] ]) -""" \ No newline at end of file +let ia = + input [| Type "hidden" + Name "code" + Required "required" |] +""" + +[] +let ``div with children array`` () = + formatSourceString false """let d = + div [||] [| p [||] [| str "oh my foobar" |] |] +""" config + |> prepend newline + |> should equal """ +let d = + div [||] [| + p [||] [| str "oh my foobar" |] + |] +""" + +[] +let ``mix lists and array`` () = + formatSourceString false """let view dispatch model = + div [| Class "container" |] + [ + h1 [] [| str "my title" |] + button [| OnClick (fun _ -> dispatch Msg.Foo) |] [ + str "click me" + ] + ] +""" config + |> prepend newline + |> should equal """ +let view dispatch model = + div [| Class "container" |] [ + h1 [] [| str "my title" |] + button [| OnClick(fun _ -> dispatch Msg.Foo) |] [ + str "click me" + ] + ] +""" diff --git a/src/Fantomas/CodePrinter.fs b/src/Fantomas/CodePrinter.fs index 19dfbe23ce..476605ef84 100644 --- a/src/Fantomas/CodePrinter.fs +++ b/src/Fantomas/CodePrinter.fs @@ -827,8 +827,7 @@ and genExpr astContext synExpr = match synExpr with | ElmishReactWithoutChildren(identifier, attributes) -> fun ctx -> - // TODO consider max elmish oneliner setting - let maxRemainingArrayLength = ctx.Config.MaxArrayOrListWidth - identifier.Length + let maxRemainingArrayLength = ctx.Config.MaxElmishWidth - identifier.Length let ctx' = { ctx with Config = { ctx.Config with MaxArrayOrListWidth = maxRemainingArrayLength @@ -844,35 +843,38 @@ and genExpr astContext synExpr = MultilineBlockBracketsOnSameColumn = ctx.Config.MultilineBlockBracketsOnSameColumn } }) ctx' - | ElmishReactWithChildren((identifier,_,_), attributes, (_isArray,children)) -> + | ElmishReactWithChildren((identifier,_,_), attributes, (isArray,children)) -> let genChildren isShort = match children with - | [] -> sepOpenLFixed +> sepCloseLFixed + | [] when (not isArray) -> sepOpenLFixed +> sepCloseLFixed + | [] when isArray -> sepOpenAFixed +> sepCloseAFixed | [singleChild] -> if isShort then - sepOpenL +> genExpr astContext singleChild +> sepCloseL + ifElse isArray sepOpenA sepOpenL + +> genExpr astContext singleChild + +> ifElse isArray sepCloseA sepCloseL else - sepOpenL + ifElse isArray sepOpenA sepOpenL +> indent +> sepNln +> genExpr astContext singleChild +> unindent +> sepNln - +> sepCloseLFixed + +> ifElse isArray sepCloseAFixed sepCloseLFixed | children -> if isShort then - sepOpenL + ifElse isArray sepOpenA sepOpenL +> col sepSemi children (genExpr astContext) - +> sepCloseL + +> ifElse isArray sepCloseA sepCloseL else - sepOpenL + ifElse isArray sepOpenA sepOpenL +> indent +> sepNln +> col sepNln children (genExpr astContext) +> unindent +> sepNln - +> sepCloseLFixed + +> ifElse isArray sepCloseAFixed sepCloseLFixed let shortExpression = !- identifier @@ -889,8 +891,7 @@ and genExpr astContext synExpr = +> genChildren false) fun ctx -> isShortExpression - // TODO consider max elmish oneliner setting - ctx.Config.MaxArrayOrListWidth + ctx.Config.MaxElmishWidth shortExpression longExpression ctx diff --git a/src/Fantomas/FormatConfig.fs b/src/Fantomas/FormatConfig.fs index bf84ca80d3..50147e1328 100644 --- a/src/Fantomas/FormatConfig.fs +++ b/src/Fantomas/FormatConfig.fs @@ -37,6 +37,7 @@ type FormatConfig = MultilineBlockBracketsOnSameColumn : bool NewlineBetweenTypeDefinitionAndMembers: bool KeepIfThenInSameLine : bool + MaxElmishWidth: Num /// Prettyprinting based on ASTs only StrictMode : bool } @@ -63,8 +64,9 @@ type FormatConfig = MaxFunctionBindingWidth = 40 MultilineBlockBracketsOnSameColumn = false KeepIfThenInSameLine = false - StrictMode = false - NewlineBetweenTypeDefinitionAndMembers = false } + MaxElmishWidth = 40 + NewlineBetweenTypeDefinitionAndMembers = false + StrictMode = false } static member ApplyOptions(currentConfig, options) = let currentValues = Reflection.getRecordFields currentConfig diff --git a/src/Fantomas/schema.json b/src/Fantomas/schema.json index bb4fd5e0d2..26fdffdb19 100644 --- a/src/Fantomas/schema.json +++ b/src/Fantomas/schema.json @@ -71,6 +71,9 @@ "NewlineBetweenTypeDefinitionAndMembers": { "type": "boolean" }, + "MaxElmishWidth": { + "type": "integer" + }, "StrictMode": { "type": "boolean" } From 1e8a86141e89f5417b94510329f53a98cd1874ea Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 17 Jun 2020 20:12:56 +0200 Subject: [PATCH 4/4] Bumped to alpha 10 --- RELEASE_NOTES.md | 2 +- .../Fantomas.CoreGlobalTool.Tests.fsproj | 2 +- src/Fantomas.CoreGlobalTool/Fantomas.CoreGlobalTool.fsproj | 2 +- src/Fantomas.Tests/Fantomas.Tests.fsproj | 2 +- src/Fantomas/Fantomas.fsproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6dbe40dcd8..3aebd4bf0e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,4 +1,4 @@ -### 4.0.0-alpha-009 - 06/2020 +### 4.0.0-alpha-010 - 06/2020 * WIP for [#705](https://github.com/fsprojects/fantomas/issues/705) * FCS 36 diff --git a/src/Fantomas.CoreGlobalTool.Tests/Fantomas.CoreGlobalTool.Tests.fsproj b/src/Fantomas.CoreGlobalTool.Tests/Fantomas.CoreGlobalTool.Tests.fsproj index 1f9c050fcc..1e402e07c1 100644 --- a/src/Fantomas.CoreGlobalTool.Tests/Fantomas.CoreGlobalTool.Tests.fsproj +++ b/src/Fantomas.CoreGlobalTool.Tests/Fantomas.CoreGlobalTool.Tests.fsproj @@ -4,7 +4,7 @@ netcoreapp3.1 false false - 4.0.0-alpha-009 + 4.0.0-alpha-010 FS0988 diff --git a/src/Fantomas.CoreGlobalTool/Fantomas.CoreGlobalTool.fsproj b/src/Fantomas.CoreGlobalTool/Fantomas.CoreGlobalTool.fsproj index 84435fc6e5..59ba23a699 100644 --- a/src/Fantomas.CoreGlobalTool/Fantomas.CoreGlobalTool.fsproj +++ b/src/Fantomas.CoreGlobalTool/Fantomas.CoreGlobalTool.fsproj @@ -4,7 +4,7 @@ netcoreapp3.1 fantomas True - 4.0.0-alpha-009 + 4.0.0-alpha-010 fantomas-tool diff --git a/src/Fantomas.Tests/Fantomas.Tests.fsproj b/src/Fantomas.Tests/Fantomas.Tests.fsproj index 1a9cb4a58a..b30444531c 100644 --- a/src/Fantomas.Tests/Fantomas.Tests.fsproj +++ b/src/Fantomas.Tests/Fantomas.Tests.fsproj @@ -1,7 +1,7 @@ - 4.0.0-alpha-009 + 4.0.0-alpha-010 FS0988 netcoreapp3.1 diff --git a/src/Fantomas/Fantomas.fsproj b/src/Fantomas/Fantomas.fsproj index 0db9e911c2..dfb0da4a0c 100644 --- a/src/Fantomas/Fantomas.fsproj +++ b/src/Fantomas/Fantomas.fsproj @@ -3,7 +3,7 @@ netstandard2.0 - 4.0.0-alpha-009 + 4.0.0-alpha-010 Source code formatter for F#