mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Retain search history for inline assistants
This only works in-memory for now.
This commit is contained in:
parent
5c498c8610
commit
c6f4390511
@ -7,13 +7,13 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use chrono::{DateTime, Local};
|
use chrono::{DateTime, Local};
|
||||||
use collections::{hash_map, HashMap, HashSet};
|
use collections::{hash_map, HashMap, HashSet, VecDeque};
|
||||||
use editor::{
|
use editor::{
|
||||||
display_map::{
|
display_map::{
|
||||||
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, ToDisplayPoint,
|
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, ToDisplayPoint,
|
||||||
},
|
},
|
||||||
scroll::autoscroll::{Autoscroll, AutoscrollStrategy},
|
scroll::autoscroll::{Autoscroll, AutoscrollStrategy},
|
||||||
Anchor, Editor, MultiBufferSnapshot, ToOffset, ToPoint,
|
Anchor, Editor, MoveDown, MoveUp, MultiBufferSnapshot, ToOffset, ToPoint,
|
||||||
};
|
};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::{channel::mpsc, SinkExt, Stream, StreamExt};
|
use futures::{channel::mpsc, SinkExt, Stream, StreamExt};
|
||||||
@ -106,6 +106,8 @@ pub fn init(cx: &mut AppContext) {
|
|||||||
cx.add_action(InlineAssistant::confirm);
|
cx.add_action(InlineAssistant::confirm);
|
||||||
cx.add_action(InlineAssistant::cancel);
|
cx.add_action(InlineAssistant::cancel);
|
||||||
cx.add_action(InlineAssistant::toggle_include_conversation);
|
cx.add_action(InlineAssistant::toggle_include_conversation);
|
||||||
|
cx.add_action(InlineAssistant::move_up);
|
||||||
|
cx.add_action(InlineAssistant::move_down);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -139,10 +141,13 @@ pub struct AssistantPanel {
|
|||||||
pending_inline_assists: HashMap<usize, PendingInlineAssist>,
|
pending_inline_assists: HashMap<usize, PendingInlineAssist>,
|
||||||
pending_inline_assist_ids_by_editor: HashMap<WeakViewHandle<Editor>, Vec<usize>>,
|
pending_inline_assist_ids_by_editor: HashMap<WeakViewHandle<Editor>, Vec<usize>>,
|
||||||
include_conversation_in_next_inline_assist: bool,
|
include_conversation_in_next_inline_assist: bool,
|
||||||
|
inline_prompt_history: VecDeque<String>,
|
||||||
_watch_saved_conversations: Task<Result<()>>,
|
_watch_saved_conversations: Task<Result<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssistantPanel {
|
impl AssistantPanel {
|
||||||
|
const INLINE_PROMPT_HISTORY_MAX_LEN: usize = 20;
|
||||||
|
|
||||||
pub fn load(
|
pub fn load(
|
||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
cx: AsyncAppContext,
|
cx: AsyncAppContext,
|
||||||
@ -206,6 +211,7 @@ impl AssistantPanel {
|
|||||||
pending_inline_assists: Default::default(),
|
pending_inline_assists: Default::default(),
|
||||||
pending_inline_assist_ids_by_editor: Default::default(),
|
pending_inline_assist_ids_by_editor: Default::default(),
|
||||||
include_conversation_in_next_inline_assist: false,
|
include_conversation_in_next_inline_assist: false,
|
||||||
|
inline_prompt_history: Default::default(),
|
||||||
_watch_saved_conversations,
|
_watch_saved_conversations,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -269,29 +275,16 @@ impl AssistantPanel {
|
|||||||
} else {
|
} else {
|
||||||
InlineAssistKind::Transform
|
InlineAssistKind::Transform
|
||||||
};
|
};
|
||||||
let prompt_editor = cx.add_view(|cx| {
|
|
||||||
let mut editor = Editor::single_line(
|
|
||||||
Some(Arc::new(|theme| theme.assistant.inline.editor.clone())),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
let placeholder = match assist_kind {
|
|
||||||
InlineAssistKind::Transform => "Enter transformation prompt…",
|
|
||||||
InlineAssistKind::Generate => "Enter generation prompt…",
|
|
||||||
};
|
|
||||||
editor.set_placeholder_text(placeholder, cx);
|
|
||||||
editor
|
|
||||||
});
|
|
||||||
let measurements = Rc::new(Cell::new(BlockMeasurements::default()));
|
let measurements = Rc::new(Cell::new(BlockMeasurements::default()));
|
||||||
let inline_assistant = cx.add_view(|cx| {
|
let inline_assistant = cx.add_view(|cx| {
|
||||||
let assistant = InlineAssistant {
|
let assistant = InlineAssistant::new(
|
||||||
id: inline_assist_id,
|
inline_assist_id,
|
||||||
prompt_editor,
|
assist_kind,
|
||||||
confirmed: false,
|
measurements.clone(),
|
||||||
has_focus: false,
|
self.include_conversation_in_next_inline_assist,
|
||||||
include_conversation: self.include_conversation_in_next_inline_assist,
|
self.inline_prompt_history.clone(),
|
||||||
measurements: measurements.clone(),
|
cx,
|
||||||
error: None,
|
);
|
||||||
};
|
|
||||||
cx.focus_self();
|
cx.focus_self();
|
||||||
assistant
|
assistant
|
||||||
});
|
});
|
||||||
@ -520,6 +513,10 @@ impl AssistantPanel {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.inline_prompt_history.push_back(user_prompt.into());
|
||||||
|
if self.inline_prompt_history.len() > Self::INLINE_PROMPT_HISTORY_MAX_LEN {
|
||||||
|
self.inline_prompt_history.pop_front();
|
||||||
|
}
|
||||||
let range = pending_assist.range.clone();
|
let range = pending_assist.range.clone();
|
||||||
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
|
let snapshot = editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||||
let selected_text = snapshot
|
let selected_text = snapshot
|
||||||
@ -2895,6 +2892,10 @@ struct InlineAssistant {
|
|||||||
include_conversation: bool,
|
include_conversation: bool,
|
||||||
measurements: Rc<Cell<BlockMeasurements>>,
|
measurements: Rc<Cell<BlockMeasurements>>,
|
||||||
error: Option<anyhow::Error>,
|
error: Option<anyhow::Error>,
|
||||||
|
prompt_history: VecDeque<String>,
|
||||||
|
prompt_history_ix: Option<usize>,
|
||||||
|
pending_prompt: String,
|
||||||
|
_subscription: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for InlineAssistant {
|
impl Entity for InlineAssistant {
|
||||||
@ -2995,6 +2996,54 @@ impl View for InlineAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InlineAssistant {
|
impl InlineAssistant {
|
||||||
|
fn new(
|
||||||
|
id: usize,
|
||||||
|
kind: InlineAssistKind,
|
||||||
|
measurements: Rc<Cell<BlockMeasurements>>,
|
||||||
|
include_conversation: bool,
|
||||||
|
prompt_history: VecDeque<String>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Self {
|
||||||
|
let prompt_editor = cx.add_view(|cx| {
|
||||||
|
let mut editor = Editor::single_line(
|
||||||
|
Some(Arc::new(|theme| theme.assistant.inline.editor.clone())),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
let placeholder = match kind {
|
||||||
|
InlineAssistKind::Transform => "Enter transformation prompt…",
|
||||||
|
InlineAssistKind::Generate => "Enter generation prompt…",
|
||||||
|
};
|
||||||
|
editor.set_placeholder_text(placeholder, cx);
|
||||||
|
editor
|
||||||
|
});
|
||||||
|
let subscription = cx.subscribe(&prompt_editor, Self::handle_prompt_editor_events);
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
prompt_editor,
|
||||||
|
confirmed: false,
|
||||||
|
has_focus: false,
|
||||||
|
include_conversation,
|
||||||
|
measurements,
|
||||||
|
error: None,
|
||||||
|
prompt_history,
|
||||||
|
prompt_history_ix: None,
|
||||||
|
pending_prompt: String::new(),
|
||||||
|
_subscription: subscription,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_prompt_editor_events(
|
||||||
|
&mut self,
|
||||||
|
_: ViewHandle<Editor>,
|
||||||
|
event: &editor::Event,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
if let editor::Event::Edited = event {
|
||||||
|
self.pending_prompt = self.prompt_editor.read(cx).text(cx);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn cancel(&mut self, _: &editor::Cancel, cx: &mut ViewContext<Self>) {
|
fn cancel(&mut self, _: &editor::Cancel, cx: &mut ViewContext<Self>) {
|
||||||
cx.emit(InlineAssistantEvent::Canceled);
|
cx.emit(InlineAssistantEvent::Canceled);
|
||||||
}
|
}
|
||||||
@ -3047,6 +3096,43 @@ impl InlineAssistant {
|
|||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(ix) = self.prompt_history_ix {
|
||||||
|
if ix > 0 {
|
||||||
|
self.prompt_history_ix = Some(ix - 1);
|
||||||
|
let prompt = self.prompt_history[ix - 1].clone();
|
||||||
|
self.set_prompt(&prompt, cx);
|
||||||
|
}
|
||||||
|
} else if !self.prompt_history.is_empty() {
|
||||||
|
self.prompt_history_ix = Some(self.prompt_history.len() - 1);
|
||||||
|
let prompt = self.prompt_history[self.prompt_history.len() - 1].clone();
|
||||||
|
self.set_prompt(&prompt, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(ix) = self.prompt_history_ix {
|
||||||
|
if ix < self.prompt_history.len() - 1 {
|
||||||
|
self.prompt_history_ix = Some(ix + 1);
|
||||||
|
let prompt = self.prompt_history[ix + 1].clone();
|
||||||
|
self.set_prompt(&prompt, cx);
|
||||||
|
} else {
|
||||||
|
self.prompt_history_ix = None;
|
||||||
|
let pending_prompt = self.pending_prompt.clone();
|
||||||
|
self.set_prompt(&pending_prompt, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_prompt(&mut self, prompt: &str, cx: &mut ViewContext<Self>) {
|
||||||
|
self.prompt_editor.update(cx, |editor, cx| {
|
||||||
|
editor.buffer().update(cx, |buffer, cx| {
|
||||||
|
let len = buffer.len(cx);
|
||||||
|
buffer.edit([(0..len, prompt)], None, cx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This wouldn't need to exist if we could pass parameters when rendering child views.
|
// This wouldn't need to exist if we could pass parameters when rendering child views.
|
||||||
|
Loading…
Reference in New Issue
Block a user