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

challenge(formatter): add bracketSpacing option matching Prettier's setting #627

Merged
merged 9 commits into from Nov 19, 2023

Conversation

faultyserver
Copy link
Contributor

Summary

This PR adds a new bracketSpacing option for the js formatter which matches Prettier's equivalent option. This option controls whether spaces are inserted around "object-like literals", meaning object expressions, named import/export specifier lists, import assertions, and type expressions.

The following is a case that should cover pretty much every variation that this option affects:

import {a, b, c} from "foo" assert {type: "json"};

type X = {a: string};
type Y = {
  a: string;
};

const {a, b} = {a, b};
let {foo, bar, baz, zzz, andareallylongname} = {
  foo,
  bar,
  baz,
  zzz,
  andareallylongname,
};

export {a, b as car};

type Foo<A extends {type: string}> = A<{type: B}>;

With bracketSpacing: false, this code should appear unchanged after formatting.

By default, bracketSpacing is left as true, just like in Prettier, and users will have to explicitly disable it to see any changes from their current formatting.

The goal of adding this option is to ease adoption from existing Prettier users. While Biome does not (and I'm assuming won't) aim to have perfect parity with Prettier's features, bracket spacing is a commonly-set option, and one that causes a massive diff for large codebases.

Not much other discussion has happened on this option, other than this post from a little over a month ago: #392.

Implementation Notes

I'm both relatively new to Rust and completely new to this project, so I looked for inspiration on how to add an option by finding a previous addition, arrowParentheses, and copying over the plumbing, then implementing the feature, and finally adding the relevant tests.

I decided to use a plain bool value for the option rather than a string/enum only because that's what Prettier does. If a string value would be preferred, I'm happy to change it to match those as well.

Test Plan

cargo test with a bunch of new snapshots. A local playground link with the sample code above also demonstrates the behavior.

@github-actions github-actions bot added A-CLI Area: CLI A-Project Area: project A-Formatter Area: formatter A-Website Area: website L-JavaScript Language: JavaScript and super languages A-Changelog Area: changelog labels Oct 30, 2023
Comment on lines 634 to 629
pub fn conditional_space(should_insert: bool) -> Option<Space> {
if should_insert {
Some(Space)
} else {
None
}
}

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 added this conditional_space function just for clarity at the callsites. Not sure if it has too much utility outside of this option, but I like the "branchless" flow from the caller side while still making it clear that the space may or may not exist.

Copy link
Member

Choose a reason for hiding this comment

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

Instead of using conditional_space(cond) at call site, did you try cond.then_some(space())?

Copy link
Member

Choose a reason for hiding this comment

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

Instead of using conditional_space(cond) at call site, did you try cond.then_some(space())?

That wouldn't be very ergonomic when writing the IR, and it saves us a bunch of branching.

I can totally see the usage of this builder because our infra can understand Option. I would call this function maybe_space (sorry for the bike-shedding) because it's a prefix we already used through the code base,.

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 can definitely agree with maybe_*. Honestly idk why I didn't go with that in the first place since it's normally what I end up using lol.

I didn't actually know .then_some existed when I wrote this code (still very new to Rust), and had I known I might've used that instead. But I do like the declarative nature of saying maybe_ for things, along with the advantage of working with any boolean value, not just an Option.

PR should be updated with the name change now.

crates/biome_formatter/src/builders.rs Outdated Show resolved Hide resolved
@migueldaipre

This comment has been minimized.

@Conaclos
Copy link
Member

@faultyserver you can be interested by #720

@faultyserver faultyserver changed the title feat(formatter): add bracketSpacing option matching Prettier's setting challenge(formatter): add bracketSpacing option matching Prettier's setting Nov 15, 2023
@faultyserver
Copy link
Contributor Author

Happy to turn this into a challenge feature! I hadn't seen anything about whether there was a goal to support all of Prettier's options as part of the challenge or not.

@SuperchupuDev
Copy link
Member

@faultyserver FYI we just got confirmation that the challenge does aim for all prettier js options, after we asked for clarification

@github-actions github-actions bot added A-Linter Area: linter A-Tooling Area: internal tools labels Nov 18, 2023
@github-actions github-actions bot removed A-Linter Area: linter A-Tooling Area: internal tools labels Nov 18, 2023
@faultyserver
Copy link
Contributor Author

I ended up basically re-implementing the feature after trying to rebase off main and somehow ended up with 20 fewer files changed lol.

But I think this is now ready to go again pending CI.

Copy link
Member

@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 implementation looks good, and fortunately, this option doesn't add much friction in terms of maintenance.

I added some comments we should address before merging, although I am very pleased to see this contribution, thank you @faultyserver !

@@ -36,6 +36,10 @@ pub struct JavascriptFormatter {
#[bpaf(long("arrow-parentheses"), argument("always|as-needed"), optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub arrow_parentheses: Option<ArrowParentheses>,
/// Whether to insert spaces around brackets in object literals. Defaults to true.
#[bpaf(long("bracket-spacing"), argument("BOOLEAN"), optional)]
Copy link
Member

Choose a reason for hiding this comment

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

For arguments that are true/false, at the moment we use different implementation, e.g.

    #[bpaf(long("vcs-use-ignore-file"), argument("true|false"), optional)]
    pub use_ignore_file: Option<bool>,

We could change all of them to use BOOLEAN, but for now we should be more consistent. .

Comment on lines 634 to 629
pub fn conditional_space(should_insert: bool) -> Option<Space> {
if should_insert {
Some(Space)
} else {
None
}
}

Copy link
Member

Choose a reason for hiding this comment

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

Instead of using conditional_space(cond) at call site, did you try cond.then_some(space())?

That wouldn't be very ergonomic when writing the IR, and it saves us a bunch of branching.

I can totally see the usage of this builder because our infra can understand Option. I would call this function maybe_space (sorry for the bike-shedding) because it's a prefix we already used through the code base,.

/// # Ok(())
/// # }
/// ```
pub fn soft_block_indent_with_conditional_space<Context>(
Copy link
Member

Choose a reason for hiding this comment

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

Same as the previous suggestion: soft_block_indent_with_maybe_space

Copy link
Member

@SuperchupuDev SuperchupuDev left a comment

Choose a reason for hiding this comment

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

you forgot one minor detail, but other than that, looks good!

CHANGELOG.md Outdated Show resolved Hide resolved
Copy link
Member

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

Great! We just need to fix the conflicts with the new options we just added and we can merge the PR! Again, great work here

Copy link
Member

@SuperchupuDev SuperchupuDev left a comment

Choose a reason for hiding this comment

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

i can't really properly review a PR like this without my browser tab lagging but i'm approving to remove the previous requested changes review. great work! thanks a lot for contributing ❤

@faultyserver
Copy link
Contributor Author

faultyserver commented Nov 19, 2023

should hopefully be updated with no conflitcs now 🤞

@faultyserver
Copy link
Contributor Author

i should stop assuming before CI passes, but now finally it's all set.

@Conaclos Conaclos merged commit e60c49b into biomejs:main Nov 19, 2023
14 checks passed
@faultyserver faultyserver deleted the feature/bracket-spacing branch November 19, 2023 23:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Changelog Area: changelog A-CLI Area: CLI A-Formatter Area: formatter A-Project Area: project A-Website Area: website L-JavaScript Language: JavaScript and super languages
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants