-
Notifications
You must be signed in to change notification settings - Fork 656
feature: Added formatting for self-closing JSX element #2273
Conversation
Deploying with
|
Latest commit: |
c3e4e31
|
Status: | ✅ Deploy successful! |
Preview URL: | https://93509e6a.tools-8rn.pages.dev |
Nit: Would you mind filling out the PR template. You can go into some more detail. Like does it support formatting props too? Or just empty self closing elements? How did you test it? Did you add new test cases? |
Do we want a proper folder structure for JSX tests? I can have it mirror the JSX formatter directory structure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The snapshot shows that there are a lot of unimplemented nodes (they still format verbatim).
/>; | ||
|
||
|
||
## Unimplemented nodes/tokens |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the snapshot has this section, it means that we are not formatting some tokens/nodes. This means that a test should be valid if it doesn't have this section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this was on purpose because I didn't want to go down the rabbit hole of attribute/name formatting. If this is necessary I can put this PR on hold and implement all of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think going step by step is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, we don't need to format everything in one go. Let's just focus on the small feature and gradually add things along the way. Simply formatting <Foo />
is just fine
<LineWidthInput lineWidth={lineWidth} setLineWidth={setLineWidth} />; | ||
<IndentStyleSelect indentWidth={indentWidth} setIndentWidth={setIndentWidth} indentStyle={indentStyle} setIndentStyle={setIndentStyle} | ||
/>; | ||
<SourceTypeSelect | ||
isTypeScript={isTypeScript} | ||
|
||
setIsTypeScript={setIsTypeScript} | ||
|
||
isJsx={isJsx} | ||
|
||
setIsJsx={setIsJsx} | ||
/>; | ||
|
||
<CodeEditor | ||
value={code} | ||
language="js" | ||
placeholder="Enter JS here" | ||
onChange={(evn) => { | ||
setCode(evn.target.value); | ||
}} | ||
style={{ fontSize: 12, height: "100vh", fontFamily: | ||
"ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace", | ||
}} | ||
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the PR should be focusing only on self closing tags, I would suggest to remove everything around attributes, which are out of scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well the formatting does include how we place attributes, so it is important that we have tests with attributes. Should I remove the non trivial ones then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but the PR is exclusively about self closing tags, and having formatting for attributes can be done with a different PR without dependencies, that's why I created a task just for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm okay. I was mostly testing this code namely the soft line breaks around attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can iterate again over the line breaks when we will implement the format of attributes.
You can leave the implementation as it is, the most important part is that the test doesn't have to have JSXAttributes.
"Foo " => 1..5 | ||
"Foo " => 11..25 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if you noticed, but Foo
is not actually formatted, (it formatted via format_verbatim
), which means it's not correct.
Probably we have to implement JsxText
node.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I'm confused here because it does correctly format <Foo />;
as <Foo />
. Perhaps it's just printing the name part (which is purposefully not formatted) with trivia?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason why the second Foo
have the spaces removed, is because we actually trim the tokens out from the verbatim tokens (the first leading and the first trailing), and by chance the first trailing trivia is a white space. You could try to insert some new lines and could see the different.
Anyway, if you check the AST:
tag: JsxSelfClosingElement {
l_angle_token: [email protected] "<" [] [],
name: JsxReferenceIdentifier {
value_token: [email protected] "Foo" [] [Whitespace(" ")],
},
type_arguments: missing (optional),
attributes: JsxAttributeList [],
slash_token: [email protected] "/" [] [],
r_angle_token: [email protected] ">" [] [],
},
name
is a JsxReferenceIdentifier
which is still formatting verbatim
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't handle any formatting of JsxAnyElementName. Should I?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah you really should, at least to cover the small test of your PR
You can check this section: https://github.com/rome/tools/blob/main/crates/rome_formatter/docs/implement_the_formatter.md to make sure to understand when a test is correct. |
for attribute in self.attributes() { | ||
attributes.push(attribute.format(formatter)?); | ||
attributes.push(soft_line_break_or_space()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use join_elements
for this together with format_nodes
for attribute in self.attributes() { | |
attributes.push(attribute.format(formatter)?); | |
attributes.push(soft_line_break_or_space()); | |
} | |
let attributes = join_elements(soft_line_break_or_space(), formatter.format_nodes(self.attributes()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm so I switched to this with a tweak:
let mut attributes = Vec::new();
for attribute in self.attributes() {
attributes.push(attribute.format(formatter)?);
attributes.push(soft_line_break_or_space());
}
But then for some reason the lines always became a hard line break. I don't really know why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible, we should avoid creating new vectors. join_elements
uses concat_elements
under the hoods, which is really optimized around the usage of new vectors.
904c0bc
to
b4a36c6
Compare
Parser conformance results on ubuntu-latestjs/262
jsx/babel
ts/babel
ts/microsoft
|
"Foo " => 1..5 | ||
"Foo " => 11..25 | ||
"LineWidthInput " => 31..46 | ||
"IndentStyleSelect" => 51..68 | ||
"SourceTypeSelect" => 75..91 | ||
"CodeEditor" => 99..109 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The presence of these information in the snapshot means that we haven't implemented these nodes: https://github.com/rome/tools/blob/main/xtask/codegen/js.ungram#L2202-L2203 (they still format verbatim)
As general rule, if we have this information, we consider the test as invalid: https://github.com/rome/tools/blob/main/crates/rome_js_formatter/docs/write_tests.md#identify-issues
some tokens haven't been printed; usually you will have this information inside the snapshot, under a section called "Unimplemeted tokens/nodes"; a test, in order to be valid, can't have that section;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we had agreed that JsxName formatting was out of scope. But no worries, implemented it
71490d7
to
e4197d7
Compare
let mut attributes = Vec::new(); | ||
for attribute in self.attributes() { | ||
attributes.push(attribute.format(formatter)?); | ||
attributes.push(soft_line_break_or_space()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use join_elements(formatter.format_nodes(self.attributes())?, soft_line_break_or_space())
to get the same result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the weirdest issue with that last time where it added line breaks when it shouldn't have. No clue why, but switched to it now.
Summary
Formats JSX self-closing elements (#2272). Doesn't implement formatting for attributes or element names.
Test Plan
Added test file
self_closing.jsx
with some examples, some of which are pulled fromrome_playground