diff --git a/docs/cli/tasks.md b/docs/cli/tasks.md index 449cf1971a..09c88c838e 100644 --- a/docs/cli/tasks.md +++ b/docs/cli/tasks.md @@ -12,7 +12,7 @@ Manage tasks Task name to get info of -## Flags +## Global Flags ### `--no-header` diff --git a/docs/cli/tasks/ls.md b/docs/cli/tasks/ls.md index 8aadeba321..5147c4dd26 100644 --- a/docs/cli/tasks/ls.md +++ b/docs/cli/tasks/ls.md @@ -11,7 +11,7 @@ So if you have global tasks in `~/.config/mise/tasks/*` and project-specific tas ~/myproject/.mise/tasks/*, then they'll both be available but the project-specific tasks will override the global ones if they have the same name. -## Flags +## Global Flags ### `--no-header` diff --git a/e2e/tasks/test_task_ls b/e2e/tasks/test_task_ls index 674c8be9ab..de9caab568 100644 --- a/e2e/tasks/test_task_ls +++ b/e2e/tasks/test_task_ls @@ -29,7 +29,12 @@ cat <<'EOF' >mytasks/filetask2 EOF chmod +x mytasks/filetask2 -assert "mise task ls" "filetask2 ~/workdir/mytasks/filetask2 -lint ~/workdir/mise.toml -test ~/workdir/tasks.toml -test-with-args ~/workdir/tasks.toml" +assert "mise task ls" "filetask2 +lint +test +test-with-args" + +assert "mise task -x ls" "filetask2 ./mytasks/filetask2 +lint ./mise.toml +test ./tasks.toml +test-with-args ./tasks.toml" diff --git a/e2e/tasks/test_task_run_file b/e2e/tasks/test_task_run_file index 2c57754ae2..002fb9d8d7 100644 --- a/e2e/tasks/test_task_run_file +++ b/e2e/tasks/test_task_run_file @@ -66,8 +66,8 @@ console.log(`MYVAR: ${process.env.MYVAR}`); EOF chmod +x .mise/tasks/jstask assert "mise run jstask" "MYVAR: 1" -assert "mise tasks" "configtask ~/workdir/.mise.toml -filetask ~/workdir/.mise/tasks/filetask -jstask This is a test build script ~/workdir/.mise/tasks/jstask -lint ~/workdir/.mise.toml -test ~/workdir/.mise.toml" +assert "mise tasks -x" "configtask ./.mise.toml +filetask ./.mise/tasks/filetask +jstask ./.mise/tasks/jstask This is a test build script +lint ./.mise.toml +test ./.mise.toml" diff --git a/mise.usage.kdl b/mise.usage.kdl index e8ad9fe23f..e45ff0288b 100644 --- a/mise.usage.kdl +++ b/mise.usage.kdl @@ -1234,21 +1234,21 @@ cmd "tasks" help="Manage tasks" { $ mise tasks ls " - flag "--no-header" help="Do not print table header" - flag "-x --extended" help="Show all columns" - flag "--hidden" help="Show hidden tasks" - flag "--sort" help="Sort by column. Default is name." { + flag "--no-header" help="Do not print table header" global=true + flag "-x --extended" help="Show all columns" global=true + flag "--hidden" help="Show hidden tasks" global=true + flag "--sort" help="Sort by column. Default is name." global=true { arg "" { choices "name" "alias" "description" "source" } } - flag "--sort-order" help="Sort order. Default is asc." { + flag "--sort-order" help="Sort order. Default is asc." global=true { arg "" { choices "asc" "desc" } } - flag "-J --json" help="Output in JSON format" - flag "--usage" hide=true + flag "-J --json" help="Output in JSON format" global=true + flag "--usage" hide=true global=true arg "[TASK]" help="Task name to get info of" cmd "deps" help="Display a tree visualization of a dependency graph" { after_long_help r#"Examples: @@ -1322,21 +1322,21 @@ tasks will override the global ones if they have the same name." $ mise tasks ls " - flag "--no-header" help="Do not print table header" - flag "-x --extended" help="Show all columns" - flag "--hidden" help="Show hidden tasks" - flag "--sort" help="Sort by column. Default is name." { + flag "--no-header" help="Do not print table header" global=true + flag "-x --extended" help="Show all columns" global=true + flag "--hidden" help="Show hidden tasks" global=true + flag "--sort" help="Sort by column. Default is name." global=true { arg "" { choices "name" "alias" "description" "source" } } - flag "--sort-order" help="Sort order. Default is asc." { + flag "--sort-order" help="Sort order. Default is asc." global=true { arg "" { choices "asc" "desc" } } - flag "-J --json" help="Output in JSON format" - flag "--usage" hide=true + flag "-J --json" help="Output in JSON format" global=true + flag "--usage" hide=true global=true } cmd "run" help="Run task(s)" { alias "r" diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 93f83a1ab4..c8e11b823d 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -342,7 +342,11 @@ impl Cli { Ok(cmd) } else { if let Some(task) = self.task { - if Config::get().tasks()?.contains_key(&task) { + if Config::get() + .tasks()? + .iter() + .any(|(_, t)| t.is_match(&task)) + { return Ok(Commands::Run(run::Run { task, args: self.task_args.unwrap_or_default(), diff --git a/src/cli/tasks/ls.rs b/src/cli/tasks/ls.rs index a79b32c47d..06b6c74dc2 100644 --- a/src/cli/tasks/ls.rs +++ b/src/cli/tasks/ls.rs @@ -1,13 +1,11 @@ -use console::truncate_str; +use comfy_table::{Attribute, Cell, Row}; use eyre::Result; use itertools::Itertools; -use tabled::Tabled; use crate::config::Config; -use crate::file::display_path; +use crate::file::{display_path, display_rel_path}; use crate::task::Task; -use crate::ui::info::trim_line_end_whitespace; -use crate::ui::{style, table}; +use crate::ui::table::MiseTable; /// List available tasks to execute /// These may be included from the config file or from the project's .mise/tasks directory @@ -20,30 +18,30 @@ use crate::ui::{style, table}; #[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)] pub struct TasksLs { /// Do not print table header - #[clap(long, alias = "no-headers", verbatim_doc_comment)] + #[clap(long, alias = "no-headers", global = true, verbatim_doc_comment)] pub no_header: bool, /// Show all columns - #[clap(short = 'x', long, verbatim_doc_comment)] + #[clap(short = 'x', long, global = true, verbatim_doc_comment)] pub extended: bool, /// Show hidden tasks - #[clap(long, verbatim_doc_comment)] + #[clap(long, global = true, verbatim_doc_comment)] pub hidden: bool, /// Sort by column. Default is name. - #[clap(long, value_name = "COLUMN", verbatim_doc_comment)] + #[clap(long, global = true, value_name = "COLUMN", verbatim_doc_comment)] pub sort: Option, /// Sort order. Default is asc. - #[clap(long, verbatim_doc_comment)] + #[clap(long, global = true, verbatim_doc_comment)] pub sort_order: Option, /// Output in JSON format - #[clap(short = 'J', long, verbatim_doc_comment)] + #[clap(short = 'J', global = true, long, verbatim_doc_comment)] pub json: bool, - #[clap(long, hide = true)] + #[clap(long, global = true, hide = true)] pub usage: bool, } @@ -83,16 +81,18 @@ impl TasksLs { } fn display(&self, tasks: Vec) -> Result<()> { - let rows = tasks.iter().map(|t| t.into()).collect::>(); - let mut table = tabled::Table::new(rows); - table::default_style(&mut table, self.no_header); - // hide columns alias - if !self.extended { - table::disable_columns(&mut table, vec![1]); + let mut table = MiseTable::new( + self.no_header, + if self.extended { + &["Name", "Aliases", "Source", "Description"] + } else { + &["Name", "Description"] + }, + ); + for task in tasks { + table.add_row(self.task_to_row(&task)); } - let table = format!("{table}"); - miseprintln!("{}", trim_line_end_whitespace(&table)); - Ok(()) + table.print() } fn display_usage(&self, tasks: Vec) -> Result<()> { @@ -117,7 +117,7 @@ impl TasksLs { .filter(|t| self.hidden || !t.hide) .map(|task| { let mut inner = serde_json::Map::new(); - inner.insert("name".to_string(), task.name.into()); + inner.insert("name".to_string(), task.display_name().into()); if !task.aliases.is_empty() { inner.insert("aliases".to_string(), task.aliases.join(", ").into()); } @@ -149,39 +149,18 @@ impl TasksLs { _ => cmp, } } -} - -#[derive(Tabled)] -#[tabled(rename_all = "PascalCase")] -struct Row { - name: String, - alias: String, - description: String, - // command: String, - source: String, -} -impl From<&Task> for Row { - fn from(task: &Task) -> Self { - // let cmd = tasks.command_string().unwrap_or_default(); - Self { - name: style::nbold(&task.name).bright().to_string(), - alias: style::ndim(&task.aliases.join(", ")).dim().to_string(), - description: style::nblue(truncate(&task.description, 40)).to_string(), - // command: style::ndim(truncate(&cmd, 20)).dim().to_string(), - source: display_path(&task.config_source), + fn task_to_row(&self, task: &Task) -> Row { + let mut row = vec![Cell::new(task.display_name()).add_attribute(Attribute::Bold)]; + if self.extended { + row.push(Cell::new(task.aliases.join(", "))); + row.push(Cell::new(display_rel_path(&task.config_source))); } + row.push(Cell::new(&task.description).add_attribute(Attribute::Dim)); + row.into() } } -fn first_line(s: &str) -> &str { - s.lines().next().unwrap_or_default() -} - -fn truncate(s: &str, len: usize) -> String { - first_line(&truncate_str(s, len, "…")).to_string() -} - // TODO: fill this out static AFTER_LONG_HELP: &str = color_print::cstr!( r#"Examples: diff --git a/src/task/mod.rs b/src/task/mod.rs index 8656e1e577..7fd4eac46c 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -182,6 +182,23 @@ impl Task { Ok(task) } + /// prints the task name without an extension + pub fn display_name(&self) -> String { + self.name + .rsplitn(2, '.') + .last() + .unwrap_or_default() + .to_string() + } + + pub fn is_match(&self, pat: &str) -> bool { + if self.name == pat || self.aliases.contains(&pat.to_string()) { + return true; + } + let pat = pat.rsplitn(2, '.').last().unwrap_or_default(); + self.name == pat || self.aliases.contains(&pat.to_string()) + } + pub fn task_dir() -> PathBuf { let config = Config::get(); let cwd = dirs::CWD.clone().unwrap_or_default(); diff --git a/src/ui/style.rs b/src/ui/style.rs index 064c285219..2136e22c79 100644 --- a/src/ui/style.rs +++ b/src/ui/style.rs @@ -1,6 +1,5 @@ use std::path::Path; -use crate::env::TERM_WIDTH; use crate::file::display_path; use console::{style, StyledObject}; @@ -64,18 +63,10 @@ pub fn nstyle(val: D) -> StyledObject { style(val).for_stdout() } -pub fn nblue(val: D) -> StyledObject { - nstyle(val).blue() -} - pub fn ncyan(val: D) -> StyledObject { nstyle(val).cyan() } -pub fn nbold(val: D) -> StyledObject { - nstyle(val).bold() -} - pub fn nunderline(val: D) -> StyledObject { nstyle(val).underlined() } @@ -91,8 +82,3 @@ pub fn nred(val: D) -> StyledObject { pub fn ndim(val: D) -> StyledObject { nstyle(val).dim() } - -#[allow(unused)] -pub fn truncate_str(s: impl AsRef) -> String { - console::truncate_str(s.as_ref(), *TERM_WIDTH - 14, "…").to_string() -} diff --git a/src/ui/table.rs b/src/ui/table.rs index cae462b4c9..a4b0a4d4f9 100644 --- a/src/ui/table.rs +++ b/src/ui/table.rs @@ -41,12 +41,6 @@ pub fn default_style(table: &mut Table, no_headers: bool) { .with(Modify::new(Columns::last()).with(Padding::zero())); } -pub fn disable_columns(table: &mut Table, col_idxs: Vec) { - for idx in col_idxs { - table.with(Remove::column(Columns::single(idx))); - } -} - pub struct MiseTable { table: comfy_table::Table, truncate: bool,