-
Notifications
You must be signed in to change notification settings - Fork 664
feat: add rome_flags crate for feature flags #2487
Conversation
Deploying with Cloudflare Pages
|
Parser conformance results on ubuntu-latestjs/262
jsx/babel
ts/babel
ts/microsoft
|
3c61cac
to
719510b
Compare
719510b
to
aa6e32f
Compare
crates/rome_features/src/lib.rs
Outdated
|
||
use once_cell::sync::OnceCell; | ||
|
||
static FLAGS: OnceCell<FeatureFlags> = OnceCell::new(); |
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.
This approach might not play nicely with the idea of a long-running Rome server because the feature flags can only be set once.
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 first idea that comes to mind to allow the set of enabled features to be modified at runtime would be to have the features declared as a bitflags!
and stored in a AtomicU*
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.
My two cents, I like the bitflags
approach. We use it in some other places of the code base, so it's a familiar pattern we already use.
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.
That’s a good approach for allowing the global state to be modified at runtime, but there’s still a question of whether or not global state is going to be compatible with our future architecture. If we have a long-running server that can be queried simultaneously by multiple CLI instances, then we don’t want each CLI invocation to be able to change the server's feature flags.
@leops Do you think it would be fine to stick with global state for now as a minimal solution, or will that cause us problems later if we need to move away from it? Also, if we switch to bitflags
, is there a good way to avoid having to manually include the bit values (1 << x
) for each flag?
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.
Sorry it took me some time to figure it out, but yes there is a way to let the compiler automatically generate a value for each flag by using the discriminant of an intermediate enum:
macro_rules! declare_feature_flags {
( $( $(#[doc = $doc:tt])* $feature:ident ),* )=> {
#[allow(non_camel_case_types)]
enum FlagValues {
$( $feature, )*
}
bitflags! {
#[derive(Default)]
/// State of all feature flags
pub struct FeatureFlags: u32 {
$( const $feature = 1 << FlagValues::$feature as u32; )*
}
}
};
}
Overall I think the way we handle global state is something we'll revisit when we do move to having a persistent daemon, so for the time being I'm not too worried about storing these in a global state (and maybe in the meantime the Rust language will have gained some interesting features to help with 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.
Thanks! For now, I'll merge this PR as-is so that I can unblock Ema's unstable formatting work. And then I'll follow up later with a PR to change the FeatureFlags
implementation.
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.
Thank you Yasser! I'd say we can polish up this PR and merge it, IMHO. It summarizes the discussion we had, and we can expand it later if we need.
crates/rome_cli/src/lib.rs
Outdated
// Must be a comma-separated list (no spaces) of features declared in rome_features | ||
let features: FeatureFlags = session | ||
.args | ||
.opt_value_from_str("--features") | ||
.map_err(|source| Termination::ParseError { | ||
argument: "--features", | ||
source, | ||
})? | ||
.unwrap_or(FeatureFlags::NONE); | ||
|
||
rome_features::set_flags(features); |
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.
As discussed in the discussion, maybe we can keep this commented out for now. In the future people will be able to opt-in, but that would require more documentation from our end, which is not a priority now.
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. We can decide later how we want to be able to toggle features.
crates/rome_features/src/lib.rs
Outdated
|
||
use once_cell::sync::OnceCell; | ||
|
||
static FLAGS: OnceCell<FeatureFlags> = OnceCell::new(); |
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.
My two cents, I like the bitflags
approach. We use it in some other places of the code base, so it's a familiar pattern we already use.
This PR currently uses I don't know that |
My initial proposal was for the use of the word unstable, with the thought that eventually it will be stable. Experimental gives me vibes of something that might be dropped because "the experiment failed", which is not what we want. |
2510ec8
to
d3c3887
Compare
Made some changes:
Checking for a feature flag now looks like this: if rome_flags::unstable().unfinished_feature() {
// Do something
} |
Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
Summary
This is a simple implementation of feature flags based on the discussion in #2471.
This PR:
rome_flags
crate with adeclare_feature_flags
macro--unstable
arg to the CLI that enables all feature flagsunstable
setting for VS Code and LSP that enables all feature flagsThis PR no longer contains a
--features
option that allows toggling feature flags individually, but it’s still useful for the feature flags themselves to have unique names. It helps associate gated code paths with the features that added them so that they're easier to clean up when features are stabilized. It also allows for in-code toggling during development.In the future, we can consider a way to allow individual feature toggling, ideally with file-based config support.
Declaring feature flags
Checking feature flags
Examples
Edit: The examples below worked when this PR was a draft that contained some example changes to object and assignment expression formatting. That dummy code was removed in order to ready this PR for review.
With a file
foo.js
containing:Running
rome format foo.js --write
results in:Formatting with
--features new_linebreaking
results in:Formatting with
--features new_spacing
results in:Formatting with
--experimental
or--features new_spacing,new_linebreaking
both result in: