From d6b2e6bad2039834d2da8fb465d7956b535dfe4d Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 20 May 2024 00:25:18 -0700 Subject: [PATCH] Fix submodule recipe listing indentation (#2063) --- src/lib.rs | 16 +++++---- src/recipe_signature.rs | 16 +++++++++ src/subcommand.rs | 58 +++++++++++++++--------------- tests/lib.rs | 1 + tests/list.rs | 79 +++++++++++++++++++++++++++++++++++++++++ tests/modules.rs | 53 --------------------------- 6 files changed, 135 insertions(+), 88 deletions(-) create mode 100644 src/recipe_signature.rs create mode 100644 tests/list.rs diff --git a/src/lib.rs b/src/lib.rs index 9894e92..74e85dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,13 +29,14 @@ pub(crate) use { output::output, output_error::OutputError, parameter::Parameter, parameter_kind::ParameterKind, parser::Parser, platform::Platform, platform_interface::PlatformInterface, position::Position, positional::Positional, ran::Ran, range_ext::RangeExt, recipe::Recipe, - recipe_context::RecipeContext, recipe_resolver::RecipeResolver, scope::Scope, search::Search, - search_config::SearchConfig, search_error::SearchError, set::Set, setting::Setting, - settings::Settings, shebang::Shebang, shell::Shell, show_whitespace::ShowWhitespace, - source::Source, string_kind::StringKind, string_literal::StringLiteral, subcommand::Subcommand, - suggestion::Suggestion, table::Table, thunk::Thunk, token::Token, token_kind::TokenKind, - unresolved_dependency::UnresolvedDependency, unresolved_recipe::UnresolvedRecipe, - use_color::UseColor, variables::Variables, verbosity::Verbosity, warning::Warning, + recipe_context::RecipeContext, recipe_resolver::RecipeResolver, + recipe_signature::RecipeSignature, scope::Scope, search::Search, search_config::SearchConfig, + search_error::SearchError, set::Set, setting::Setting, settings::Settings, shebang::Shebang, + shell::Shell, show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind, + string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table, + thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency, + unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables, + verbosity::Verbosity, warning::Warning, }, std::{ borrow::Cow, @@ -168,6 +169,7 @@ mod range_ext; mod recipe; mod recipe_context; mod recipe_resolver; +mod recipe_signature; mod run; mod scope; mod search; diff --git a/src/recipe_signature.rs b/src/recipe_signature.rs new file mode 100644 index 0000000..d805ba6 --- /dev/null +++ b/src/recipe_signature.rs @@ -0,0 +1,16 @@ +use super::*; + +pub(crate) struct RecipeSignature<'a> { + pub(crate) name: &'a str, + pub(crate) recipe: &'a Recipe<'a>, +} + +impl<'a> ColorDisplay for RecipeSignature<'a> { + fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result { + write!(f, "{}", self.name)?; + for parameter in &self.recipe.parameters { + write!(f, " {}", parameter.color_display(color))?; + } + Ok(()) + } +} diff --git a/src/subcommand.rs b/src/subcommand.rs index ddc1ac0..ce4e756 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -466,8 +466,12 @@ impl Subcommand { fn list(config: &Config, level: usize, justfile: &Justfile) { const MAX_WIDTH: usize = 50; + if level == 0 { + print!("{}", config.list_heading); + } + // Construct a target to alias map. - let mut recipe_aliases: BTreeMap<&str, Vec<&str>> = BTreeMap::new(); + let mut recipe_aliases = BTreeMap::<&str, Vec<&str>>::new(); if !config.no_aliases { for alias in justfile.aliases.values() { if alias.is_private() { @@ -483,7 +487,7 @@ impl Subcommand { } } - let mut line_widths: BTreeMap<&str, usize> = BTreeMap::new(); + let mut line_widths = BTreeMap::<&str, usize>::new(); for (name, recipe) in &justfile.recipes { if !recipe.is_public() { @@ -491,26 +495,24 @@ impl Subcommand { } for name in iter::once(name).chain(recipe_aliases.get(name).unwrap_or(&Vec::new())) { - let mut line_width = UnicodeWidthStr::width(*name); - - for parameter in &recipe.parameters { - line_width += UnicodeWidthStr::width( - format!(" {}", parameter.color_display(Color::never())).as_str(), - ); - } - - if line_width <= MAX_WIDTH { - line_widths.insert(name, line_width); - } + line_widths.insert( + name, + UnicodeWidthStr::width( + RecipeSignature { name, recipe } + .color_display(Color::never()) + .to_string() + .as_str(), + ), + ); } } - let max_line_width = line_widths.values().copied().max().unwrap_or_default(); - let doc_color = config.color.stdout().doc(); - - if level == 0 { - print!("{}", config.list_heading); - } + let max_line_width = line_widths + .values() + .filter(|line_width| **line_width <= MAX_WIDTH) + .copied() + .max() + .unwrap_or_default(); for recipe in justfile.public_recipes(config.unsorted) { let name = recipe.name(); @@ -519,10 +521,11 @@ impl Subcommand { .chain(recipe_aliases.get(name).unwrap_or(&Vec::new())) .enumerate() { - print!("{}{name}", config.list_prefix.repeat(level + 1)); - for parameter in &recipe.parameters { - print!(" {}", parameter.color_display(config.color.stdout())); - } + print!( + "{}{}", + config.list_prefix.repeat(level + 1), + RecipeSignature { name, recipe }.color_display(config.color.stdout()) + ); let doc = match (i, recipe.doc) { (0, Some(doc)) => Some(Cow::Borrowed(doc)), @@ -534,10 +537,9 @@ impl Subcommand { print!( " {:padding$}{} {}", "", - doc_color.paint("#"), - doc_color.paint(&doc), - padding = max_line_width - .saturating_sub(line_widths.get(name).copied().unwrap_or(max_line_width)) + config.color.stdout().doc().paint("#"), + config.color.stdout().doc().paint(&doc), + padding = max_line_width.saturating_sub(line_widths[name]), ); } @@ -546,7 +548,7 @@ impl Subcommand { } for (name, module) in &justfile.modules { - println!(" {name}:"); + println!("{}{name}:", config.list_prefix.repeat(level + 1)); Self::list(config, level + 1, module); } } diff --git a/tests/lib.rs b/tests/lib.rs index 806cdca..a3d18a5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -69,6 +69,7 @@ mod interrupts; mod invocation_directory; mod json; mod line_prefixes; +mod list; mod man; mod misc; mod modules; diff --git a/tests/list.rs b/tests/list.rs new file mode 100644 index 0000000..cf93410 --- /dev/null +++ b/tests/list.rs @@ -0,0 +1,79 @@ +use super::*; + +#[test] +fn list_displays_recipes_in_submodules() { + Test::new() + .write("foo.just", "bar:\n @echo FOO") + .justfile( + " + mod foo + ", + ) + .test_round_trip(false) + .arg("--unstable") + .arg("--list") + .stdout( + " + Available recipes: + foo: + bar + ", + ) + .run(); +} + +#[test] +fn module_recipe_list_alignment_ignores_private_recipes() { + Test::new() + .write( + "foo.just", + " +# foos +foo: + @echo FOO + +[private] +barbarbar: + @echo BAR + +@_bazbazbaz: + @echo BAZ + ", + ) + .justfile("mod foo") + .test_round_trip(false) + .arg("--unstable") + .arg("--list") + .stdout( + " + Available recipes: + foo: + foo # foos + ", + ) + .run(); +} + +#[test] +fn nested_modules_are_properly_indented() { + Test::new() + .write("foo.just", "mod bar") + .write("bar.just", "baz:\n @echo FOO") + .justfile( + " + mod foo + ", + ) + .test_round_trip(false) + .arg("--unstable") + .arg("--list") + .stdout( + " + Available recipes: + foo: + bar: + baz + ", + ) + .run(); +} diff --git a/tests/modules.rs b/tests/modules.rs index 4c10b41..b9089ae 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -512,28 +512,6 @@ fn missing_optional_modules_do_not_conflict() { .run(); } -#[test] -fn list_displays_recipes_in_submodules() { - Test::new() - .write("foo.just", "bar:\n @echo FOO") - .justfile( - " - mod foo - ", - ) - .test_round_trip(false) - .arg("--unstable") - .arg("--list") - .stdout( - " - Available recipes: - foo: - bar - ", - ) - .run(); -} - #[test] fn root_dotenv_is_available_to_submodules() { Test::new() @@ -695,37 +673,6 @@ fn module_paths_beginning_with_tilde_are_expanded_to_homdir() { .run(); } -#[test] -fn module_recipe_list_alignment_ignores_private_recipes() { - Test::new() - .write( - "foo.just", - " -# foos -foo: - @echo FOO - -[private] -barbarbar: - @echo BAR - -@_bazbazbaz: - @echo BAZ - ", - ) - .justfile("mod foo") - .test_round_trip(false) - .arg("--unstable") - .arg("--list") - .stdout( - "Available recipes: - foo: - foo # foos -", - ) - .run(); -} - #[test] fn recipes_with_same_name_are_both_run() { Test::new()