mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
refactor(tui/history): consolidate merges + introduce ExecAction enum
- Add ExecAction enum and action_enum_from_parsed() - Switch exec_tools to use enum instead of string tags - Add ChatWidget::history_maybe_merge_tool_with_previous() - Adopt merge helper in web_search_complete - Keep behavior identical; centralize merge pathways Validated with ./build-fast.sh (no warnings).
This commit is contained in:
@@ -1605,6 +1605,44 @@ impl ChatWidget<'_> {
|
||||
crate::chatwidget::exec_tools::try_merge_completed_exec_at(self, idx);
|
||||
}
|
||||
|
||||
// Merge adjacent tool cells with the same header (e.g., successive Web Search blocks)
|
||||
fn history_maybe_merge_tool_with_previous(&mut self, idx: usize) {
|
||||
if idx == 0 || idx >= self.history_cells.len() { return; }
|
||||
let new_lines = self.history_cells[idx].display_lines();
|
||||
let new_header = new_lines
|
||||
.first()
|
||||
.and_then(|l| l.spans.get(0))
|
||||
.map(|s| s.content.clone().to_string())
|
||||
.unwrap_or_default();
|
||||
if new_header.is_empty() { return; }
|
||||
let prev_lines = self.history_cells[idx - 1].display_lines();
|
||||
let prev_header = prev_lines
|
||||
.first()
|
||||
.and_then(|l| l.spans.get(0))
|
||||
.map(|s| s.content.clone().to_string())
|
||||
.unwrap_or_default();
|
||||
if new_header != prev_header { return; }
|
||||
let mut combined = prev_lines.clone();
|
||||
while combined
|
||||
.last()
|
||||
.map(|l| crate::render::line_utils::is_blank_line_trim(l))
|
||||
.unwrap_or(false)
|
||||
{ combined.pop(); }
|
||||
let mut body: Vec<ratatui::text::Line<'static>> = new_lines.into_iter().skip(1).collect();
|
||||
while body.first().map(|l| crate::render::line_utils::is_blank_line_trim(l)).unwrap_or(false) { body.remove(0); }
|
||||
while body.last().map(|l| crate::render::line_utils::is_blank_line_trim(l)).unwrap_or(false) { body.pop(); }
|
||||
if let Some(first_line) = body.first_mut() {
|
||||
if let Some(first_span) = first_line.spans.get_mut(0) {
|
||||
if first_span.content == " └ " || first_span.content == "└ " {
|
||||
first_span.content = " ".into();
|
||||
}
|
||||
}
|
||||
}
|
||||
combined.extend(body);
|
||||
self.history_replace_at(idx - 1, Box::new(crate::history_cell::PlainHistoryCell { lines: combined, kind: crate::history_cell::HistoryCellType::Plain }));
|
||||
self.history_remove_at(idx);
|
||||
}
|
||||
|
||||
/// Clean up faded-out animation cells
|
||||
fn process_animation_cleanup(&mut self) {
|
||||
// With trait-based cells, we can't easily detect and clean up specific cell types
|
||||
|
||||
@@ -301,10 +301,10 @@ pub(super) fn handle_exec_begin_now(chat: &mut ChatWidget<'_>, ev: ExecCommandBe
|
||||
if chat.ended_call_ids.contains(&ev.call_id) { return; }
|
||||
for cell in &chat.history_cells { cell.trigger_fade(); }
|
||||
let parsed_command = ev.parsed_cmd.clone();
|
||||
let action = history_cell::action_from_parsed(&parsed_command);
|
||||
let action = history_cell::action_enum_from_parsed(&parsed_command);
|
||||
chat.height_manager.borrow_mut().record_event(HeightEvent::RunBegin);
|
||||
|
||||
if action == "read" {
|
||||
if matches!(action, history_cell::ExecAction::Read) {
|
||||
chat.exec.running_commands.insert(
|
||||
ev.call_id.clone(),
|
||||
super::RunningCommand { command: ev.command.clone(), parsed: parsed_command.clone(), history_index: None },
|
||||
@@ -379,13 +379,13 @@ pub(super) fn handle_exec_end_now(chat: &mut ChatWidget<'_>, ev: ExecCommandEndE
|
||||
.map(|cmd| (cmd.command, cmd.parsed, cmd.history_index))
|
||||
.unwrap_or_else(|| (vec![call_id.clone()], vec![], None));
|
||||
|
||||
let action = history_cell::action_from_parsed(&parsed);
|
||||
if action == "read" {
|
||||
let action = history_cell::action_enum_from_parsed(&parsed);
|
||||
if matches!(action, history_cell::ExecAction::Read) {
|
||||
let any_read_running = chat
|
||||
.exec
|
||||
.running_commands
|
||||
.values()
|
||||
.any(|rc| history_cell::action_from_parsed(&rc.parsed) == "read");
|
||||
.any(|rc| matches!(history_cell::action_enum_from_parsed(&rc.parsed), history_cell::ExecAction::Read));
|
||||
if !any_read_running {
|
||||
if let Some(idx) = chat.exec.running_read_agg_index.take() {
|
||||
if idx < chat.history_cells.len() {
|
||||
|
||||
@@ -49,32 +49,7 @@ pub(super) fn web_search_complete(chat: &mut ChatWidget<'_>, call_id: String, qu
|
||||
let final_query = query.or(maybe_query);
|
||||
let completed = rt.finalize_web_search(true, final_query);
|
||||
chat.history_replace_at(i, Box::new(completed));
|
||||
|
||||
// Merge adjacent Web Search blocks with same header
|
||||
if i > 0 {
|
||||
let new_lines = chat.history_cells[i].display_lines();
|
||||
let new_header = new_lines.first().and_then(|l| l.spans.get(0)).map(|s| s.content.clone().to_string()).unwrap_or_default();
|
||||
let prev_lines = chat.history_cells[i - 1].display_lines();
|
||||
let prev_header = prev_lines.first().and_then(|l| l.spans.get(0)).map(|s| s.content.clone().to_string()).unwrap_or_default();
|
||||
if !new_header.is_empty() && new_header == prev_header {
|
||||
let mut combined = prev_lines.clone();
|
||||
while combined.last().map(|l| crate::render::line_utils::is_blank_line_trim(l)).unwrap_or(false) { combined.pop(); }
|
||||
let mut body: Vec<ratatui::text::Line<'static>> = new_lines.into_iter().skip(1).collect();
|
||||
while body.first().map(|l| crate::render::line_utils::is_blank_line_trim(l)).unwrap_or(false) { body.remove(0); }
|
||||
while body.last().map(|l| crate::render::line_utils::is_blank_line_trim(l)).unwrap_or(false) { body.pop(); }
|
||||
if let Some(first_line) = body.first_mut() {
|
||||
if let Some(first_span) = first_line.spans.get_mut(0) {
|
||||
if first_span.content == " └ " || first_span.content == "└ " {
|
||||
first_span.content = " ".into();
|
||||
}
|
||||
}
|
||||
}
|
||||
combined.extend(body);
|
||||
chat.history_replace_at(i - 1, Box::new(history_cell::PlainHistoryCell { lines: combined, kind: history_cell::HistoryCellType::Plain }));
|
||||
chat.history_remove_at(i);
|
||||
}
|
||||
}
|
||||
// history_replace_at already invalidates and redraws
|
||||
chat.history_maybe_merge_tool_with_previous(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,28 @@ pub(crate) enum ExecKind {
|
||||
Run,
|
||||
}
|
||||
|
||||
// Unified action classification for exec commands
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ExecAction {
|
||||
Read,
|
||||
Search,
|
||||
List,
|
||||
Run,
|
||||
}
|
||||
|
||||
pub(crate) fn action_enum_from_parsed(parsed: &Vec<codex_core::parse_command::ParsedCommand>) -> ExecAction {
|
||||
use codex_core::parse_command::ParsedCommand;
|
||||
for p in parsed {
|
||||
match p {
|
||||
ParsedCommand::Read { .. } => return ExecAction::Read,
|
||||
ParsedCommand::Search { .. } => return ExecAction::Search,
|
||||
ParsedCommand::ListFiles { .. } => return ExecAction::List,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ExecAction::Run
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ExecStatus {
|
||||
Running,
|
||||
|
||||
Reference in New Issue
Block a user