Feliz indentation and Fantomas

Indentation is important in F#, as it defines the code blocks and the separate code elements. Using Feliz to define a view, in a Fable.React application, the indentation matters just as much, and even more.

Fantomas is a beautiful tool that automatically formats your code and ensures consistency, also in indentation. But using code like with the Feliz library, there some serious drawbacks.

For example this is Feliz code formatted by Fantomas:

let render (state: State) (dispatch: Msg -> unit) =
    let filter = createFilter state.Generics state.Indications state.Routes state.Patients dispatch
    let details =
        match state.Details with
        | HasNotStartedYet -> "## Geen generiek gekozen"
        | InProgress -> "## Doseringen worden opgehaald ..."
        | Resolved s -> s.Trim()
        |> markdown.source
        |> List.singleton
        |> List.append [ markdown.escapeHtml false ]
        |> Markdown.markdown
    Mui.themeProvider
        [ themeProvider.theme defaultTheme
          themeProvider.children
              [ Html.div
                  [ [ Fable.MaterialUI.Icons.menuIcon "", (fun _ -> ToggleEdit |> dispatch) ]
                    |> Components.TitleBar.render (state.Versions |> printTitle)

                    if state.EditView then 
                        Html.div [ 
                            prop.style [ style.marginTop 100 ]
                            prop.text "Edit view" 
                        ]
                    else 
                        Mui.container
                            [ prop.style
                                [ style.marginTop 90
                                  style.padding 10 ]
                              container.maxWidth.md
                              prop.children
                                  [ // search
                                    Html.div
                                        [ prop.style [ style.padding 10 ]
                                          paper.children filter ]
                                    match state.Generics with
                                    | Resolved _ ->
                                        // details
                                        Mui.paper
                                            [ prop.style
                                                [ style.padding 10
                                                  style.color Colors.indigo.``900`` ]
                                              prop.children [ details ] ]
                                    | _ -> Html.none ] ] ] ] ]

This indentation however has a couple of problems.

  1. It is very hard to see which are the different elements in the code.
  2. When you indent pieces of this code, it quickly results in hard to debug indentation errors.

This is because of the following indentation structure:

    Mui.themeProvider [ themeProvider.theme defaultTheme
                        themeProvider.children [ Html.div [ [ Fable.MaterialUI.Icons.menuIcon "",
                                                              (fun _ -> ToggleEdit |> dispatch) ]
                                                            |> Components.TitleBar.render (state.Versions |> printTitle) ] ] ]

So every list argument starts at the same line as the list opening: i.e.

// fantomas formatted
Html.div [ prop.style [ style.marginTop 40 ]
           prop.children [ Mui.typography [ prop.text "the fantomas way" ] ] ]
// easier to extract and indent
Html.div [ 
    prop.style [
        style.marginTop 40
    ]
    prop.children [
        Mui.typography [
            prop.text "easier to extract and indent"
        ]
    ]
]

When you reformat the original code to:

let render (state: State) (dispatch: Msg -> unit) =
    let filter = createFilter state.Generics state.Indications state.Routes state.Patients dispatch

    let details =
        match state.Details with
        | HasNotStartedYet -> "## Geen generiek gekozen"
        | InProgress -> "## Doseringen worden opgehaald ..."
        | Resolved s -> s.Trim()
        |> markdown.source
        |> List.singleton
        |> List.append [ markdown.escapeHtml false ]
        |> Markdown.markdown

    Mui.themeProvider [ 
            themeProvider.theme defaultTheme
            themeProvider.children [ 
                Html.div [ 
                    [ 
                        Fable.MaterialUI.Icons.menuIcon "", (fun _ -> ToggleEdit |> dispatch) 
                    ]
                    |> Components.TitleBar.render (state.Versions |> printTitle)

                    if state.EditView then 
                        Html.div [ 
                            prop.style [ style.marginTop 100 ]
                            prop.text "Edit view" 
                        ]
                    else 
                        Mui.container [ 
                            prop.style [ 
                                style.marginTop 90
                                style.padding 10 
                            ]
                            container.maxWidth.md
                            prop.children [ 
                                // search
                                Html.div [ 
                                    prop.style [ style.padding 10 ]
                                    paper.children filter 
                                ]

                                match state.Generics with
                                | Resolved _ ->
                                    // details
                                    Mui.paper [ 
                                        prop.style [ 
                                            style.padding 10
                                            style.color Colors.indigo.``900`` 
                                        ]
                                        prop.children [ details ] 
                                    ]
                                | _ -> Html.none 
                            ] 
                        ] 
                ] 
            ] 
        ]

This is easier to read and identify the different elements. Also, when you need to add indentation, because for example you want to wrap things in an additional div, this is far easier to do.

Even the editor view is much nicer as it allows you to collapse each individual element:

With the right indentation formatting, you can collapse each different element.

Leave a Reply

Your email address will not be published. Required fields are marked *