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

feat(js_formatter): provide the JS formatter with the ability to format other languages #3403

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

ah-yu
Copy link
Contributor

@ah-yu ah-yu commented Jul 10, 2024

Summary

Part of #3334

Now you can use f.context().get_foreign_language_formatter().format(Css, "") to format CSS code

Test Plan

CI should pass

@github-actions github-actions bot added A-Project Area: project A-Formatter Area: formatter A-Tooling Area: internal tools L-JavaScript Language: JavaScript and super languages labels Jul 10, 2024
Copy link

codspeed-hq bot commented Jul 10, 2024

CodSpeed Performance Report

Merging #3403 will not alter performance

Comparing ah-yu:feat/embed-api (94bd39b) with main (5b7d158)

Summary

✅ 99 untouched benchmarks

Comment on lines +32 to +34
biome_css_formatter = { path = "../biome_css_formatter" }
biome_css_parser = { path = "../biome_css_parser" }
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if it could be a circular dependency problem at some point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, I dont know. I'm not familiar with cargo. Does a dev dependency participate in the build phase? If there is a cycle, will cargo report it?

Copy link
Member

@ematipico ematipico Jul 10, 2024

Choose a reason for hiding this comment

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

Cycles aren't allowed, and rust won't compile until it's resolved.

As for dev deps, I believe that's the same because the code is compiled, even for tests.

We have options:

  • move tests inside biome_service
  • use the workspace

The first option is maybe the best one. I would also argue that embedded languages are an esoteric area and they should not be part of the testing of a specific language - in this example, the JavaScript language

Copy link
Contributor Author

@ah-yu ah-yu Jul 10, 2024

Choose a reason for hiding this comment

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

Cycles aren't allowed, and rust won't compile until it's resolved.

Thank you! TIL

The first option is maybe the best one

Agree

Copy link
Contributor

Choose a reason for hiding this comment

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

The more I think about it, the more I am inclined to believe that it may be okay to have CSS dependencies in the dev dependencies for tests? It's simpler and prevents the JS formatter tests from being spread across the project. If we move it to another crate, how will we collect statistics about Prettier compatibility?

@@ -44,12 +45,19 @@ pub struct JsFormatContext {
cached_function_body: Option<(AnyJsFunctionBody, FormatElement)>,

source_map: Option<TransformSourceMap>,

foreign_language_formatter: Rc<dyn JsForeignLanguageFormatter>,
Copy link
Contributor

@denbezrukov denbezrukov Jul 10, 2024

Choose a reason for hiding this comment

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

I'm just curious why Rc<dyn> is a better choice over other options?

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 didn't think about it deeply and just followed the comments. Any better suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I also noticed the comments and couldn't find an explanation.
Unfortunately, I don't have any suggestions about it.
There are several options:

  • Have a <T> and have a constraint over T in the impls.
  • Box<dyn>?

Copy link
Contributor Author

@ah-yu ah-yu Jul 10, 2024

Choose a reason for hiding this comment

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

Oh, sorry, you mean Rc<dyn>. I thought you were asking why use Rc instead of another smart pointer.

Initially, I wanted to use a closure, but I encountered a problem with deriving the Clone trait, so I chose to use Rc<dyn> instead. Rust is hard 😢

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are several options:

Have a and have a constraint over T in the impls.
Box?

Thank you! I will think about it!

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I got it. Indeed, if we need the Clone trait, we can't use Box :(
It may be fine to use Rc.

Copy link
Contributor Author

@ah-yu ah-yu Jul 11, 2024

Choose a reason for hiding this comment

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

It seems dyn has trade-offs.

Still need to think about it, convert this PR to a draft temporarily

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have a T and have a constraint over T in the impls.

If we attach a generic type to JsFormatContext, we also need to attach a generic type to many other types associated with JsFormatContext, for example FormatJsSyntaxToken.

The rust book mentions that dyn has runtime cost but I have no idea how much the cost it is. Wonder if the cost is acceptable while keeping the code flexible

This comment was marked as off-topic.

Copy link
Contributor

Choose a reason for hiding this comment

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

The rust book mentions that dyn has runtime cost but I have no idea how much the cost it is. Wonder if the cost is acceptable while keeping the code flexible

It looks like the overhead will be totally acceptable in this case, so I’d just stick with the Rc<dyn>. After all, it’s a single pointer to the formatter that should be used, but it shouldn’t affect the performance of the formatter itself.

Generally in Rust, don’t worry too much about performance unless you’re making clones all over the place or something is becoming noticeably slow :)

@ah-yu ah-yu marked this pull request as draft July 11, 2024 02:30
@dyc3
Copy link
Contributor

dyc3 commented Oct 15, 2024

This is great to see. If this is merged by the time I get back from vacation, I'll replicate this approach for HTML.

@ah-yu
Copy link
Contributor Author

ah-yu commented Oct 15, 2024

This is great to see. If this is merged by the time I get back from vacation, I'll replicate this approach for HTML.

Good to know! I still need some time to explore whether there is a better implementation. Any suggestions are welcome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Formatter Area: formatter A-Project Area: project A-Tooling Area: internal tools L-JavaScript Language: JavaScript and super languages
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants