diff --git a/crates/ruff/src/commands/format.rs b/crates/ruff/src/commands/format.rs index eb768bee58..8cb0fae965 100644 --- a/crates/ruff/src/commands/format.rs +++ b/crates/ruff/src/commands/format.rs @@ -489,6 +489,9 @@ pub(crate) fn format_source( formatted, ))) } + SourceKind::Markdown(source) => { + unimplemented!() + } } } diff --git a/crates/ruff_linter/src/source_kind.rs b/crates/ruff_linter/src/source_kind.rs index a146d2db13..bdcd43bbfe 100644 --- a/crates/ruff_linter/src/source_kind.rs +++ b/crates/ruff_linter/src/source_kind.rs @@ -22,6 +22,8 @@ pub enum SourceKind { Python(String), /// The source contains a Jupyter notebook. IpyNotebook(Box), + /// The source contains Markdown text. + Markdown(String), } impl SourceKind { @@ -33,6 +35,7 @@ impl SourceKind { match self { SourceKind::IpyNotebook(notebook) => Some(notebook), SourceKind::Python(_) => None, + SourceKind::Markdown(_) => None, } } @@ -40,20 +43,36 @@ impl SourceKind { match self { SourceKind::Python(code) => Some(code), SourceKind::IpyNotebook(_) => None, + SourceKind::Markdown(_) => None, + } + } + + pub fn as_markdown(&self) -> Option<&str> { + match self { + SourceKind::Markdown(code) => Some(code), + SourceKind::Python(_) => None, + SourceKind::IpyNotebook(_) => None, } } pub fn expect_python(self) -> String { match self { SourceKind::Python(code) => code, - SourceKind::IpyNotebook(_) => panic!("expected python code"), + _ => panic!("expected python code"), } } pub fn expect_ipy_notebook(self) -> Notebook { match self { SourceKind::IpyNotebook(notebook) => *notebook, - SourceKind::Python(_) => panic!("expected ipy notebook"), + _ => panic!("expected ipy notebook"), + } + } + + pub fn expect_markdown(self) -> String { + match self { + SourceKind::Markdown(code) => code, + _ => panic!("expected markdown text"), } } @@ -66,6 +85,7 @@ impl SourceKind { SourceKind::IpyNotebook(cloned) } SourceKind::Python(_) => SourceKind::Python(new_source), + SourceKind::Markdown(_) => SourceKind::Markdown(new_source), } } @@ -74,6 +94,7 @@ impl SourceKind { match self { SourceKind::Python(source) => source, SourceKind::IpyNotebook(notebook) => notebook.source_code(), + SourceKind::Markdown(source) => source, } } @@ -120,6 +141,10 @@ impl SourceKind { notebook.write(writer)?; Ok(()) } + SourceKind::Markdown(source) => { + writer.write_all(source.as_bytes())?; + Ok(()) + } } } @@ -140,6 +165,10 @@ impl SourceKind { kind: DiffKind::IpyNotebook(src, dst), path, }), + (SourceKind::Markdown(src), SourceKind::Markdown(dst)) => Some(SourceKindDiff { + kind: DiffKind::Markdown(src, dst), + path, + }), _ => None, } } @@ -212,6 +241,17 @@ impl std::fmt::Display for SourceKindDiff<'_> { writeln!(f)?; } + DiffKind::Markdown(original, modified) => { + let mut diff = CodeDiff::new(original, modified); + + let relative_path = self.path.map(fs::relativize_path); + + if let Some(relative_path) = &relative_path { + diff.header(relative_path, relative_path); + } + + writeln!(f, "{diff}")?; + } } Ok(()) @@ -222,6 +262,7 @@ impl std::fmt::Display for SourceKindDiff<'_> { enum DiffKind<'a> { Python(&'a str, &'a str), IpyNotebook(&'a Notebook, &'a Notebook), + Markdown(&'a str, &'a str), } struct CodeDiff<'a> { diff --git a/crates/ruff_python_ast/src/lib.rs b/crates/ruff_python_ast/src/lib.rs index 31c4cd019d..24e1e4c7f0 100644 --- a/crates/ruff_python_ast/src/lib.rs +++ b/crates/ruff_python_ast/src/lib.rs @@ -89,6 +89,8 @@ pub enum PySourceType { Stub, /// The source is a Jupyter notebook (`.ipynb`). Ipynb, + /// The source is a Markdown file (`.md`). + Markdown, } impl PySourceType { @@ -106,6 +108,7 @@ impl PySourceType { "pyi" => Self::Stub, "pyw" => Self::Python, "ipynb" => Self::Ipynb, + "md" => Self::Markdown, _ => return None, }; @@ -134,6 +137,10 @@ impl PySourceType { pub const fn is_ipynb(self) -> bool { matches!(self, Self::Ipynb) } + + pub const fn is_markdown(self) -> bool { + matches!(self, Self::Markdown) + } } impl> From

for PySourceType { diff --git a/crates/ruff_python_formatter/src/comments/format.rs b/crates/ruff_python_formatter/src/comments/format.rs index fdf7250cc3..e2c5c2bd63 100644 --- a/crates/ruff_python_formatter/src/comments/format.rs +++ b/crates/ruff_python_formatter/src/comments/format.rs @@ -334,7 +334,7 @@ impl Format> for FormatEmptyLines { PySourceType::Stub => { write!(f, [empty_line()]) } - PySourceType::Python | PySourceType::Ipynb => { + PySourceType::Python | PySourceType::Ipynb | PySourceType::Markdown => { write!(f, [empty_line(), empty_line()]) } }, diff --git a/crates/ruff_python_formatter/src/statement/suite.rs b/crates/ruff_python_formatter/src/statement/suite.rs index 9ed32beb76..5d0756ce47 100644 --- a/crates/ruff_python_formatter/src/statement/suite.rs +++ b/crates/ruff_python_formatter/src/statement/suite.rs @@ -283,7 +283,9 @@ impl FormatRule> for FormatSuite { PySourceType::Stub => { empty_line().fmt(f)?; } - PySourceType::Python | PySourceType::Ipynb => { + PySourceType::Python + | PySourceType::Ipynb + | PySourceType::Markdown => { write!(f, [empty_line(), empty_line()])?; } }, @@ -324,7 +326,7 @@ impl FormatRule> for FormatSuite { PySourceType::Stub => { empty_line().fmt(f)?; } - PySourceType::Python | PySourceType::Ipynb => { + PySourceType::Python | PySourceType::Ipynb | PySourceType::Markdown => { write!(f, [empty_line(), empty_line()])?; } }, @@ -376,7 +378,7 @@ impl FormatRule> for FormatSuite { PySourceType::Stub => { empty_line().fmt(f)?; } - PySourceType::Python | PySourceType::Ipynb => { + PySourceType::Python | PySourceType::Ipynb | PySourceType::Markdown => { write!(f, [empty_line(), empty_line()])?; } }, diff --git a/crates/ruff_python_parser/src/lib.rs b/crates/ruff_python_parser/src/lib.rs index 86bfe56697..db527c5574 100644 --- a/crates/ruff_python_parser/src/lib.rs +++ b/crates/ruff_python_parser/src/lib.rs @@ -525,7 +525,7 @@ pub trait AsMode { impl AsMode for PySourceType { fn as_mode(&self) -> Mode { match self { - PySourceType::Python | PySourceType::Stub => Mode::Module, + PySourceType::Python | PySourceType::Stub | PySourceType::Markdown => Mode::Module, PySourceType::Ipynb => Mode::Ipython, } } diff --git a/crates/ty_test/src/parser.rs b/crates/ty_test/src/parser.rs index 30f0d8b248..10c166787f 100644 --- a/crates/ty_test/src/parser.rs +++ b/crates/ty_test/src/parser.rs @@ -318,6 +318,7 @@ impl EmbeddedFilePath<'_> { EmbeddedFilePath::Autogenerated(PySourceType::Python) => "mdtest_snippet.py", EmbeddedFilePath::Autogenerated(PySourceType::Stub) => "mdtest_snippet.pyi", EmbeddedFilePath::Autogenerated(PySourceType::Ipynb) => "mdtest_snippet.ipynb", + EmbeddedFilePath::Autogenerated(PySourceType::Markdown) => "mdtest_snippet.md", EmbeddedFilePath::Explicit(path) => path, } }