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:
parent
d65855c4a1
commit
d0e01dbd8f
4 changed files with 79 additions and 44 deletions
|
@ -2,4 +2,5 @@ mod completion_provider;
|
||||||
mod message_history;
|
mod message_history;
|
||||||
mod thread_view;
|
mod thread_view;
|
||||||
|
|
||||||
|
pub use message_history::MessageHistory;
|
||||||
pub use thread_view::AcpThreadView;
|
pub use thread_view::AcpThreadView;
|
||||||
|
|
|
@ -3,19 +3,25 @@ pub struct MessageHistory<T> {
|
||||||
current: Option<usize>,
|
current: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MessageHistory<T> {
|
impl<T> Default for MessageHistory<T> {
|
||||||
pub fn new() -> Self {
|
fn default() -> Self {
|
||||||
MessageHistory {
|
MessageHistory {
|
||||||
items: Vec::new(),
|
items: Vec::new(),
|
||||||
current: None,
|
current: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MessageHistory<T> {
|
||||||
pub fn push(&mut self, message: T) {
|
pub fn push(&mut self, message: T) {
|
||||||
self.current.take();
|
self.current.take();
|
||||||
self.items.push(message);
|
self.items.push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_position(&mut self) {
|
||||||
|
self.current.take();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn prev(&mut self) -> Option<&T> {
|
pub fn prev(&mut self) -> Option<&T> {
|
||||||
if self.items.is_empty() {
|
if self.items.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -46,7 +52,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_prev_next() {
|
fn test_prev_next() {
|
||||||
let mut history = MessageHistory::new();
|
let mut history = MessageHistory::default();
|
||||||
|
|
||||||
// Test empty history
|
// Test empty history
|
||||||
assert_eq!(history.prev(), None);
|
assert_eq!(history.prev(), None);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -53,6 +54,8 @@ pub struct AcpThreadView {
|
||||||
thread_state: ThreadState,
|
thread_state: ThreadState,
|
||||||
diff_editors: HashMap<EntityId, Entity<Editor>>,
|
diff_editors: HashMap<EntityId, Entity<Editor>>,
|
||||||
message_editor: Entity<Editor>,
|
message_editor: Entity<Editor>,
|
||||||
|
message_set_from_history: bool,
|
||||||
|
_message_editor_subscription: Subscription,
|
||||||
mention_set: Arc<Mutex<MentionSet>>,
|
mention_set: Arc<Mutex<MentionSet>>,
|
||||||
last_error: Option<Entity<Markdown>>,
|
last_error: Option<Entity<Markdown>>,
|
||||||
list_state: ListState,
|
list_state: ListState,
|
||||||
|
@ -60,7 +63,7 @@ pub struct AcpThreadView {
|
||||||
expanded_tool_calls: HashSet<ToolCallId>,
|
expanded_tool_calls: HashSet<ToolCallId>,
|
||||||
expanded_thinking_blocks: HashSet<(usize, usize)>,
|
expanded_thinking_blocks: HashSet<(usize, usize)>,
|
||||||
edits_expanded: bool,
|
edits_expanded: bool,
|
||||||
message_history: MessageHistory<acp::SendUserMessageParams>,
|
message_history: Rc<RefCell<MessageHistory<acp::SendUserMessageParams>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ThreadState {
|
enum ThreadState {
|
||||||
|
@ -81,6 +84,7 @@ impl AcpThreadView {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
|
message_history: Rc<RefCell<MessageHistory<acp::SendUserMessageParams>>>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -125,6 +129,17 @@ impl AcpThreadView {
|
||||||
editor
|
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(
|
let list_state = ListState::new(
|
||||||
0,
|
0,
|
||||||
gpui::ListAlignment::Bottom,
|
gpui::ListAlignment::Bottom,
|
||||||
|
@ -147,6 +162,8 @@ impl AcpThreadView {
|
||||||
project: project.clone(),
|
project: project.clone(),
|
||||||
thread_state: Self::initial_state(workspace, project, window, cx),
|
thread_state: Self::initial_state(workspace, project, window, cx),
|
||||||
message_editor,
|
message_editor,
|
||||||
|
message_set_from_history: false,
|
||||||
|
_message_editor_subscription: message_editor_subscription,
|
||||||
mention_set,
|
mention_set,
|
||||||
diff_editors: Default::default(),
|
diff_editors: Default::default(),
|
||||||
list_state: list_state,
|
list_state: list_state,
|
||||||
|
@ -155,7 +172,7 @@ impl AcpThreadView {
|
||||||
expanded_tool_calls: HashSet::default(),
|
expanded_tool_calls: HashSet::default(),
|
||||||
expanded_thinking_blocks: HashSet::default(),
|
expanded_thinking_blocks: HashSet::default(),
|
||||||
edits_expanded: false,
|
edits_expanded: false,
|
||||||
message_history: MessageHistory::new(),
|
message_history,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +375,7 @@ impl AcpThreadView {
|
||||||
editor.remove_creases(mention_set.lock().drain(), cx)
|
editor.remove_creases(mention_set.lock().drain(), cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
self.message_history.push(message);
|
self.message_history.borrow_mut().push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn previous_history_message(
|
fn previous_history_message(
|
||||||
|
@ -367,11 +384,11 @@ impl AcpThreadView {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
Self::set_draft_message(
|
self.message_set_from_history = Self::set_draft_message(
|
||||||
self.message_editor.clone(),
|
self.message_editor.clone(),
|
||||||
self.mention_set.clone(),
|
self.mention_set.clone(),
|
||||||
self.project.clone(),
|
self.project.clone(),
|
||||||
self.message_history.prev(),
|
self.message_history.borrow_mut().prev(),
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -383,43 +400,16 @@ impl AcpThreadView {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
Self::set_draft_message(
|
self.message_set_from_history = Self::set_draft_message(
|
||||||
self.message_editor.clone(),
|
self.message_editor.clone(),
|
||||||
self.mention_set.clone(),
|
self.mention_set.clone(),
|
||||||
self.project.clone(),
|
self.project.clone(),
|
||||||
self.message_history.next(),
|
self.message_history.borrow_mut().next(),
|
||||||
window,
|
window,
|
||||||
cx,
|
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(
|
fn set_draft_message(
|
||||||
message_editor: Entity<Editor>,
|
message_editor: Entity<Editor>,
|
||||||
mention_set: Arc<Mutex<MentionSet>>,
|
mention_set: Arc<Mutex<MentionSet>>,
|
||||||
|
@ -427,15 +417,11 @@ impl AcpThreadView {
|
||||||
message: Option<&acp::SendUserMessageParams>,
|
message: Option<&acp::SendUserMessageParams>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) -> bool {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
let Some(message) = message else {
|
let Some(message) = message else {
|
||||||
message_editor.update(cx, |editor, cx| {
|
return false;
|
||||||
editor.clear(window, cx);
|
|
||||||
editor.remove_creases(mention_set.lock().drain(), cx)
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
|
@ -495,6 +481,35 @@ impl AcpThreadView {
|
||||||
mention_set.lock().insert(crease_id, project_path);
|
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(
|
fn handle_thread_event(
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -433,6 +434,8 @@ pub struct AgentPanel {
|
||||||
configuration_subscription: Option<Subscription>,
|
configuration_subscription: Option<Subscription>,
|
||||||
local_timezone: UtcOffset,
|
local_timezone: UtcOffset,
|
||||||
active_view: ActiveView,
|
active_view: ActiveView,
|
||||||
|
acp_message_history:
|
||||||
|
Rc<RefCell<crate::acp::MessageHistory<agentic_coding_protocol::SendUserMessageParams>>>,
|
||||||
previous_view: Option<ActiveView>,
|
previous_view: Option<ActiveView>,
|
||||||
history_store: Entity<HistoryStore>,
|
history_store: Entity<HistoryStore>,
|
||||||
history: Entity<ThreadHistory>,
|
history: Entity<ThreadHistory>,
|
||||||
|
@ -699,6 +702,7 @@ impl AgentPanel {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
inline_assist_context_store,
|
inline_assist_context_store,
|
||||||
previous_view: None,
|
previous_view: None,
|
||||||
|
acp_message_history: Default::default(),
|
||||||
history_store: history_store.clone(),
|
history_store: history_store.clone(),
|
||||||
history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, window, cx)),
|
history: cx.new(|cx| ThreadHistory::new(weak_self, history_store, window, cx)),
|
||||||
hovered_recent_history_item: None,
|
hovered_recent_history_item: None,
|
||||||
|
@ -888,10 +892,17 @@ impl AgentPanel {
|
||||||
fn new_gemini_thread(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn new_gemini_thread(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let workspace = self.workspace.clone();
|
let workspace = self.workspace.clone();
|
||||||
let project = self.project.clone();
|
let project = self.project.clone();
|
||||||
|
let message_history = self.acp_message_history.clone();
|
||||||
|
|
||||||
cx.spawn_in(window, async move |this, cx| {
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
let thread_view = cx.new_window_entity(|window, 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.update_in(cx, |this, window, cx| {
|
||||||
this.set_active_view(
|
this.set_active_view(
|
||||||
|
@ -1432,6 +1443,8 @@ impl AgentPanel {
|
||||||
self.active_view = new_view;
|
self.active_view = new_view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.acp_message_history.borrow_mut().reset_position();
|
||||||
|
|
||||||
self.focus_handle(cx).focus(window);
|
self.focus_handle(cx).focus(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue