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

Preserve function body braces and distinguish open vs. X.() #1826

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jordwalke
Copy link
Member

@jordwalke jordwalke commented Feb 16, 2018

Summary:Created a new place to store some stylistic information in the
AST. [@refmt "foo"] attributes used at parse time, never printed.

Before the following would get reformatted:

let f () => {
  switch(x) {
  | X => 0
  };
};

To this:

let f () =>
  switch(x) {
  | X => 0
  };

That was annoying to many people because you can't easily add a "let" binding above the switch.
After this diff, the original form will be preserved:

let f () => {
  switch(x) {
  | X => 0
  };
};

With this diff you can still have the printer print this form if you prefer: You simply write it in this form and it will be retained.

let f () =>
  switch(x) {
  | X => 0
  };

I consider this a "pretty good" solution to making editing Reason syntax more enjoyable, but is certainly not the best possible solution. Ideally we would automatically insert the braces depending on whether or not the body of the function "breaks". We can also (or even instead) do that when our formatter is more expressive.

Before the following would get reformatted:

let f () => {
  open X;
  switch(x) {
  | X => 0
  };
};

To this:

let f () =>
  X.(
    switch(x) {
    | X => 0
    }
  );

Which is sometimes neat, but sometimes it gets pretty unwieldy especially with nested opens. With this diff, we preserve the original way you wrote the open. You can write it with open X or X.() and the formatter will retain the form you wrote. It's pretty hard to write an algorithm that looks great in all cases.

Summary:Created a new place to store some stylistic information in the
AST. `[@refmt "foo"]` attributes used at parse time, never printed.

Test Plan:

Reviewers:

CC:
@jordwalke
Copy link
Member Author

jordwalke commented Feb 16, 2018

Some people that have requested this: @sgrove, @kyldvs

let add = a => [@onRet] a;
let add = a =>
[@onRet]
{
Copy link
Member

Choose a reason for hiding this comment

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

What's happening here

Copy link
Member Author

@jordwalke jordwalke Feb 16, 2018

Choose a reason for hiding this comment

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

The input file actually has braces. The old test output was stripping the braces and now this diff preserves them.
This is what the input has:

let add(a) { [@onRet] a };

It doesn't distinguish between [@a] { x } and { [@a] x } as you can see.

[@bs.get null] key2: [@onType2] Js.t(int),
[@bs.get null] key3: [@onType2] Js.t(int),
[@bs.get
{
Copy link
Member

Choose a reason for hiding this comment

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

What's happening here then?

Copy link
Member Author

Choose a reason for hiding this comment

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

The same thing. The attributes were specified with braces, and before this diff they were getting refmted away.

Copy link
Member

Choose a reason for hiding this comment

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

Does that mean these will always get formatted like this now or something?

Copy link

Choose a reason for hiding this comment

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

If you remove the braces I guess they will get formatted how they were previously.

Copy link
Member Author

@jordwalke jordwalke Feb 19, 2018

Choose a reason for hiding this comment

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

Exactly. Previously, the braces were getting formatted away. Now, we preserve whether or not you included additional braces. You can have it format whichever way you want - and it will preserve your intent.

@TheSpyder
Copy link

While I really like the local open change, I’m less sold on retaining optional braces. I usually like the way simplifying a function can lead to the braces becoming unnecessary and collapse the code; often into a single line.

Although I guess it’s easy to argue both sides. This does make me think that reducing how opinionated refmt is means we start needing some options to control behaviour 🤔

@jordwalke
Copy link
Member Author

The open brace issue is pretty important because they get removed before you have the opportunity to add a let binding before the return value. Today people already do force the braces to be preserved by adding a dummy let binding to unit. That’s pretty extreme. What this diff does is just makes it more natural to tell the formatter you want them preserved.
However I think that if we had the ability to only print braces when the function body breaks, then this feature would be less useful and at that time perhaps we can revisit.

@kyldvs
Copy link

kyldvs commented Feb 20, 2018

I run into the bracket issue all the time, particularly because I'm used to a format-on-save workflow.

let foo = (foo) => {
  /* some placeholder */
};

Will format out the braces and I have to add them back constantly. Typically my workaround is to always add some empty unit statements to the function body like this:

let foo = (foo) => {
  ();
  /* some placeholder */
};

Really looking forward to this change

@sgrove
Copy link
Contributor

sgrove commented Feb 20, 2018

@kyldvs Literally my exact workflow! Looking forward to it as well.

@phated
Copy link

phated commented Jul 8, 2018

@jordwalke are you still planning to land this (or something similar)? I've been encountering both behaviors you outlined originally and it makes editing in vscode with formatOnSave: true very jarring. Often I'll habitually save, get one of those reformats when I planned on editing in the block and have to undo the format (which sometimes ends in a weird result).

@jaredly
Copy link
Contributor

jaredly commented Jul 19, 2018

This is the main reason I have refmt on save turned off. I might try to get to this to resolve the conflicts if I can in the next couple weeks

@jordwalke
Copy link
Member Author

jordwalke commented Jul 20, 2018

@jaredly Just an observation: I felt that the refactor included in this diff was really necessary to make it easy to retain these "preserved styles". I found the current approach in the printer/parser not really sustainable and kind of fragile. With my refactor in this stack, there's a specific place carved out just for stylistic preservation and it is clearly out of the way because it's its own category that is cleanly filtered out.

@facebook-github-bot
Copy link

Thank you for your pull request. We require contributors to sign our Contributor License Agreement, and yours has expired.

Before we can review or merge your code, we need you to email [email protected] with your details so we can update your status.

anmonteiro added a commit to anmonteiro/reason that referenced this pull request Oct 9, 2018
This is an alternative to reasonml#1826 implementing just the brace preservation
code, to make it easier to be reviewed.

I think this is now in a better place to get into the codebase given
that we have `Reason_attributes` outside of `Reason_pprint_ast` and that
we now remove our own attributes before printing to OCaml.

I think we can probably refactor this code further in future PRs, as
well as add other stylistic preservation changes.
anmonteiro added a commit to anmonteiro/reason that referenced this pull request Oct 9, 2018
This is an alternative to reasonml#1826 implementing just the brace preservation
code, to make it easier to be reviewed.

I think this is now in a better place to get into the codebase given
that we have `Reason_attributes` outside of `Reason_pprint_ast` and that
we now remove our own attributes before printing to OCaml.

I think we can probably refactor this code further in future PRs, as
well as add other stylistic preservation changes.
chenglou pushed a commit that referenced this pull request Nov 13, 2018
* Preserve function body braces

This is an alternative to #1826 implementing just the brace preservation
code, to make it easier to be reviewed.

I think this is now in a better place to get into the codebase given
that we have `Reason_attributes` outside of `Reason_pprint_ast` and that
we now remove our own attributes before printing to OCaml.

I think we can probably refactor this code further in future PRs, as
well as add other stylistic preservation changes.

* Rebase and fix

* address review comments

* Adding so many optional laballed to `unparseExprRecurse` can't scale
@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

4 similar comments
@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

@facebook-github-bot
Copy link

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants