List groups in source order with just --groups --unsorted (#2160)

This commit is contained in:
Casey Rodarmor 2024-06-14 13:35:03 -07:00 committed by GitHub
parent 5f91b37c82
commit b05a75d168
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 124 additions and 31 deletions

View File

@ -1861,6 +1861,8 @@ Recipe groups:
rust recipes rust recipes
``` ```
Use `just --groups --unsorted` to print groups in their justfile order.
### Command Evaluation Using Backticks ### Command Evaluation Using Backticks
Backticks can be used to store the result of commands: Backticks can be used to store the result of commands:

View File

@ -4,7 +4,7 @@ use super::*;
pub(crate) type Assignment<'src> = Binding<'src, Expression<'src>>; pub(crate) type Assignment<'src> = Binding<'src, Expression<'src>>;
impl<'src> Display for Assignment<'src> { impl<'src> Display for Assignment<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.export { if self.export {
write!(f, "export ")?; write!(f, "export ")?;
} }

View File

@ -12,7 +12,7 @@ pub(crate) struct Ast<'src> {
} }
impl<'src> Display for Ast<'src> { impl<'src> Display for Ast<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut iter = self.items.iter().peekable(); let mut iter = self.items.iter().peekable();
while let Some(item) = iter.next() { while let Some(item) = iter.next() {

View File

@ -110,7 +110,7 @@ impl<'src> Attribute<'src> {
} }
impl<'src> Display for Attribute<'src> { impl<'src> Display for Attribute<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.name())?; write!(f, "{}", self.name())?;
if let Some(argument) = self.argument() { if let Some(argument) = self.argument() {
write!(f, "({argument})")?; write!(f, "({argument})")?;

View File

@ -28,7 +28,7 @@ fn capitalize(s: &str) -> String {
} }
impl Display for CompileError<'_> { impl Display for CompileError<'_> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use CompileErrorKind::*; use CompileErrorKind::*;
match &*self.kind { match &*self.kind {

View File

@ -8,7 +8,7 @@ pub(crate) struct Condition<'src> {
} }
impl<'src> Display for Condition<'src> { impl<'src> Display for Condition<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{} {} {}", self.lhs, self.operator, self.rhs) write!(f, "{} {} {}", self.lhs, self.operator, self.rhs)
} }
} }

View File

@ -8,7 +8,7 @@ pub(crate) struct Dependency<'src> {
} }
impl<'src> Display for Dependency<'src> { impl<'src> Display for Dependency<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.arguments.is_empty() { if self.arguments.is_empty() {
write!(f, "{}", self.recipe.name()) write!(f, "{}", self.recipe.name())
} else { } else {

View File

@ -51,7 +51,7 @@ impl<'src> Expression<'src> {
} }
impl<'src> Display for Expression<'src> { impl<'src> Display for Expression<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::Assert { condition, error } => write!(f, "assert({condition}, {error})"), Self::Assert { condition, error } => write!(f, "assert({condition}, {error})"),
Self::Backtick { token, .. } => write!(f, "{}", token.lexeme()), Self::Backtick { token, .. } => write!(f, "{}", token.lexeme()),

View File

@ -365,13 +365,13 @@ impl<'src> Justfile<'src> {
modules modules
} }
pub(crate) fn public_recipes(&self, config: &Config) -> Vec<&Recipe<'src, Dependency>> { pub(crate) fn public_recipes(&self, config: &Config) -> Vec<&Recipe> {
let mut recipes = self let mut recipes = self
.recipes .recipes
.values() .values()
.map(AsRef::as_ref) .map(AsRef::as_ref)
.filter(|recipe| recipe.is_public()) .filter(|recipe| recipe.is_public())
.collect::<Vec<&Recipe<Dependency>>>(); .collect::<Vec<&Recipe>>();
if config.unsorted { if config.unsorted {
recipes.sort_by_key(|recipe| (&recipe.import_offsets, recipe.name.offset)); recipes.sort_by_key(|recipe| (&recipe.import_offsets, recipe.name.offset));
@ -380,19 +380,33 @@ impl<'src> Justfile<'src> {
recipes recipes
} }
pub(crate) fn public_groups(&self) -> BTreeSet<String> { pub(crate) fn public_groups(&self, config: &Config) -> Vec<String> {
self let mut groups = Vec::new();
.recipes
.values() for recipe in self.recipes.values() {
.map(AsRef::as_ref) if recipe.is_public() {
.filter(|recipe| recipe.is_public()) for group in recipe.groups() {
.flat_map(Recipe::groups) groups.push((&recipe.import_offsets, recipe.name.offset, group));
.collect() }
}
}
if config.unsorted {
groups.sort();
} else {
groups.sort_by(|(_, _, a), (_, _, b)| a.cmp(b));
}
let mut seen = HashSet::new();
groups.retain(|(_, _, group)| seen.insert(group.clone()));
groups.into_iter().map(|(_, _, group)| group).collect()
} }
} }
impl<'src> ColorDisplay for Justfile<'src> { impl<'src> ColorDisplay for Justfile<'src> {
fn fmt(&self, f: &mut Formatter, color: Color) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result {
let mut items = self.recipes.len() + self.assignments.len() + self.aliases.len(); let mut items = self.recipes.len() + self.assignments.len() + self.aliases.len();
for (name, assignment) in &self.assignments { for (name, assignment) in &self.assignments {
if assignment.export { if assignment.export {

View File

@ -15,7 +15,7 @@ pub(crate) enum OutputError {
} }
impl Display for OutputError { impl Display for OutputError {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self { match *self {
Self::Code(code) => write!(f, "Process exited with status code {code}"), Self::Code(code) => write!(f, "Process exited with status code {code}"),
Self::Io(ref io_error) => write!(f, "Error executing process: {io_error}"), Self::Io(ref io_error) => write!(f, "Error executing process: {io_error}"),

View File

@ -14,7 +14,7 @@ pub(crate) struct Parameter<'src> {
} }
impl<'src> ColorDisplay for Parameter<'src> { impl<'src> ColorDisplay for Parameter<'src> {
fn fmt(&self, f: &mut Formatter, color: Color) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result {
if let Some(prefix) = self.kind.prefix() { if let Some(prefix) = self.kind.prefix() {
write!(f, "{}", color.annotation().paint(prefix))?; write!(f, "{}", color.annotation().paint(prefix))?;
} }

View File

@ -476,7 +476,7 @@ impl<'src, D> Recipe<'src, D> {
} }
impl<'src, D: Display> ColorDisplay for Recipe<'src, D> { impl<'src, D: Display> ColorDisplay for Recipe<'src, D> {
fn fmt(&self, f: &mut Formatter, color: Color) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result {
if let Some(doc) = self.doc { if let Some(doc) = self.doc {
writeln!(f, "# {doc}")?; writeln!(f, "# {doc}")?;
} }

View File

@ -13,7 +13,7 @@ impl<'src> Keyed<'src> for Set<'src> {
} }
impl<'src> Display for Set<'src> { impl<'src> Display for Set<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "set {} := {}", self.name, self.value) write!(f, "set {} := {}", self.name, self.value)
} }
} }

View File

@ -20,7 +20,7 @@ pub(crate) enum Setting<'src> {
} }
impl<'src> Display for Setting<'src> { impl<'src> Display for Setting<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::AllowDuplicateRecipes(value) Self::AllowDuplicateRecipes(value)
| Self::AllowDuplicateVariables(value) | Self::AllowDuplicateVariables(value)

View File

@ -7,7 +7,7 @@ pub(crate) struct Shell<'src> {
} }
impl<'src> Display for Shell<'src> { impl<'src> Display for Shell<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "[{}", self.command)?; write!(f, "[{}", self.command)?;
for argument in &self.arguments { for argument in &self.arguments {

View File

@ -97,7 +97,7 @@ impl Subcommand {
fn groups(config: &Config, justfile: &Justfile) { fn groups(config: &Config, justfile: &Justfile) {
println!("Recipe groups:"); println!("Recipe groups:");
for group in justfile.public_groups() { for group in justfile.public_groups(config) {
println!("{}{group}", config.list_prefix); println!("{}{group}", config.list_prefix);
} }
} }
@ -215,7 +215,7 @@ impl Subcommand {
overrides: &BTreeMap<String, String>, overrides: &BTreeMap<String, String>,
chooser: Option<&str>, chooser: Option<&str>,
) -> Result<(), Error<'src>> { ) -> Result<(), Error<'src>> {
let mut recipes = Vec::<&Recipe<Dependency>>::new(); let mut recipes = Vec::<&Recipe>::new();
let mut stack = vec![justfile]; let mut stack = vec![justfile];
while let Some(module) = stack.pop() { while let Some(module) = stack.pop() {
recipes.extend( recipes.extend(

View File

@ -39,7 +39,7 @@ pub(crate) enum TokenKind {
} }
impl Display for TokenKind { impl Display for TokenKind {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use TokenKind::*; use TokenKind::*;
write!( write!(
f, f,

View File

@ -7,7 +7,7 @@ pub(crate) struct UnresolvedDependency<'src> {
} }
impl<'src> Display for UnresolvedDependency<'src> { impl<'src> Display for UnresolvedDependency<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.arguments.is_empty() { if self.arguments.is_empty() {
write!(f, "{}", self.recipe) write!(f, "{}", self.recipe)
} else { } else {

View File

@ -157,12 +157,89 @@ fn list_groups_with_shorthand_syntax() {
bar: bar:
", ",
) )
.args(["--groups", "--list-prefix", "..."]) .arg("--groups")
.stdout( .stdout(
" "
Recipe groups: Recipe groups:
...A A
...B B
",
)
.run();
}
#[test]
fn list_groups_unsorted() {
Test::new()
.justfile(
"
[group: 'Z']
baz:
[group: 'B']
foo:
[group: 'A', group: 'B']
bar:
",
)
.args(["--groups", "--unsorted"])
.stdout(
"
Recipe groups:
Z
B
A
",
)
.run();
}
#[test]
fn list_groups_private_unsorted() {
Test::new()
.justfile(
"
[private]
[group: 'A']
foo:
[group: 'B']
bar:
[group: 'A']
baz:
",
)
.args(["--groups", "--unsorted"])
.stdout(
"
Recipe groups:
B
A
",
)
.run();
}
#[test]
fn list_groups_private() {
Test::new()
.justfile(
"
[private]
[group: 'A']
foo:
[group: 'B']
bar:
",
)
.args(["--groups", "--unsorted"])
.stdout(
"
Recipe groups:
B
", ",
) )
.run(); .run();