From d04e9712836400290ea93013b413a4dba798410c Mon Sep 17 00:00:00 2001 From: Arian Rezazadeh Date: Tue, 7 Jan 2025 15:38:22 +0330 Subject: [PATCH] feat: format files in parallel --- crates/cli/src/commands/format.rs | 50 +++++++++++-------- .../format__format_patterns.snap.new | 6 +++ 2 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 crates/cli_bin/tests/snapshots/format__format_patterns.snap.new diff --git a/crates/cli/src/commands/format.rs b/crates/cli/src/commands/format.rs index f091f60e6..69a359307 100644 --- a/crates/cli/src/commands/format.rs +++ b/crates/cli/src/commands/format.rs @@ -1,12 +1,13 @@ use crate::{ resolver::{resolve_from_cwd, Source}, - ux::format_diff, + ux::{format_diff, DiffString}, }; use anyhow::{anyhow, ensure, Context, Result}; use biome_grit_formatter::context::GritFormatOptions; use clap::Args; use colored::Colorize; use marzano_gritmodule::{config::ResolvedGritDefinition, parser::PatternFileExt}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; use serde::Serialize; #[derive(Args, Debug, Serialize, Clone)] @@ -17,16 +18,28 @@ pub struct FormatArgs { } pub async fn run_format(arg: &FormatArgs) -> Result<()> { - let (mut resolved, _) = resolve_from_cwd(&Source::Local).await?; - // sort to have consistent output for tests - resolved.sort(); + let (resolved, _) = resolve_from_cwd(&Source::Local).await?; let file_path_to_resolved = group_resolved_patterns_by_group(resolved); - for (file_path, resolved_patterns) in file_path_to_resolved { - if let Err(error) = - format_file_resolved_patterns(file_path.clone(), resolved_patterns, arg.clone()).await - { - eprintln!("couldn't format '{}': {error:?}", file_path) + let mut results = file_path_to_resolved + .into_par_iter() + .map(|(file_path, resolved_patterns)| { + ( + file_path.clone(), + format_file_resolved_patterns(file_path, resolved_patterns, arg.clone()), + ) + }) + .collect::>(); + + // sort outputs to ensure consistent stdout output + // also avoid using sort_by_key to prevent additional cloning of file_path + results.sort_by(|(file_path, _), (other_file_path, _)| file_path.cmp(&other_file_path)); + + for (file_path, result) in results { + match result { + Err(error) => eprintln!("couldn't format '{}': {error:?}", file_path), + Ok(Some(diff)) => println!("{}:\n{}", file_path.bold(), diff), + Ok(None) => (), // `args.write` is true or file is already formated } } Ok(()) @@ -49,11 +62,11 @@ fn group_resolved_patterns_by_group( }) } -async fn format_file_resolved_patterns( +fn format_file_resolved_patterns( file_path: String, patterns: Vec, arg: FormatArgs, -) -> Result<()> { +) -> Result> { let first_pattern = patterns .first() .ok_or_else(|| anyhow!("patterns is empty"))?; @@ -77,22 +90,15 @@ async fn format_file_resolved_patterns( }; if &new_file_content == old_file_content { - return Ok(()); + return Ok(None); } if arg.write { - tokio::fs::write(file_path, new_file_content) - .await - .with_context(|| "could not write to file")?; + std::fs::write(file_path, new_file_content).with_context(|| "could not write to file")?; + Ok(None) } else { - println!( - "{}:\n{}", - file_path.bold(), - format_diff(old_file_content, &new_file_content) - ); + Ok(Some(format_diff(old_file_content, &new_file_content))) } - - Ok(()) } fn format_yaml_file(file_content: &str) -> Result { diff --git a/crates/cli_bin/tests/snapshots/format__format_patterns.snap.new b/crates/cli_bin/tests/snapshots/format__format_patterns.snap.new new file mode 100644 index 000000000..dc6d643d8 --- /dev/null +++ b/crates/cli_bin/tests/snapshots/format__format_patterns.snap.new @@ -0,0 +1,6 @@ +--- +source: crates/cli_bin/tests/format.rs +assertion_line: 23 +expression: "String::from_utf8(output.stdout)?" +--- +".grit/./others/test_move_import.md:\n tags: [private]\n ---\n ```grit\n -language js\n -\n +language js;\n `sanitizeFilePath` as $s where {\n - move_import(`sanitizeFilePath`, `'@getgrit/universal'`)\n +\tmove_import(`sanitizeFilePath`, `'@getgrit/universal'`)\n }\n ```\n \n\n.grit/grit.yaml:\n version: 0.0.1\n patterns:\n - - name: aspect_ratio_yaml\n - description: Yaml version of aspect_ratio.md\n - body: |\n - language css\n -\n - `a { $props }` where {\n - $props <: contains `aspect-ratio: $x`\n - }\n +- name: aspect_ratio_yaml\n + description: Yaml version of aspect_ratio.md\n + body: |+\n + language css;\n + `a { $props }` where { $props <: contains `aspect-ratio: $x` }\n \n - - file: ./others/test_move_import.md\n +- file: ./others/test_move_import.md\n \n\n.grit/patterns/aspect_ratio.md:\n ---\n \n ```grit\n -language css\n -\n -`a { $props }` where {\n - $props <: contains `aspect-ratio: $x`\n -}\n +language css;\n +`a { $props }` where { $props <: contains `aspect-ratio: $x` }\n ```\n \n ## Matches the right selector and declaration block\n \n\n.grit/patterns/dependency.grit:\n -language json\n -\n +language json;\n pattern upgrade_dependency($target_dep, $target_version, $dependency_key) {\n - or {\n - `$key: $value` where {\n - $key <: `\"$target_dep\"`,\n - $value => `\"$target_version\"`\n - },\n - pair($key, $value) where {\n - $key <: `\"$dependency_key\"`,\n - $value <: object($properties) where {\n - $properties <: not contains pair(key=$dep_key) where {\n - $dep_key <: contains `$target_dep`\n - },\n - $properties => `\"$target_dep\": \"$target_version\",\\n$properties`\n - }\n - }\n - }\n -}\n +\tor {\n +\t\t`$key: $value` where {\n +\t\t\t$key <: `\"$target_dep\"`,\n +\t\t\t$value => `\"$target_version\"`\n +\t\t},\n +\t\tpair($key, $value) where {\n +\t\t\t$key <: `\"$dependency_key\"`,\n +\t\t\t$value <: object($properties) where {\n +\t\t\t\t$properties <: notcontains pair(key = $dep_key) where {\n +\t\t\t\t\t$dep_key <: contains `$target_dep`\n +\t\t\t\t},\n +\t\t\t\t$properties => `\"$target_dep\": \"$target_version\",\\n$properties`\n +\t\t\t}\n +\t\t}\n +\t}}\n \n pattern console_method_to_info($method) {\n - `console.$method($message)` => `console.info($message)`\n -}\n +\t`console.$method($message)` => `console.info($message)`}\n \n\n"