Skip to main content

tmx/ui/
worktree_picker.rs

1use anyhow::Result;
2use ratatui::style::Style;
3use ratatui::text::{Line, Span};
4
5use crate::ui::picker::{PickerConfig, PickerItem, run_picker};
6use crate::ui::theme;
7use crate::worktree::{WorktreeEntry, short_branch};
8
9impl PickerItem for WorktreeEntry {
10    fn fuzzy_key(&self) -> &str {
11        // Prefer short branch name for the fuzzy key; fall back to path.
12        if let Some(b) = self.branch.as_deref() {
13            short_branch(b)
14        } else {
15            // path may not be valid utf8 in pathological cases; PickerItem
16            // requires &str so we lean on the lossy view via a stash.
17            // We can't easily return a &str into a temporary String here, so
18            // tradeoff: fall back to a static placeholder when path isn't utf8.
19            self.path.to_str().unwrap_or("(detached)")
20        }
21    }
22
23    fn render(&self, is_cursor: bool) -> Line<'static> {
24        let (cursor_marker, name_style) = if is_cursor {
25            (
26                Span::styled("▸ ", Style::default().fg(theme::VIOLET)),
27                theme::selected_style(),
28            )
29        } else {
30            (Span::raw("  "), theme::normal_style())
31        };
32
33        let label = match self.branch.as_deref() {
34            Some(b) => short_branch(b).to_string(),
35            None => "(detached)".to_string(),
36        };
37
38        let mut spans = vec![
39            cursor_marker,
40            Span::styled(label, name_style),
41            Span::styled(format!("  {}", self.path.display()), theme::muted_style()),
42        ];
43        if self.locked {
44            spans.push(Span::styled(
45                " [locked]",
46                Style::default().fg(theme::VIOLET),
47            ));
48        }
49        if self.prunable {
50            spans.push(Span::styled(" [prunable]", theme::muted_style()));
51        }
52        Line::from(spans)
53    }
54}
55
56pub fn run_worktree_picker(entries: Vec<WorktreeEntry>) -> Result<Option<WorktreeEntry>> {
57    run_picker(
58        entries,
59        PickerConfig {
60            title: "git worktrees",
61            search_placeholder: "Search worktrees...",
62            empty_message: "No worktrees",
63            item_noun: "worktrees",
64        },
65    )
66}