Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

feature: Added formatting for self-closing JSX element #2273

Merged
merged 10 commits into from
Apr 12, 2022

Conversation

NicholasLYang
Copy link
Contributor

@NicholasLYang NicholasLYang commented Mar 21, 2022

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 from rome_playground

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 21, 2022

Deploying with  Cloudflare Pages  Cloudflare Pages

Latest commit: c3e4e31
Status: ✅  Deploy successful!
Preview URL: https://93509e6a.tools-8rn.pages.dev

View logs

@NicholasLYang NicholasLYang marked this pull request as ready for review March 22, 2022 14:20
@MichaReiser
Copy link
Contributor

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?

@NicholasLYang
Copy link
Contributor Author

Do we want a proper folder structure for JSX tests? I can have it mirror the JSX formatter directory structure

Copy link
Contributor

@ematipico ematipico left a 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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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

Comment on lines 5 to 28
<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",
}}
/>
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Comment on lines 77 to 78
"Foo " => 1..5
"Foo " => 11..25
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

@ematipico ematipico Mar 23, 2022

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

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 don't handle any formatting of JsxAnyElementName. Should I?

Copy link
Contributor

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

@ematipico
Copy link
Contributor

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.

@NicholasLYang NicholasLYang requested a review from ematipico March 23, 2022 17:15
Comment on lines 12 to 15
for attribute in self.attributes() {
attributes.push(attribute.format(formatter)?);
attributes.push(soft_line_break_or_space());
}
Copy link
Contributor

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

Suggested change
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());

Copy link
Contributor Author

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.

Copy link
Contributor

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.

@NicholasLYang NicholasLYang force-pushed the feature/jsx-self-closing-element branch from 904c0bc to b4a36c6 Compare April 5, 2022 16:38
@github-actions
Copy link

github-actions bot commented Apr 5, 2022

Parser conformance results on ubuntu-latest

js/262

Test result main count This PR count Difference
Total 45250 45250 0
Passed 44310 44310 0
Failed 940 940 0
Panics 0 0 0
Coverage 97.92% 97.92% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 39 39 0
Passed 36 36 0
Failed 3 3 0
Panics 0 0 0
Coverage 92.31% 92.31% 0.00%

ts/babel

Test result main count This PR count Difference
Total 584 584 0
Passed 508 508 0
Failed 76 76 0
Panics 0 0 0
Coverage 86.99% 86.99% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 15983 15983 0
Passed 12167 12167 0
Failed 3816 3816 0
Panics 0 0 0
Coverage 76.12% 76.12% 0.00%

Comment on lines 44 to 49
"Foo " => 1..5
"Foo " => 11..25
"LineWidthInput " => 31..46
"IndentStyleSelect" => 51..68
"SourceTypeSelect" => 75..91
"CodeEditor" => 99..109
Copy link
Contributor

@ematipico ematipico Apr 11, 2022

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;

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 thought we had agreed that JsxName formatting was out of scope. But no worries, implemented it

@NicholasLYang NicholasLYang force-pushed the feature/jsx-self-closing-element branch from 71490d7 to e4197d7 Compare April 12, 2022 14:06
Comment on lines 11 to 15
let mut attributes = Vec::new();
for attribute in self.attributes() {
attributes.push(attribute.format(formatter)?);
attributes.push(soft_line_break_or_space());
}
Copy link
Contributor

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.

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 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.

@NicholasLYang NicholasLYang merged commit 9f6312b into main Apr 12, 2022
@NicholasLYang NicholasLYang deleted the feature/jsx-self-closing-element branch April 12, 2022 14:47
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants