diff --git a/crates/ruff/tests/cli/format.rs b/crates/ruff/tests/cli/format.rs index 6e7c8a8117625..5e7dcbe6b2bf3 100644 --- a/crates/ruff/tests/cli/format.rs +++ b/crates/ruff/tests/cli/format.rs @@ -2512,3 +2512,63 @@ fn markdown_formatting_stdin() -> Result<()> { "#); Ok(()) } + +#[test] +fn format_mapped_extension_files() -> Result<()> { + let test = CliTest::with_files([ + ( + "pyproject.toml", + r#" +[tool.ruff] +extension = {foo="python", bar="markdown"} +"#, + ), + ( + "test.foo", + r" +print( 'hello' ) +", + ), + ( + "test.bar", + r" +Text string + +```py +print( 'hello' ) +``` +", + ), + ])?; + + assert_cmd_snapshot!( + test.format_command() + .args(["format", "--preview", "--check", "."]), + @r#" + success: false + exit_code: 2 + ----- stdout ----- + io: [TMP]/format: No such file or directory (os error 2) + --> format:1:1 + + unformatted: File would be reformatted + --> test.bar:1:1 + 2 | Text string + 3 | + 4 | ```py + - print( 'hello' ) + 5 + print("hello") + 6 | ``` + + unformatted: File would be reformatted + --> test.foo:1:1 + - + - print( 'hello' ) + 1 + print("hello") + + 2 files would be reformatted + + ----- stderr ----- + "#); + Ok(()) +} diff --git a/crates/ruff_linter/src/settings/types.rs b/crates/ruff_linter/src/settings/types.rs index feb23afc6c64d..c80d0d24415d9 100644 --- a/crates/ruff_linter/src/settings/types.rs +++ b/crates/ruff_linter/src/settings/types.rs @@ -200,6 +200,7 @@ impl Deref for GlobPath { #[derive(Debug, Clone, CacheKey, PartialEq, PartialOrd, Eq, Ord)] pub enum FilePattern { Builtin(&'static str), + Config(String), User(String, GlobPath), } @@ -209,6 +210,9 @@ impl FilePattern { FilePattern::Builtin(pattern) => { builder.add(Glob::from_str(pattern)?); } + FilePattern::Config(pattern) => { + builder.add(Glob::new(&pattern)?); + } FilePattern::User(pattern, absolute) => { // Add the absolute path. builder.add(Glob::new(&absolute.to_string_lossy())?); @@ -230,7 +234,7 @@ impl Display for FilePattern { "{:?}", match self { Self::Builtin(pattern) => pattern, - Self::User(pattern, _) => pattern.as_str(), + Self::User(pattern, _) | Self::Config(pattern) => pattern.as_str(), } ) } @@ -498,6 +502,11 @@ impl From for (String, Language) { pub struct ExtensionMapping(FxHashMap); impl ExtensionMapping { + /// Return the file extensions in the mapping. + pub fn extensions(&self) -> impl Iterator { + self.0.keys() + } + /// Return the [`Language`] for the given file. pub fn get(&self, path: &Path) -> Option { let ext = path.extension()?.to_str()?; diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index dbf31290d17c7..c2c6857d70a30 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -279,9 +279,19 @@ impl Configuration { PreviewMode::Disabled => FilePatternSet::try_from_iter( self.include.unwrap_or_else(|| INCLUDE.to_vec()), )?, - PreviewMode::Enabled => FilePatternSet::try_from_iter( - self.include.unwrap_or_else(|| INCLUDE_PREVIEW.to_vec()), - )?, + PreviewMode::Enabled => { + FilePatternSet::try_from_iter(self.include.unwrap_or_else(|| { + let mut patterns = INCLUDE_PREVIEW.to_vec(); + if let Some(extension_map) = &self.extension { + patterns.extend( + extension_map + .extensions() + .map(|ext| FilePattern::Config(format!("*.{ext}"))), + ); + } + patterns + }))? + } }, respect_gitignore: self.respect_gitignore.unwrap_or(true), project_root: project_root.to_path_buf(), diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index 78e35073a1d04..f7b77e286e861 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -286,6 +286,10 @@ pub struct Options { /// by the `--extension` command-line flag). /// /// Supported file types include `python`, `pyi`, `ipynb`, and `markdown`. + /// + /// Any file extensions listed here will be automatically added to the + /// default `include` list as a `*.{ext}` glob, so that they are linted + /// and formatted without needing any additional configuration settings. #[option( default = "{}", value_type = "dict[str, Language]", diff --git a/ruff.schema.json b/ruff.schema.json index 0f1e7cf4d60f0..ebcd8c1f39c97 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -178,7 +178,7 @@ } }, "extension": { - "description": "A mapping of custom file extensions to known file types (overridden\nby the `--extension` command-line flag).\n\nSupported file types include `python`, `pyi`, `ipynb`, and `markdown`.", + "description": "A mapping of custom file extensions to known file types (overridden\nby the `--extension` command-line flag).\n\nSupported file types include `python`, `pyi`, `ipynb`, and `markdown`.\n\nAny file extensions listed here will be automatically added to the\ndefault `include` list as a `*.{ext}` glob, so that they are linted\nand formatted without needing any additional configuration settings.", "type": [ "object", "null"