Implement TUI colon command mode

This commit is contained in:
Hermes Agent
2026-08-10 08:28:55 +00:00
parent 639ae0d08e
commit d66d9b0f08
7 changed files with 1502 additions and 89 deletions

View File

@@ -160,6 +160,47 @@ impl Sidebar {
self.selected.as_ref()
}
pub fn select_path(&mut self, path: &str, directory: bool) -> bool {
let target = collect_ids(&self.full)
.into_iter()
.find(|id| id.path() == path && id.is_directory() == directory);
let Some(target) = target else {
return false;
};
if !path.is_empty() {
let mut prefix = String::new();
for component in path
.split('/')
.take(path.split('/').count().saturating_sub(1))
{
if !prefix.is_empty() {
prefix.push('/');
}
prefix.push_str(component);
self.expanded.insert(NodeId {
path: prefix.clone(),
directory: true,
});
}
}
self.selected = Some(target);
self.ensure_selected_visible();
true
}
pub fn completion_paths(&self) -> Vec<String> {
fn visit(nodes: &[SidebarNode], paths: &mut Vec<String>) {
for node in nodes {
paths.push(node.id.path.clone());
visit(&node.children, paths);
}
}
let mut paths = Vec::new();
visit(&self.full, &mut paths);
paths
}
pub fn set_viewport_height(&mut self, height: usize) {
self.viewport_height = height.max(1);
self.ensure_selected_visible();
@@ -574,6 +615,31 @@ mod tests {
);
}
#[test]
fn command_selection_expands_ancestors_and_completion_uses_the_full_tree() {
let mut sidebar = sidebar();
assert!(sidebar.select_path("personal", true));
assert!(sidebar.select_path("personal/咖啡", false));
assert_eq!(sidebar.selected().map(NodeId::path), Some("personal/咖啡"));
assert!(
sidebar
.visible_rows()
.iter()
.any(|row| row.id.path() == "personal/咖啡")
);
assert_eq!(
sidebar.completion_paths(),
vec![
"personal",
"personal/email",
"personal/咖啡",
"work",
"work/server"
]
);
assert!(!sidebar.select_path("personal/咖啡", true));
}
#[test]
fn empty_tree_has_no_stale_selection() {
let mut sidebar = Sidebar::default();