From d4027d8b65d2fc88e6474ba1387ecf30cde658ba Mon Sep 17 00:00:00 2001 From: konstin Date: Thu, 1 Jun 2023 11:55:04 +0200 Subject: [PATCH] Use new formatter infrastructure in CLI and test (#4767) * Use dummy verbatim formatter for all nodes * Use new formatter infrastructure in CLI and test * Expose the new formatter in the CLI * Merge import blocks --- Cargo.lock | 1 + crates/ruff_cli/Cargo.toml | 1 + crates/ruff_cli/src/lib.rs | 17 +++--- crates/ruff_python_formatter/src/lib.rs | 27 ++++++++-- crates/ruff_python_formatter/src/main.rs | 6 +-- .../ruff_python_formatter/src/module/mod.rs | 53 +++++++++---------- 6 files changed, 64 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 646078fa99..3df63e6e02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1864,6 +1864,7 @@ dependencies = [ "ruff_cache", "ruff_diagnostics", "ruff_python_ast", + "ruff_python_formatter", "ruff_python_stdlib", "ruff_text_size", "ruff_textwrap", diff --git a/crates/ruff_cli/Cargo.toml b/crates/ruff_cli/Cargo.toml index 1f2b3f24f5..6f0cb1ef9d 100644 --- a/crates/ruff_cli/Cargo.toml +++ b/crates/ruff_cli/Cargo.toml @@ -26,6 +26,7 @@ ruff = { path = "../ruff", features = ["clap"] } ruff_cache = { path = "../ruff_cache" } ruff_diagnostics = { path = "../ruff_diagnostics" } ruff_python_ast = { path = "../ruff_python_ast" } +ruff_python_formatter = { path = "../ruff_python_formatter" } ruff_text_size = { workspace = true } ruff_textwrap = { path = "../ruff_textwrap" } diff --git a/crates/ruff_cli/src/lib.rs b/crates/ruff_cli/src/lib.rs index 3a88139e54..8f0f2d34c8 100644 --- a/crates/ruff_cli/src/lib.rs +++ b/crates/ruff_cli/src/lib.rs @@ -12,6 +12,7 @@ use ruff::logging::{set_up_logging, LogLevel}; use ruff::settings::types::SerializationFormat; use ruff::settings::{flags, CliSettings}; use ruff::{fs, warn_user_once}; +use ruff_python_formatter::format_module; use crate::args::{Args, CheckArgs, Command}; use crate::commands::run_stdin::read_from_stdin; @@ -131,22 +132,26 @@ fn format(files: &[PathBuf]) -> Result { internal use only." ); - // dummy - let format_code = |code: &str| code.replace("# DEL", ""); + let format_code = |code: &str| { + // dummy, to check that the function was actually called + let contents = code.replace("# DEL", ""); + // real formatting that is currently a passthrough + format_module(&contents) + }; match &files { // Check if we should read from stdin [path] if path == Path::new("-") => { let unformatted = read_from_stdin()?; - let formatted = format_code(&unformatted); - stdout().lock().write_all(formatted.as_bytes())?; + let formatted = format_code(&unformatted)?; + stdout().lock().write_all(formatted.as_code().as_bytes())?; } _ => { for file in files { let unformatted = std::fs::read_to_string(file) .with_context(|| format!("Could not read {}: ", file.display()))?; - let formatted = format_code(&unformatted); - std::fs::write(file, formatted) + let formatted = format_code(&unformatted)?; + std::fs::write(file, formatted.as_code().as_bytes()) .with_context(|| format!("Could not write to {}, exiting", file.display()))?; } } diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index 4604384e8c..f24e8eb7f5 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -16,7 +16,6 @@ use ruff_python_ast::source_code::{CommentRanges, CommentRangesBuilder, Locator} use crate::comments::Comments; use crate::context::PyFormatContext; -use crate::module::FormatModule; pub mod cli; mod comments; @@ -130,9 +129,9 @@ pub fn format_node<'a>( line_width: 88.try_into().unwrap(), }, locator.contents(), - comments + comments, ), - [FormatModule::new(root)] + [root.format()] ) } @@ -165,13 +164,33 @@ mod tests { use insta::assert_snapshot; use rustpython_parser::lexer::lex; use rustpython_parser::{parse_tokens, Mode}; + use similar::TextDiff; use ruff_python_ast::source_code::CommentRangesBuilder; use ruff_testing_macros::fixture; - use similar::TextDiff; use crate::{format_module, format_node}; + /// Very basic test intentionally kept very similar to the CLI + #[test] + fn basic() -> Result<()> { + let input = r#" +# preceding +if True: + print( "hi" ) +# trailing +"#; + let expected = r#" +# preceding +if True: + print( "hi" ) +# trailing +"#; + let actual = format_module(input)?.as_code().to_string(); + assert_eq!(expected, actual); + Ok(()) + } + #[fixture(pattern = "resources/test/fixtures/black/**/*.py")] #[test] fn black_test(input_path: &Path) -> Result<()> { diff --git a/crates/ruff_python_formatter/src/main.rs b/crates/ruff_python_formatter/src/main.rs index c71a6b1a3f..19178aa6ac 100644 --- a/crates/ruff_python_formatter/src/main.rs +++ b/crates/ruff_python_formatter/src/main.rs @@ -6,12 +6,10 @@ use clap::Parser as ClapParser; use ruff_python_formatter::cli::Cli; use ruff_python_formatter::format_module; +#[allow(clippy::print_stdout)] fn main() -> Result<()> { let cli = Cli::parse(); let contents = fs::read_to_string(cli.file)?; - #[allow(clippy::print_stdout)] - { - println!("{}", format_module(&contents)?.as_code()); - } + println!("{}", format_module(&contents)?.as_code()); Ok(()) } diff --git a/crates/ruff_python_formatter/src/module/mod.rs b/crates/ruff_python_formatter/src/module/mod.rs index ce3fc5e8ac..6036af3942 100644 --- a/crates/ruff_python_formatter/src/module/mod.rs +++ b/crates/ruff_python_formatter/src/module/mod.rs @@ -1,38 +1,37 @@ +use crate::context::PyFormatContext; +use crate::{AsFormat, IntoFormat, PyFormatter}; +use ruff_formatter::{Format, FormatOwnedWithRule, FormatRefWithRule, FormatResult, FormatRule}; +use rustpython_parser::ast::Mod; + pub(crate) mod mod_expression; pub(crate) mod mod_function_type; pub(crate) mod mod_interactive; pub(crate) mod mod_module; -use crate::context::PyFormatContext; -use ruff_formatter::format_element::tag::VerbatimKind; -use ruff_formatter::prelude::*; -use ruff_formatter::write; -use rustpython_parser::ast::{Mod, Ranged}; +#[derive(Default)] +pub struct FormatMod; -pub(crate) struct FormatModule<'a> { - module: &'a Mod, -} - -impl<'a> FormatModule<'a> { - pub(crate) fn new(module: &'a Mod) -> Self { - Self { module } +impl FormatRule> for FormatMod { + fn fmt(&self, item: &Mod, f: &mut PyFormatter) -> FormatResult<()> { + match item { + Mod::Module(x) => x.format().fmt(f), + Mod::Interactive(x) => x.format().fmt(f), + Mod::Expression(x) => x.format().fmt(f), + Mod::FunctionType(x) => x.format().fmt(f), + } } } -impl Format> for FormatModule<'_> { - fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { - let range = self.module.range(); - - write!(f, [source_position(range.start())])?; - - f.write_element(FormatElement::Tag(Tag::StartVerbatim( - VerbatimKind::Verbatim { - length: range.len(), - }, - )))?; - write!(f, [source_text_slice(range, ContainsNewlines::Detect)])?; - f.write_element(FormatElement::Tag(Tag::EndVerbatim))?; - - write!(f, [source_position(range.end())]) +impl<'ast> AsFormat> for Mod { + type Format<'a> = FormatRefWithRule<'a, Mod, FormatMod, PyFormatContext<'ast>>; + fn format(&self) -> Self::Format<'_> { + FormatRefWithRule::new(self, FormatMod::default()) + } +} + +impl<'ast> IntoFormat> for Mod { + type Format = FormatOwnedWithRule>; + fn into_format(self) -> Self::Format { + FormatOwnedWithRule::new(self, FormatMod::default()) } }