Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix nested values inside anonymous records and types not displaying properly in stroustrup style #2687

Merged
merged 8 commits into from
Jan 6, 2023

Conversation

josh-degraw
Copy link
Contributor

Closes #2413

Copy link
Contributor

@nojaf nojaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Josh, thank you so much for this PR. I really appreciate you working on this. Many people want Stroustrup and you are the only one making a real difference in this 🎉.

Praise aside, I'm afraid I negatively reviewed this PR.
Overall the required changes to pull this off are very scope to the junction of identifier, token (:/=) and body (expr/type). So I would really like to see only changes in those areas.

We don't apply the boy scout rule here. In a couple of months from now, something (which we didn't cover with a unit test, unfortunately) can break. When retracing our steps, it helps a bunch when the relevant PRs are small and focused.

That being said, I'm not against cleaning up the code. But if prefer it to happen as separate PRs (for example #2678), so we can easily detect if those changes introduced a problem or not.

I also noticed these changes don't cover the regular record instance:

[<Test>]
let ``regular records`` () =
    formatSourceString
        false
        """
let foo = {
    Data =
        {
            Name = "Isaac"
            Age = 43
            Day = "Monday"
            Colours =
                [
                    "Red"
                    "Blue"
                    "White"
                    "Orange"
                    "Red"
                    "Blue"
                    "White"
                    "Orange"
                    "Red"
                    "Blue"
                    "White"
                    "Orange"
                    "Red"
                    "Blue"
                    "White"
                    "Orange"
                ]
        }
}
"""
        config
    |> prepend newline
    |> should
        equal
        """
let foo = {
    Data = {
        Name = "Isaac"
        Age = 43
        Day = "Monday"
        Colours = [
            "Red"
            "Blue"
            "White"
            "Orange"
            "Red"
            "Blue"
            "White"
            "Orange"
            "Red"
            "Blue"
            "White"
            "Orange"
            "Red"
            "Blue"
            "White"
            "Orange"
        ]
    }
}
"""

it seems like a good opportunity to include those as well.

To move forward I would revert the changes to Fantomas.Core and reintroduce them one by one until the tests pass again.

@@ -3032,8 +3032,8 @@ let genType (t: Type) =
| None -> sepOpenAnonRecdFixed +> addSpaceIfSpaceAroundDelimiter
| Some n -> genSingleTextNode n +> addSpaceIfSpaceAroundDelimiter

let genAnonRecordFieldType (i, t) =
genSingleTextNode i
let genAnonRecordFieldType (textNode, t) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick but i stood for identifier.
textNode no longer really carries that semantic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, I had no idea what it was supposed to stand for so I assumed it was leftover as like an iterator var name and renamed it to indicate the type of the value. I can change it back if you'd like tho

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change it to identifier or identifierNode. textNode was a tad misleading.

+> indentSepNlnUnindent (atCurrentColumnIndent genFields)
let genAnonRecordFields = col sepNln node.Fields genAnonRecordFieldType

handleBracketStyle (function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really a fan of introducing a new helper function with a single usage.
Especially when this usage is essentially doing the same as ifAlignOrStroustrupBrackets.

In general, please try to reduce your PR to make the smallest amount of changes.
Before committing, circle back to everything and reflect on whether each change is really necessary to make your point.

Copy link
Contributor Author

@josh-degraw josh-degraw Jan 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah sorry, originally I had used this in more than one place, and did slightly more than just match on the bracket type, but I guess I removed the other usage without realizing it haha


fun (ctx: Context) ->
let size = getRecordSize ctx node.Fields

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also don't introduce these additional newlines.
Fantomas will combine two single lines of code without an additional new line.
That is the heuristic and we wish to respect that.

By adding the extra newline you took a stylistic decision that the previous developer on the same team didn't make. We use a code formatter to remove every personal touch any developer might make, so please keep an eye out for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure if it's the case here but I noticed that when I ran the FormatAll build command it added a bunch of newlines for me. I thought I removed them but I guess not. Not sure why it would have done that

let multilineExpression (ctx: Context) =
let noMembers = List.isEmpty members
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't make any changes whatsoever to TypeDefn.Record in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, leftover from trying to find the source of the issue 😅. Will change back

@@ -3458,7 +3458,10 @@ let genField (node: FieldNode) =
| Some name ->
genSingleTextNode name
+> sepColon
+> autoIndentAndNlnIfExpressionExceedsPageWidth (genType node.Type))
+> ifElseCtx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to introduce a new function similar to autoIndentAndNlnExpressUnlessStroustrup but which takes a Type instead of an Expr. Accepting any single Type will eventually lead to invalid code.

Example:

type MangaDexAtHomeResponse = {
    baseUrl: string
    chapter: {|
            hash: string
            data: string[]
            otherThing: int
        |}
        ->
            // comment
            (int -> int) -> {|
            hash: string
            data: string[]
            otherThing: int
        |}
}

This is an extreme example of course but it would only be a matter of time before someone encounters some problem in the wild.

The type needs to be formatted like this:

            {|
                hash: string
                data: string[]
                otherThing: int
            |}
                ->
                // comment
                (int -> int)
                -> {|
                    hash: string
                    data: string[]
                    otherThing: int
                |}

in order to remain valid.

@josh-degraw
Copy link
Contributor Author

We don't apply the boy scout rule here. In a couple of months from now, something (which we didn't cover with a unit test, unfortunately) can break. When retracing our steps, it helps a bunch when the relevant PRs are small and focused.

No worries, that's a habit I've gotten into after working on lots of legacy systems that notoriously never went back to address tech debt, but I can have a tendency to get a little carried away with that 😅 I know it's important to isolate changes as much as possible so no offense taken haha. I'll clean it up.

@nojaf
Copy link
Contributor

nojaf commented Jan 5, 2023

Thanks 😊

@josh-degraw
Copy link
Contributor Author

I also noticed these changes don't cover the regular record instance:
...
it seems like a good opportunity to include those as well.

Good catch, I didn't even realize those weren't already handled correctly! Got those fixed up.

Copy link
Contributor

@nojaf nojaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for this PR!

@nojaf nojaf merged commit 9d74413 into fsprojects:main Jan 6, 2023
@josh-degraw josh-degraw deleted the nested-anon-records branch January 6, 2023 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

fsharp_experimental_stroustrup_style=true breaks on types with nested anonymous records
2 participants