diff --git a/Cargo.lock b/Cargo.lock index 66a881ad374878..dced049465740e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2177,6 +2177,8 @@ version = "0.0.0" dependencies = [ "drop_bomb", "insta", + "ruff_cache", + "ruff_macros", "ruff_text_size", "rustc-hash", "schemars", @@ -2335,7 +2337,9 @@ dependencies = [ "itertools 0.11.0", "memchr", "once_cell", + "ruff_cache", "ruff_formatter", + "ruff_macros", "ruff_python_ast", "ruff_python_index", "ruff_python_parser", @@ -2527,8 +2531,10 @@ dependencies = [ "pep440_rs", "regex", "ruff_cache", + "ruff_formatter", "ruff_linter", "ruff_macros", + "ruff_python_formatter", "rustc-hash", "schemars", "serde", diff --git a/crates/ruff_cache/src/cache_key.rs b/crates/ruff_cache/src/cache_key.rs index c62e580d1e2b34..6140792f143ec4 100644 --- a/crates/ruff_cache/src/cache_key.rs +++ b/crates/ruff_cache/src/cache_key.rs @@ -2,7 +2,10 @@ use std::borrow::Cow; use std::collections::hash_map::DefaultHasher; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::hash::{Hash, Hasher}; -use std::num::NonZeroU8; +use std::num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, + NonZeroU32, NonZeroU64, NonZeroU8, +}; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; @@ -205,14 +208,29 @@ impl CacheKey for i8 { state.write_i8(*self); } } - -impl CacheKey for NonZeroU8 { - #[inline] - fn cache_key(&self, state: &mut CacheKeyHasher) { - state.write_u8(self.get()); - } +macro_rules! impl_cache_key_non_zero { + ($name:ident) => { + impl CacheKey for $name { + #[inline] + fn cache_key(&self, state: &mut CacheKeyHasher) { + self.get().cache_key(state) + } + } + }; } +impl_cache_key_non_zero!(NonZeroU8); +impl_cache_key_non_zero!(NonZeroU16); +impl_cache_key_non_zero!(NonZeroU32); +impl_cache_key_non_zero!(NonZeroU64); +impl_cache_key_non_zero!(NonZeroU128); + +impl_cache_key_non_zero!(NonZeroI8); +impl_cache_key_non_zero!(NonZeroI16); +impl_cache_key_non_zero!(NonZeroI32); +impl_cache_key_non_zero!(NonZeroI64); +impl_cache_key_non_zero!(NonZeroI128); + macro_rules! impl_cache_key_tuple { () => ( impl CacheKey for () { diff --git a/crates/ruff_cli/src/commands/format.rs b/crates/ruff_cli/src/commands/format.rs index f8277601eb3124..4d45d5a95cdd2f 100644 --- a/crates/ruff_cli/src/commands/format.rs +++ b/crates/ruff_cli/src/commands/format.rs @@ -1,6 +1,5 @@ use std::fmt::{Display, Formatter}; use std::io; -use std::num::NonZeroU16; use std::path::{Path, PathBuf}; use std::time::Instant; @@ -10,12 +9,10 @@ use log::error; use rayon::iter::Either::{Left, Right}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use thiserror::Error; -use tracing::{debug, warn}; +use tracing::debug; -use ruff_formatter::LineWidth; use ruff_linter::fs; use ruff_linter::logging::LogLevel; -use ruff_linter::settings::types::PreviewMode; use ruff_linter::warn_user_once; use ruff_python_ast::{PySourceType, SourceType}; use ruff_python_formatter::{format_module, FormatModuleError, PyFormatOptions}; @@ -76,17 +73,7 @@ pub(crate) fn format( }; let resolved_settings = resolver.resolve(path, &pyproject_config); - - // TODO(micha): Use `formatter` settings instead - let preview = match resolved_settings.linter.preview { - PreviewMode::Enabled => ruff_python_formatter::PreviewMode::Enabled, - PreviewMode::Disabled => ruff_python_formatter::PreviewMode::Disabled, - }; - let line_length = resolved_settings.linter.line_length; - - let options = PyFormatOptions::from_source_type(source_type) - .with_line_width(LineWidth::from(NonZeroU16::from(line_length))) - .with_preview(preview); + let options = resolved_settings.formatter.to_format_options(source_type); debug!("Formatting {} with {:?}", path.display(), options); Some(match catch_unwind(|| format_path(path, options, mode)) { diff --git a/crates/ruff_cli/src/commands/format_stdin.rs b/crates/ruff_cli/src/commands/format_stdin.rs index e0ae8d9ba8128d..bcbf6342f32c93 100644 --- a/crates/ruff_cli/src/commands/format_stdin.rs +++ b/crates/ruff_cli/src/commands/format_stdin.rs @@ -1,12 +1,10 @@ use std::io::{stdout, Write}; -use std::num::NonZeroU16; use std::path::Path; use anyhow::Result; use log::warn; -use ruff_formatter::LineWidth; -use ruff_linter::settings::types::PreviewMode; +use ruff_python_ast::PySourceType; use ruff_python_formatter::{format_module, PyFormatOptions}; use ruff_workspace::resolver::python_file_at_path; @@ -39,18 +37,10 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &Overrides) -> Resu // Format the file. let path = cli.stdin_filename.as_deref(); - // TODO(micha): Use Formatter settings - let preview = match pyproject_config.settings.linter.preview { - PreviewMode::Enabled => ruff_python_formatter::PreviewMode::Enabled, - PreviewMode::Disabled => ruff_python_formatter::PreviewMode::Disabled, - }; - let line_length = pyproject_config.settings.linter.line_length; - - let options = path - .map(PyFormatOptions::from_extension) - .unwrap_or_default() - .with_line_width(LineWidth::from(NonZeroU16::from(line_length))) - .with_preview(preview); + let options = pyproject_config + .settings + .formatter + .to_format_options(path.map(PySourceType::from).unwrap_or_default()); match format_source(path, options, mode) { Ok(result) => match mode { diff --git a/crates/ruff_dev/src/format_dev.rs b/crates/ruff_dev/src/format_dev.rs index 19ef98200d54de..3c4c4cf3c0801a 100644 --- a/crates/ruff_dev/src/format_dev.rs +++ b/crates/ruff_dev/src/format_dev.rs @@ -18,7 +18,6 @@ use imara_diff::{diff, Algorithm}; use indicatif::ProgressStyle; #[cfg_attr(feature = "singlethreaded", allow(unused_imports))] use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use ruff_linter::line_width::LineLength; use serde::Deserialize; use similar::{ChangeTag, TextDiff}; use tempfile::NamedTempFile; @@ -551,10 +550,8 @@ fn format_dir_entry( let settings = resolver.resolve(&path, pyproject_config); // That's a bad way of doing this but it's not worth doing something better for format_dev // TODO(micha) use formatter settings instead - if settings.linter.line_length != LineLength::default() { - options = options.with_line_width(LineWidth::from(NonZeroU16::from( - settings.linter.line_length, - ))); + if settings.formatter.line_width != LineWidth::default() { + options = options.with_line_width(settings.formatter.line_width); } // Handle panics (mostly in `debug_assert!`) diff --git a/crates/ruff_formatter/Cargo.toml b/crates/ruff_formatter/Cargo.toml index 88f5bbf892df46..8e8a4332941236 100644 --- a/crates/ruff_formatter/Cargo.toml +++ b/crates/ruff_formatter/Cargo.toml @@ -11,6 +11,8 @@ repository = { workspace = true } license = { workspace = true } [dependencies] +ruff_cache = { path = "../ruff_cache" } +ruff_macros = { path = "../ruff_macros" } ruff_text_size = { path = "../ruff_text_size" } drop_bomb = { version = "0.1.5" } diff --git a/crates/ruff_formatter/src/lib.rs b/crates/ruff_formatter/src/lib.rs index 3ef9b94e61de2b..63c4a5a6042a2c 100644 --- a/crates/ruff_formatter/src/lib.rs +++ b/crates/ruff_formatter/src/lib.rs @@ -51,9 +51,10 @@ pub use source_code::{SourceCode, SourceCodeSlice}; pub use crate::diagnostics::{ActualStart, FormatError, InvalidDocumentError, PrintError}; pub use format_element::{normalize_newlines, FormatElement, LINE_TERMINATORS}; pub use group_id::GroupId; +use ruff_macros::CacheKey; use ruff_text_size::{TextRange, TextSize}; -#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, CacheKey)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[derive(Default)] @@ -116,8 +117,14 @@ impl TryFrom for IndentWidth { } } +impl From for IndentWidth { + fn from(value: NonZeroU8) -> Self { + Self(value) + } +} + /// The maximum visual width to which the formatter should try to limit a line. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, CacheKey)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct LineWidth(NonZeroU16); diff --git a/crates/ruff_formatter/src/printer/printer_options/mod.rs b/crates/ruff_formatter/src/printer/printer_options/mod.rs index efbe850cbf22ef..e3fca43c2bee88 100644 --- a/crates/ruff_formatter/src/printer/printer_options/mod.rs +++ b/crates/ruff_formatter/src/printer/printer_options/mod.rs @@ -1,4 +1,5 @@ use crate::{FormatOptions, IndentStyle, IndentWidth, LineWidth}; +use ruff_macros::CacheKey; /// Options that affect how the [`crate::Printer`] prints the format tokens #[derive(Clone, Debug, Eq, PartialEq, Default)] @@ -120,7 +121,7 @@ impl SourceMapGeneration { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, CacheKey)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum LineEnding { /// Line Feed only (\n), common on Linux and macOS as well as inside git repos diff --git a/crates/ruff_python_formatter/Cargo.toml b/crates/ruff_python_formatter/Cargo.toml index 8e7a3b55ad6484..639e350d1f6d3b 100644 --- a/crates/ruff_python_formatter/Cargo.toml +++ b/crates/ruff_python_formatter/Cargo.toml @@ -11,7 +11,9 @@ repository = { workspace = true } license = { workspace = true } [dependencies] +ruff_cache = { path = "../ruff_cache" } ruff_formatter = { path = "../ruff_formatter" } +ruff_macros = { path = "../ruff_macros" } ruff_python_trivia = { path = "../ruff_python_trivia" } ruff_source_file = { path = "../ruff_source_file" } ruff_python_ast = { path = "../ruff_python_ast" } diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index 5ba8e3dde02789..09d3a07d7858f4 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -17,6 +17,7 @@ use crate::comments::{ pub use crate::context::PyFormatContext; pub use crate::options::{MagicTrailingComma, PreviewMode, PyFormatOptions, QuoteStyle}; use crate::verbatim::suppressed_node; +pub use settings::FormatterSettings; pub(crate) mod builders; pub mod cli; @@ -29,6 +30,7 @@ mod options; pub(crate) mod other; pub(crate) mod pattern; mod prelude; +mod settings; pub(crate) mod statement; pub(crate) mod type_param; mod verbatim; diff --git a/crates/ruff_python_formatter/src/options.rs b/crates/ruff_python_formatter/src/options.rs index 457b2f9884c4e3..064aeaaf1c8365 100644 --- a/crates/ruff_python_formatter/src/options.rs +++ b/crates/ruff_python_formatter/src/options.rs @@ -1,9 +1,12 @@ use ruff_formatter::printer::{LineEnding, PrinterOptions, SourceMapGeneration}; use ruff_formatter::{FormatOptions, IndentStyle, IndentWidth, LineWidth}; +use ruff_macros::CacheKey; use ruff_python_ast::PySourceType; use std::path::Path; use std::str::FromStr; +/// Resolved options for formatting one individual file. This is different from [`FormatSettings`] which +/// represents the formatting settings for multiple files (the whole project, a subdirectory, ...) #[derive(Clone, Debug)] #[cfg_attr( feature = "serde", @@ -176,7 +179,7 @@ impl FormatOptions for PyFormatOptions { } } -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, CacheKey)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -230,7 +233,7 @@ impl FromStr for QuoteStyle { } } -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug, Default, CacheKey)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -261,7 +264,7 @@ impl FromStr for MagicTrailingComma { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, CacheKey)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))] pub enum PreviewMode { diff --git a/crates/ruff_python_formatter/src/settings.rs b/crates/ruff_python_formatter/src/settings.rs new file mode 100644 index 00000000000000..4a9f42d20afe02 --- /dev/null +++ b/crates/ruff_python_formatter/src/settings.rs @@ -0,0 +1,49 @@ +use std::path::PathBuf; + +use ruff_formatter::{FormatOptions, IndentStyle, LineWidth}; +use ruff_macros::CacheKey; +use ruff_python_ast::PySourceType; + +use crate::{MagicTrailingComma, PreviewMode, PyFormatOptions, QuoteStyle}; + +#[derive(CacheKey, Clone, Debug)] +pub struct FormatterSettings { + /// The files that are excluded from formatting (but may be linted). + pub exclude: Vec, + + pub preview: PreviewMode, + + pub line_width: LineWidth, + + pub indent_style: IndentStyle, + + pub quote_style: QuoteStyle, + + pub magic_trailing_comma: MagicTrailingComma, +} + +impl FormatterSettings { + pub fn to_format_options(&self, source_type: PySourceType) -> PyFormatOptions { + PyFormatOptions::from_source_type(source_type) + .with_indent_style(self.indent_style) + .with_quote_style(self.quote_style) + .with_magic_trailing_comma(self.magic_trailing_comma) + .with_preview(self.preview) + .with_line_width(self.line_width) + } +} + +impl Default for FormatterSettings { + fn default() -> Self { + let default_options = PyFormatOptions::default(); + + Self { + exclude: Vec::default(), + preview: PreviewMode::Disabled, + line_width: default_options.line_width(), + indent_style: default_options.indent_style(), + quote_style: default_options.quote_style(), + magic_trailing_comma: default_options.magic_trailing_comma(), + } + } +} diff --git a/crates/ruff_wasm/src/lib.rs b/crates/ruff_wasm/src/lib.rs index feef6f7a49a576..088cb1273469a4 100644 --- a/crates/ruff_wasm/src/lib.rs +++ b/crates/ruff_wasm/src/lib.rs @@ -1,21 +1,20 @@ -use std::num::NonZeroU16; use std::path::Path; use js_sys::Error; use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; -use ruff_formatter::{FormatResult, Formatted, LineWidth}; +use ruff_formatter::{FormatResult, Formatted}; use ruff_linter::directives; use ruff_linter::line_width::{LineLength, TabSize}; use ruff_linter::linter::{check_path, LinterResult}; use ruff_linter::registry::AsRule; -use ruff_linter::settings::types::{PreviewMode, PythonVersion}; +use ruff_linter::settings::types::PythonVersion; use ruff_linter::settings::{flags, DUMMY_VARIABLE_RGX, PREFIXES}; use ruff_linter::source_kind::SourceKind; use ruff_python_ast::{Mod, PySourceType}; use ruff_python_codegen::Stylist; -use ruff_python_formatter::{format_node, pretty_comments, PyFormatContext, PyFormatOptions}; +use ruff_python_formatter::{format_node, pretty_comments, PyFormatContext}; use ruff_python_index::{CommentRangesBuilder, Indexer}; use ruff_python_parser::lexer::LexResult; use ruff_python_parser::{parse_tokens, AsMode, Mode}; @@ -302,15 +301,9 @@ impl<'a> ParsedModule<'a> { fn format(&self, settings: &Settings) -> FormatResult> { // TODO(konstin): Add an options for py/pyi to the UI (2/2) - // TODO(micha): Use formatter settings instead - let options = PyFormatOptions::from_source_type(PySourceType::default()) - .with_preview(match settings.linter.preview { - PreviewMode::Disabled => ruff_python_formatter::PreviewMode::Disabled, - PreviewMode::Enabled => ruff_python_formatter::PreviewMode::Enabled, - }) - .with_line_width(LineWidth::from(NonZeroU16::from( - settings.linter.line_length, - ))); + let options = settings + .formatter + .to_format_options(PySourceType::default()); format_node( &self.module, diff --git a/crates/ruff_workspace/Cargo.toml b/crates/ruff_workspace/Cargo.toml index dd741254eb25ed..3be2dca20fbdbd 100644 --- a/crates/ruff_workspace/Cargo.toml +++ b/crates/ruff_workspace/Cargo.toml @@ -14,6 +14,8 @@ license = { workspace = true } [dependencies] ruff_linter = { path = "../ruff_linter" } +ruff_formatter = { path = "../ruff_formatter" } +ruff_python_formatter = { path = "../ruff_python_formatter" } ruff_cache = { path = "../ruff_cache" } ruff_macros = { path = "../ruff_macros" } diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index 3217f835b9cdbe..39d8be9cdf4199 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -4,21 +4,19 @@ use std::borrow::Cow; use std::env::VarError; +use std::num::NonZeroU16; use std::path::{Path, PathBuf}; -use crate::options::{ - Flake8AnnotationsOptions, Flake8BanditOptions, Flake8BugbearOptions, Flake8BuiltinsOptions, - Flake8ComprehensionsOptions, Flake8CopyrightOptions, Flake8ErrMsgOptions, Flake8GetTextOptions, - Flake8ImplicitStrConcatOptions, Flake8ImportConventionsOptions, Flake8PytestStyleOptions, - Flake8QuotesOptions, Flake8SelfOptions, Flake8TidyImportsOptions, Flake8TypeCheckingOptions, - Flake8UnusedArgumentsOptions, IsortOptions, McCabeOptions, Options, Pep8NamingOptions, - PyUpgradeOptions, PycodestyleOptions, PydocstyleOptions, PyflakesOptions, PylintOptions, -}; -use crate::settings::{ResolverSettings, Settings, EXCLUDE, INCLUDE}; use anyhow::{anyhow, Result}; use glob::{glob, GlobError, Paths, PatternError}; use regex::Regex; +use rustc_hash::{FxHashMap, FxHashSet}; +use shellexpand; +use shellexpand::LookupError; +use strum::IntoEnumIterator; + use ruff_cache::cache_dir; +use ruff_formatter::{IndentStyle, LineWidth}; use ruff_linter::line_width::{LineLength, TabSize}; use ruff_linter::registry::RuleNamespace; use ruff_linter::registry::{Rule, RuleSet, INCOMPATIBLE_CODES}; @@ -34,10 +32,17 @@ use ruff_linter::settings::{ use ruff_linter::{ fs, warn_user, warn_user_once, warn_user_once_by_id, RuleSelector, RUFF_PKG_VERSION, }; -use rustc_hash::{FxHashMap, FxHashSet}; -use shellexpand; -use shellexpand::LookupError; -use strum::IntoEnumIterator; +use ruff_python_formatter::{FormatterSettings, MagicTrailingComma, QuoteStyle}; + +use crate::options::{ + Flake8AnnotationsOptions, Flake8BanditOptions, Flake8BugbearOptions, Flake8BuiltinsOptions, + Flake8ComprehensionsOptions, Flake8CopyrightOptions, Flake8ErrMsgOptions, Flake8GetTextOptions, + Flake8ImplicitStrConcatOptions, Flake8ImportConventionsOptions, Flake8PytestStyleOptions, + Flake8QuotesOptions, Flake8SelfOptions, Flake8TidyImportsOptions, Flake8TypeCheckingOptions, + Flake8UnusedArgumentsOptions, IsortOptions, McCabeOptions, Options, Pep8NamingOptions, + PyUpgradeOptions, PycodestyleOptions, PydocstyleOptions, PyflakesOptions, PylintOptions, +}; +use crate::settings::{ResolverSettings, Settings, EXCLUDE, INCLUDE}; #[derive(Debug, Default)] pub struct RuleSelection { @@ -149,6 +154,7 @@ impl Configuration { respect_gitignore: self.respect_gitignore.unwrap_or(true), project_root: project_root.to_path_buf(), }, + linter: LinterSettings { target_version, project_root: project_root.to_path_buf(), @@ -283,6 +289,21 @@ impl Configuration { .map(PyUpgradeOptions::into_settings) .unwrap_or_default(), }, + + formatter: FormatterSettings { + exclude: vec![], + preview: self + .preview + .map(|preview| match preview { + PreviewMode::Disabled => ruff_python_formatter::PreviewMode::Disabled, + PreviewMode::Enabled => ruff_python_formatter::PreviewMode::Enabled, + }) + .unwrap_or_default(), + line_width: LineWidth::from(NonZeroU16::from(self.line_length.unwrap_or_default())), + indent_style: IndentStyle::default(), + quote_style: QuoteStyle::default(), + magic_trailing_comma: MagicTrailingComma::default(), + }, }) } @@ -805,12 +826,13 @@ pub fn resolve_src(src: &[String], project_root: &Path) -> Result> #[cfg(test)] mod tests { - use crate::configuration::{Configuration, RuleSelection}; use ruff_linter::codes::{Flake8Copyright, Pycodestyle, Refurb}; use ruff_linter::registry::{Linter, Rule, RuleSet}; use ruff_linter::settings::types::PreviewMode; use ruff_linter::RuleSelector; + use crate::configuration::{Configuration, RuleSelection}; + const NURSERY_RULES: &[Rule] = &[ Rule::MissingCopyrightNotice, Rule::IndentationWithInvalidMultiple, diff --git a/crates/ruff_workspace/src/settings.rs b/crates/ruff_workspace/src/settings.rs index be2e56d698dc5c..24daee6fcdc17c 100644 --- a/crates/ruff_workspace/src/settings.rs +++ b/crates/ruff_workspace/src/settings.rs @@ -3,6 +3,7 @@ use ruff_cache::cache_dir; use ruff_linter::settings::types::{FilePattern, FilePatternSet, SerializationFormat}; use ruff_linter::settings::LinterSettings; use ruff_macros::CacheKey; +use ruff_python_formatter::FormatterSettings; use std::path::{Path, PathBuf}; #[derive(Debug, CacheKey)] @@ -23,6 +24,7 @@ pub struct Settings { pub resolver: ResolverSettings, pub linter: LinterSettings, + pub formatter: FormatterSettings, } impl Default for Settings { @@ -37,6 +39,7 @@ impl Default for Settings { show_source: false, linter: LinterSettings::new(project_root), resolver: ResolverSettings::new(project_root), + formatter: FormatterSettings::default(), } } }