From 47a253fb62d03e1d26a967d776b221289670f88a Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Fri, 8 Sep 2023 12:04:28 +0200 Subject: [PATCH] Add PreviewMode option to formatter ## Summary This PR adds the `--preview` and `--no-preview` options to the `format` command (hidden) and passes it through to the formatte. ## Test Plan I added the `dbg(f.options().preview())` statement in `FormatNodeRule::fmt` and verified that the option gets correctly passed to the formatter. --- Cargo.lock | 1 - crates/ruff_cli/src/args.rs | 7 +++++ crates/ruff_cli/src/commands/format.rs | 9 +++++- crates/ruff_python_formatter/Cargo.toml | 1 - .../src/expression/mod.rs | 12 ++++++-- crates/ruff_python_formatter/src/lib.rs | 2 +- crates/ruff_python_formatter/src/options.rs | 29 +++++++++++++++++++ 7 files changed, 54 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7d6995eb550b..1bedcca4a0fa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2337,7 +2337,6 @@ dependencies = [ "clap", "countme", "insta", - "is-macro", "itertools", "memchr", "once_cell", diff --git a/crates/ruff_cli/src/args.rs b/crates/ruff_cli/src/args.rs index a3c8c927eab6c..1c34795cc00ad 100644 --- a/crates/ruff_cli/src/args.rs +++ b/crates/ruff_cli/src/args.rs @@ -368,6 +368,12 @@ pub struct FormatCommand { /// The name of the file when passing it through stdin. #[arg(long, help_heading = "Miscellaneous")] pub stdin_filename: Option, + + /// Enable preview mode; checks will include unstable rules and fixes. + #[arg(long, overrides_with("no_preview"), hide = true)] + preview: bool, + #[clap(long, overrides_with("preview"), hide = true)] + no_preview: bool, } #[derive(Debug, Clone, Copy, clap::ValueEnum)] @@ -496,6 +502,7 @@ impl FormatCommand { self.respect_gitignore, self.no_respect_gitignore, ), + preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from), force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude), // Unsupported on the formatter CLI, but required on `Overrides`. ..Overrides::default() diff --git a/crates/ruff_cli/src/commands/format.rs b/crates/ruff_cli/src/commands/format.rs index f46e6308ff204..789b1511d5645 100644 --- a/crates/ruff_cli/src/commands/format.rs +++ b/crates/ruff_cli/src/commands/format.rs @@ -13,6 +13,7 @@ use tracing::{debug, warn}; use ruff::fs; use ruff::logging::LogLevel; +use ruff::settings::types::PreviewMode; use ruff::warn_user_once; use ruff_formatter::LineWidth; use ruff_python_ast::{PySourceType, SourceType}; @@ -72,9 +73,15 @@ pub(crate) fn format( return None; }; + let preview = match pyproject_config.settings.lib.preview { + PreviewMode::Enabled => ruff_python_formatter::PreviewMode::Enabled, + PreviewMode::Disabled => ruff_python_formatter::PreviewMode::Disabled, + }; + let line_length = resolver.resolve(path, &pyproject_config).line_length; let options = PyFormatOptions::from_source_type(source_type) - .with_line_width(LineWidth::from(NonZeroU16::from(line_length))); + .with_line_width(LineWidth::from(NonZeroU16::from(line_length))) + .with_preview(preview); debug!("Formatting {} with {:?}", path.display(), options); Some(format_path(path, options, mode)) } diff --git a/crates/ruff_python_formatter/Cargo.toml b/crates/ruff_python_formatter/Cargo.toml index 1aae33a9edc0b..8e7a3b55ad648 100644 --- a/crates/ruff_python_formatter/Cargo.toml +++ b/crates/ruff_python_formatter/Cargo.toml @@ -23,7 +23,6 @@ anyhow = { workspace = true } bitflags = { workspace = true } clap = { workspace = true } countme = "3.0.1" -is-macro = { workspace = true } itertools = { workspace = true } memchr = { workspace = true } once_cell = { workspace = true } diff --git a/crates/ruff_python_formatter/src/expression/mod.rs b/crates/ruff_python_formatter/src/expression/mod.rs index 39af5b79e9e37..413b732a531ce 100644 --- a/crates/ruff_python_formatter/src/expression/mod.rs +++ b/crates/ruff_python_formatter/src/expression/mod.rs @@ -388,10 +388,10 @@ fn can_omit_optional_parentheses(expr: &Expr, context: &PyFormatContext) -> bool // Only use the layout if the first or last expression has parentheses of some sort, and // those parentheses are non-empty. let first_parenthesized = visitor.first.is_some_and(|first| { - has_parentheses(first, context).is_some_and(|parentheses| parentheses.is_non_empty()) + has_parentheses(first, context).is_some_and(OwnParentheses::is_non_empty) }); let last_parenthesized = visitor.last.is_some_and(|last| { - has_parentheses(last, context).is_some_and(|parentheses| parentheses.is_non_empty()) + has_parentheses(last, context).is_some_and(OwnParentheses::is_non_empty) }); first_parenthesized || last_parenthesized } @@ -706,7 +706,7 @@ impl CallChainLayout { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, is_macro::Is)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) enum OwnParentheses { /// The node has parentheses, but they are empty (e.g., `[]` or `f()`). Empty, @@ -714,6 +714,12 @@ pub(crate) enum OwnParentheses { NonEmpty, } +impl OwnParentheses { + const fn is_non_empty(self) -> bool { + matches!(self, OwnParentheses::NonEmpty) + } +} + /// Returns the [`OwnParentheses`] value for a given [`Expr`], to indicate whether it has its /// own parentheses or is itself parenthesized. /// diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index f2b2486f70a60..4d96a9374eda8 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -14,7 +14,7 @@ use crate::comments::{ dangling_comments, leading_comments, trailing_comments, Comments, SourceComment, }; pub use crate::context::PyFormatContext; -pub use crate::options::{MagicTrailingComma, PyFormatOptions, QuoteStyle}; +pub use crate::options::{MagicTrailingComma, PreviewMode, PyFormatOptions, QuoteStyle}; use crate::verbatim::suppressed_node; pub(crate) mod builders; diff --git a/crates/ruff_python_formatter/src/options.rs b/crates/ruff_python_formatter/src/options.rs index c089d3f6a23ea..e68746dcdabd5 100644 --- a/crates/ruff_python_formatter/src/options.rs +++ b/crates/ruff_python_formatter/src/options.rs @@ -39,6 +39,9 @@ pub struct PyFormatOptions { /// Should the formatter generate a source map that allows mapping source positions to positions /// in the formatted document. source_map_generation: SourceMapGeneration, + + /// Whether preview style formatting is enabled or not + preview: PreviewMode, } fn default_line_width() -> LineWidth { @@ -64,6 +67,7 @@ impl Default for PyFormatOptions { line_ending: LineEnding::default(), magic_trailing_comma: MagicTrailingComma::default(), source_map_generation: SourceMapGeneration::default(), + preview: PreviewMode::default(), } } } @@ -101,6 +105,10 @@ impl PyFormatOptions { self.line_ending } + pub fn preview(&self) -> PreviewMode { + self.preview + } + #[must_use] pub fn with_tab_width(mut self, tab_width: TabWidth) -> Self { self.tab_width = tab_width; @@ -136,6 +144,12 @@ impl PyFormatOptions { self.line_ending = line_ending; self } + + #[must_use] + pub fn with_preview(mut self, preview: PreviewMode) -> Self { + self.preview = preview; + self + } } impl FormatOptions for PyFormatOptions { @@ -246,3 +260,18 @@ impl FromStr for MagicTrailingComma { } } } + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum PreviewMode { + #[default] + Disabled, + + Enabled, +} + +impl PreviewMode { + pub const fn is_enabled(self) -> bool { + matches!(self, PreviewMode::Enabled) + } +}