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

TypeScript: format TSAsExpression with same logic as BinaryExpression #6450

Closed
wants to merge 3 commits into from

Conversation

kaicataldo
Copy link
Member

@kaicataldo kaicataldo commented Sep 4, 2019

fixes #6443.

I took a stab at working on wrapping TSAsExpression nodes! In taking a look at this, it looks like the behavior of moving the initial declaration value up to the same line as the = operator happens with other declarations and I think the change might have to happen in that logic (here's an example using the Prettier playground).

This PR instead focuses on wrapping the TSAsExpression node correctly (ensuring that the line break occurs after the as operator).

This now takes a different approach and converts the TSAsExpression node to match the BinaryExpression node interface so we can use the same formatting logic for both.

I'm pretty new to the Prettier codebase, and any and all guidance is very welcome!

  • I’ve added tests to confirm my change works.
  • (If changing the API or CLI) I’ve documented the changes I’ve made (in the docs/ directory)
  • (If the change is user-facing) I’ve added my changes to the CHANGELOG.unreleased.md file following the template.
  • I’ve read the contributing guidelines.

Try the playground for this PR

Copy link
Member

@alexander-akait alexander-akait left a comment

Choose a reason for hiding this comment

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

Need better check, regexp is bad in this case

src/language-js/printer-estree.js Outdated Show resolved Hide resolved
@kaicataldo kaicataldo force-pushed the wrap-ts-as-expression branch 2 times, most recently from 3086b23 to 0d27d3f Compare September 6, 2019 00:32
@kaicataldo
Copy link
Member Author

After thinking about this some more, I took a completely different approach. It feels like we should treat as operators similarly to any other binary operator. By postprocessing the TSAsExpression to match the interface of a BinaryExpression, we can use the exact same logic. Thoughts?

@kaicataldo kaicataldo changed the title TypeScript: wrap TSAsExpression nodes TypeScript: format TSAsExpression with same logic as BinaryExpression Sep 6, 2019
@kaicataldo kaicataldo force-pushed the wrap-ts-as-expression branch 3 times, most recently from fbcd486 to 8e752db Compare September 6, 2019 02:14
@kaicataldo
Copy link
Member Author

I'm having trouble recreating these test failures locally. Any idea what might be going on? I'm running them on Node v10.16.3 in macOS v10.14.6.

case "VariableDeclaration": {
const lastDeclaration = getLast(node.declarations);
if (lastDeclaration && lastDeclaration.init) {
overrideLocEnd(node, lastDeclaration);
}
break;
}
case "TSAsExpression": {
convertTSAsExpression(node);
Copy link
Member

Choose a reason for hiding this comment

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

Feels pretty hacky to me, but I'm not sure how to do it better. There's quite a bit of code for printing binary expressions. On the other hand – do as expressions really need to go through all of the binary expression stuff, like the JSX related parts? Maybe there’s a way to extract or copy the parts needed for as?

@vjeux You have had useful insight before on PRs with hacky code – what does your experience tell you here (gut feeling)?

Copy link
Member Author

@kaicataldo kaicataldo Sep 6, 2019

Choose a reason for hiding this comment

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

Maybe there’s a way to extract or copy the parts needed for as?

My thought here was that keeping the two in sync would be a bigger maintenance burden, but if that's the preferred way of doing things, I can definitely look into it!

I'm also coming from working on babel-eslint, where the parser has to do a lot of massaging of the AST to get it to work with ESLint. 😅

Copy link
Member

@thorn0 thorn0 Sep 6, 2019

Choose a reason for hiding this comment

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

Does it modify the passed AST itself or a copy of it? What if someone else wanted to use that AST after Prettier? Or is such a scenario already unsupported anyway?

Copy link
Member

Choose a reason for hiding this comment

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

do as expressions really need to go through all of the binary expression stuff, like the JSX related parts

Why not? as can be used anywhere, just like + or say &&,

Copy link
Member Author

Choose a reason for hiding this comment

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

Happy to work on this again once the team decides what the preferred approach is!

Copy link
Member

Choose a reason for hiding this comment

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

Another case of aligning the formatting of as with how other binary expressions are formatted: #6471

@thorn0
Copy link
Member

thorn0 commented Sep 6, 2019

I'm having trouble recreating these test failures locally. Any idea what might be going on? I'm running them on Node v10.16.3 in macOS v10.14.6.

Do you run the tests with AST_COMPARE=1?

@kaicataldo
Copy link
Member Author

@thorn0 Ah, thanks! That's what I was missing!

@kaicataldo
Copy link
Member Author

Friendly ping for the Prettier team - any insight on how we'd like to proceed here?

@alexander-akait
Copy link
Member

@kaicataldo need fix CI problems

@thorn0
Copy link
Member

thorn0 commented Sep 23, 2019

I don't represent the Prettier team, but I expect them to support the following points:

  1. formatting as-expressions like binary expressions makes total sense, but
  2. modifying the AST is not okay (e.g. see this case).

@kaicataldo
Copy link
Member Author

@thorn0 @lydell Thanks, that should unblock me. I'll get to this ASAP!

@kaicataldo
Copy link
Member Author

I'm revisiting this tonight, and it looks like our options are either to:

  1. Clone the node and modify it to match the BinaryExpression interface before printing.
  2. Copy over the current printing BinaryExpression logic and modify it to work with the current TSAsExpression node interface.

The challenge of the first strategy is that we have to convert the TSAsExpression node every time it might exist (which is lots of places! e.g. here), which adds a lot of complexity throughout the printer.

The challenge of the latter is that we'll have a fork in that logic and will have to make future changes in both code paths. This feels more feasible, but still doesn't feel great to me.

Thoughts?

@thorn0
Copy link
Member

thorn0 commented Sep 25, 2019

Might it make sense to replace all occurrences of .operator, .left and .right with function calls (getOperator, etc)?

@duailibe
Copy link
Member

@kaicataldo I think we should start with option 2 and then work from that to minimize the problems with forking later

@thorn0
Copy link
Member

thorn0 commented Oct 8, 2019

On the playground for this PR, I see that as is not formatted like other binaries (e.g. + or **), at least in this specific case (from #6623):

Prettier pr-6450
Playground link

--parser typescript

Input:

const iter1 = createIterator(this.controller, child, this.tag as SyncFunctionComponent);
const iter2 = createIterator(self.controller, child, self.tag as SyncFunctionComponent);

const iter1 = createIterator(this.controller, child, this.tag ** SyncFunctionComponent);
const iter2 = createIterator(self.controller, child, self.tag ** SyncFunctionComponent);

Output:

const iter1 = createIterator(this.controller, child, this.tag as
  SyncFunctionComponent);
const iter2 = createIterator(self.controller, child, self.tag as
  SyncFunctionComponent);

const iter1 = createIterator(
  this.controller,
  child,
  this.tag ** SyncFunctionComponent
);
const iter2 = createIterator(
  self.controller,
  child,
  self.tag ** SyncFunctionComponent
);

@kaicataldo
Copy link
Member Author

Sorry, haven’t been able to get back to this. Will close this for now so that someone else can work on it.

@kaicataldo kaicataldo closed this Nov 12, 2019
@lock lock bot added the locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting. label Feb 10, 2020
@lock lock bot locked as resolved and limited conversation to collaborators Feb 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TypeScript: type assertions (as) aren't wrapped
5 participants