use anyhow::Result;
use crate::cli::ListArgs;
use crate::context::Context;
use crate::fmt::col_width;
use crate::style;
use crate::worktree::{self, RepoWorktree, Worktree};
pub fn run(ctx: &Context, args: &ListArgs) -> Result<()> {
if args.all {
run_all(ctx, args.verbose)
} else {
run_single(ctx, args.verbose)
}
}
fn run_single(ctx: &Context, verbose: bool) -> Result<()> {
let repo = ctx.repo()?;
let trees = worktree::list(&repo)?;
if ctx.json {
println!("{}", serde_json::to_string_pretty(&trees)?);
} else {
print_single(&trees, verbose);
}
Ok(())
}
fn run_all(ctx: &Context, verbose: bool) -> Result<()> {
let global = ctx.global()?;
if global.projects_roots.is_empty() {
anyhow::bail!(
"--all: no projects.roots configured; \
try: set `projects.roots = [\"~/dev/myrepos\"]` in ~/.config/limb/config.toml"
);
}
let rows = worktree::list_all(&global.projects_roots);
if ctx.json {
println!("{}", serde_json::to_string_pretty(&rows)?);
} else {
print_all(&rows, verbose);
}
Ok(())
}
fn print_single(trees: &[Worktree], verbose: bool) {
if trees.is_empty() {
anstream::eprintln!("no worktrees");
return;
}
let name_w = col_width(trees, |w| w.name.len(), 4);
let branch_w = col_width(
trees,
|w| w.branch.as_deref().unwrap_or("(detached)").len(),
6,
);
let h = style::DIM;
anstream::println!("{h}{:<name_w$} {:<branch_w$} PATH{h:#}", "NAME", "BRANCH");
for w in trees {
let branch = w.branch.as_deref().unwrap_or("(detached)");
anstream::println!(
"{:<name_w$} {:<branch_w$} {}{}",
w.name,
branch,
w.path.display(),
render_tag(w, verbose),
);
}
}
fn print_all(rows: &[RepoWorktree], verbose: bool) {
if rows.is_empty() {
anstream::eprintln!("no worktrees");
return;
}
let repo_w = col_width(rows, |r| r.repo.len(), 4);
let name_w = col_width(rows, |r| r.worktree.name.len(), 4);
let branch_w = col_width(
rows,
|r| r.worktree.branch.as_deref().unwrap_or("(detached)").len(),
6,
);
let h = style::DIM;
anstream::println!(
"{h}{:<repo_w$} {:<name_w$} {:<branch_w$} PATH{h:#}",
"REPO",
"NAME",
"BRANCH"
);
let a = style::ACCENT;
for r in rows {
anstream::println!(
"{a}{:<repo_w$}{a:#} {:<name_w$} {:<branch_w$} {}{}",
r.repo,
r.worktree.name,
r.worktree.branch.as_deref().unwrap_or("(detached)"),
r.worktree.path.display(),
render_tag(&r.worktree, verbose),
);
}
}
fn render_tag(w: &Worktree, verbose: bool) -> String {
let (label, reason) = if w.bare {
("bare", None)
} else if w.locked {
("locked", w.locked_reason.as_deref())
} else if w.prunable {
("prunable", w.prunable_reason.as_deref())
} else {
return String::new();
};
let s = style::WARN;
if verbose
&& let Some(r) = reason
&& !r.is_empty()
{
format!(" {s}({label}: {r}){s:#}")
} else {
format!(" {s}({label}){s:#}")
}
}