fantomas icon indicating copy to clipboard operation
fantomas copied to clipboard

Idempotency problem when having a comment in an anonymous record and Stroustrup formatting

Open abelbraaksma opened this issue 2 years ago • 1 comments

Issue created from fantomas-online

Original code

let x = {|
    Y = 42
    // test
|}

Formatted code

let x = {|
    Y = 42
    // test

|}

Reformatted code

let x = {|
    Y = 42
    // test


|}

Problem description

Fantomas was not able to produce the same code after reformatting the result:

  • Having a comment on the last line of an anonymous record leads to an extra empty line being added each time you format
  • This is a relatively common scenario, i.e. using a comment temporarily to disable a certain field

Workaround

Just don't do this in code.

Extra information

  • [ ] ~The formatted result breaks my code.~
  • [ ] ~The formatted result gives compiler warnings.~
  • [x] I or my company would be willing to help fix this.
  • [x] Breaks Husky integration with precommit hook, the last line "walks down" on each format

Options

Fantomas main branch at 2022-10-08T06:02:14Z - b0eea044f22051d63093de52b73fdd0d7f1a32eb

    { config with
                MultilineBlockBracketsOnSameColumn = true
                ExperimentalStroustrupStyle = true }

Related example

A minor bug happens when Stroustrup is off. Note the comment being outdented and the closing |} bracket one space too far. Not sure it's related, see example without Stroustrup

Without Stroustrup example, before formatting

let x = {|
   Y = 42
   Z = "string"
   Foo = "Bar"
   // test
|}

Without Stroustrup after formatting

let x =
    {| Y = 42
       Z = "string"
       Foo = "Bar"
    // test
     |}

Did you know that you can ignore files when formatting from fantomas-tool or the FAKE targets by using a .fantomasignore file? YES 😃

abelbraaksma avatar Oct 08 '22 15:10 abelbraaksma

Hello, thanks for reporting this problem. The root problem is with fsharp_multiline_block_brackets_on_same_column=true.

SynExpr_AnonRecd_Field was chosen as the trivia anchor (content after). In case of a normal record the closing brace would be picked (content before).

The braces for anonymous records are not picked up as nodes.

Regular record:

https://github.com/fsprojects/fantomas/blob/4f4d646468d49f3307c47a7dc7261f36ee383576/src/Fantomas.Core/AstTransformer.fs#L257

Anon record:

https://github.com/fsprojects/fantomas/blob/4f4d646468d49f3307c47a7dc7261f36ee383576/src/Fantomas.Core/AstTransformer.fs#L268

The StartEndRange active pattern captures additional ranges for the tokens. (As the range starts and ends with these included).

In CodePrinter, trivia attached to the closing brace (in regular record instance) is taking into account the leading trivia:

https://github.com/fsprojects/fantomas/blob/4f4d646468d49f3307c47a7dc7261f36ee383576/src/Fantomas.Core/CodePrinter.fs#L2538

A similar thing could be achieved for anon records.

Are you interested in submitting a PR?

PS: As for the other problems, this is a known problem and won't be solved by fixing the idempotency issue.

nojaf avatar Oct 09 '22 07:10 nojaf

@dawedawe thanks for tackling this! You guys are too fast for me 😆.

abelbraaksma avatar Oct 24 '22 14:10 abelbraaksma