Improve thread message history (#34299)

- Keep history across threads
- Reset position when edited

Release Notes:

- N/A

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
This commit is contained in:
Agus Zubiaga 2025-07-11 13:24:41 -03:00 committed by GitHub
parent d65855c4a1
commit d0e01dbd8f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 79 additions and 44 deletions

View file

@ -2,4 +2,5 @@ mod completion_provider;
mod message_history;
mod thread_view;
pub use message_history::MessageHistory;
pub use thread_view::AcpThreadView;

View file

@ -3,19 +3,25 @@ pub struct MessageHistory<T> {
current: Option<usize>,
}
impl<T> MessageHistory<T> {
pub fn new() -> Self {
impl<T> Default for MessageHistory<T> {
fn default() -> Self {
MessageHistory {
items: Vec::new(),
current: None,
}
}
}
impl<T> MessageHistory<T> {
pub fn push(&mut self, message: T) {
self.current.take();
self.items.push(message);
}
pub fn reset_position(&mut self) {
self.current.take();
}
pub fn prev(&mut self) -> Option<&T> {
if self.items.is_empty() {
return None;
@ -46,7 +52,7 @@ mod tests {
#[test]
fn test_prev_next() {
let mut history = MessageHistory::new();
let mut history = MessageHistory::default();
// Test empty history
assert_eq!(history.prev(), None);

View file

@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::path::Path;
use std::rc::Rc;
@ -53,6 +54,8 @@ pub struct AcpThreadView {
thread_state: ThreadState,
diff_editors: HashMap<EntityId, Entity<Editor>>,
message_editor: Entity<Editor>,
message_set_from_history: bool,
_message_editor_subscription: Subscription,
mention_set: Arc<Mutex<MentionSet>>,
last_error: Option<Entity<Markdown>>,
list_state: ListState,
@ -60,7 +63,7 @@ pub struct AcpThreadView {
expanded_tool_calls: HashSet<ToolCallId>,
expanded_thinking_blocks: HashSet<(usize, usize)>,
edits_expanded: bool,
message_history: MessageHistory<acp::SendUserMessageParams>,
message_history: Rc<RefCell<MessageHistory<acp::SendUserMessageParams>>>,
}
enum ThreadState {
@ -81,6 +84,7 @@ impl AcpThreadView {
pub fn new(
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
message_history: Rc<RefCell<MessageHistory<acp::SendUserMessageParams>>>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@ -125,6 +129,17 @@ impl AcpThreadView {
editor
});
let message_editor_subscription = cx.subscribe(&message_editor, |this, _, event, _| {
if let editor::EditorEvent::BufferEdited = &event {
if !this.message_set_from_history {
this.message_history.borrow_mut().reset_position();
}
this.message_set_from_history = false;
}
});
let mention_set = mention_set.clone();
let list_state = ListState::new(
0,
gpui::ListAlignment::Bottom,
@ -147,6 +162,8 @@ impl AcpThreadView {
project: project.clone(),
thread_state: Self::initial_state(workspace, project, window, cx),
message_editor,
message_set_from_history: false,
_message_editor_subscription: message_editor_subscription,
mention_set,
diff_editors: Default::default(),
list_state: list_state,
@ -155,7 +172,7 @@ impl AcpThreadView {
expanded_tool_calls: HashSet::default(),
expanded_thinking_blocks: HashSet::default(),
edits_expanded: false,
message_history: MessageHistory::new(),
message_history,
}
}
@ -358,7 +375,7 @@ impl AcpThreadView {
editor.remove_creases(mention_set.lock().drain(), cx)
});
self.message_history.push(message);
self.message_history.borrow_mut().push(message);
}
fn previous_history_message(
@ -367,11 +384,11 @@ impl AcpThreadView {
window: &mut Window,
cx: &mut Context<Self>,
) {
Self::set_draft_message(
self.message_set_from_history = Self::set_draft_message(
self.message_editor.clone(),
self.mention_set.clone(),
self.project.clone(),
self.message_history.prev(),
self.message_history.borrow_mut().prev(),
window,
cx,
);
@ -383,43 +400,16 @@ impl AcpThreadView {
window: &mut Window,
cx: &mut Context<Self>,
) {
Self::set_draft_message(
self.message_set_from_history = Self::set_draft_message(
self.message_editor.clone(),
self.mention_set.clone(),
self.project.clone(),
self.message_history.next(),
self.message_history.borrow_mut().next(),
window,
cx,
);
}
fn open_agent_diff(&mut self, _: &OpenAgentDiff, window: &mut Window, cx: &mut Context<Self>) {
if let Some(thread) = self.thread() {
AgentDiffPane::deploy(thread.clone(), self.workspace.clone(), window, cx).log_err();
}
}
fn open_edited_buffer(
&mut self,
buffer: &Entity<Buffer>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(thread) = self.thread() else {
return;
};
let Some(diff) =
AgentDiffPane::deploy(thread.clone(), self.workspace.clone(), window, cx).log_err()
else {
return;
};
diff.update(cx, |diff, cx| {
diff.move_to_path(PathKey::for_buffer(&buffer, cx), window, cx)
})
}
fn set_draft_message(
message_editor: Entity<Editor>,
mention_set: Arc<Mutex<MentionSet>>,
@ -427,15 +417,11 @@ impl AcpThreadView {
message: Option<&acp::SendUserMessageParams>,
window: &mut Window,
cx: &mut Context<Self>,
) {
) -> bool {
cx.notify();
let Some(message) = message else {
message_editor.update(cx, |editor, cx| {
editor.clear(window, cx);
editor.remove_creases(mention_set.lock().drain(), cx)
});
return;
return false;
};
let mut text = String::new();
@ -495,6 +481,35 @@ impl AcpThreadView {
mention_set.lock().insert(crease_id, project_path);
}
}
true
}
fn open_agent_diff(&mut self, _: &OpenAgentDiff, window: &mut Window, cx: &mut Context<Self>) {
if let Some(thread) = self.thread() {
AgentDiffPane::deploy(thread.clone(), self.workspace.clone(), window, cx).log_err();
}
}
fn open_edited_buffer(
&mut self,
buffer: &Entity<Buffer>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(thread) = self.thread() else {
return;
};
let Some(diff) =
AgentDiffPane::deploy(thread.clone(), self.workspace.clone(), window, cx).log_err()
else {
return;
};
diff.update(cx, |diff, cx| {
diff.move_to_path(PathKey::for_buffer(&buffer, cx), window, cx)
})
}
fn handle_thread_event(

View file

@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::ops::Range;
use std::path::Path;
use std::rc::Rc;
@ -433,6 +434,8 @@ pub struct AgentPanel {
configuration_subscription: Option<Subscription>,
local_timezone: UtcOffset,
active_view: ActiveView,
acp_message_history:
Rc<RefCell<crate::acp::MessageHistory<agentic_coding_protocol::SendUserMessageParams>>>,
previous_view: Option<ActiveView>,
history_store: Entity<HistoryStore>,
history: Entity<ThreadHistory>,
@ -699,6 +702,7 @@ impl AgentPanel {
.unwrap(),
inline_assist_context_store,
previous_view: None,
acp_message_history: Default::default(),
history_store: history_store.clone(),
history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, window, cx)),
hovered_recent_history_item: None,
@ -888,10 +892,17 @@ impl AgentPanel {
fn new_gemini_thread(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let workspace = self.workspace.clone();
let project = self.project.clone();
let message_history = self.acp_message_history.clone();
cx.spawn_in(window, async move |this, cx| {
let thread_view = cx.new_window_entity(|window, cx| {
crate::acp::AcpThreadView::new(workspace.clone(), project, window, cx)
crate::acp::AcpThreadView::new(
workspace.clone(),
project,
message_history,
window,
cx,
)
})?;
this.update_in(cx, |this, window, cx| {
this.set_active_view(
@ -1432,6 +1443,8 @@ impl AgentPanel {
self.active_view = new_view;
}
self.acp_message_history.borrow_mut().reset_position();
self.focus_handle(cx).focus(window);
}