mirror of
https://github.com/zed-industries/zed.git
synced 2024-10-23 06:56:33 +00:00
Introduce recent files ambient context for assistant (#11791)
<img width="1637" alt="image" src="https://github.com/zed-industries/zed/assets/482957/5aaec657-3499-42c9-9528-c83728f2a7a1"> Release Notes: - Added a new ambient context feature that allows showing the model up to three buffers (along with their diagnostics) that the user interacted with recently. --------- Co-authored-by: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
e4c95b25bf
commit
a13a92fbbf
9 changed files with 522 additions and 411 deletions
1
assets/icons/countdown_timer.svg
Normal file
1
assets/icons/countdown_timer.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.15 7.49998C13.15 4.66458 10.9402 1.84998 7.50002 1.84998C4.7217 1.84998 3.34851 3.90636 2.76336 4.99997H4.5C4.77614 4.99997 5 5.22383 5 5.49997C5 5.77611 4.77614 5.99997 4.5 5.99997H1.5C1.22386 5.99997 1 5.77611 1 5.49997V2.49997C1 2.22383 1.22386 1.99997 1.5 1.99997C1.77614 1.99997 2 2.22383 2 2.49997V4.31318C2.70453 3.07126 4.33406 0.849976 7.50002 0.849976C11.5628 0.849976 14.15 4.18537 14.15 7.49998C14.15 10.8146 11.5628 14.15 7.50002 14.15C5.55618 14.15 3.93778 13.3808 2.78548 12.2084C2.16852 11.5806 1.68668 10.839 1.35816 10.0407C1.25306 9.78536 1.37488 9.49315 1.63024 9.38806C1.8856 9.28296 2.17781 9.40478 2.2829 9.66014C2.56374 10.3425 2.97495 10.9745 3.4987 11.5074C4.47052 12.4963 5.83496 13.15 7.50002 13.15C10.9402 13.15 13.15 10.3354 13.15 7.49998ZM7 10V5.00001H8V10H7Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
After Width: | Height: | Size: 974 B |
|
@ -6,11 +6,8 @@ mod prompts;
|
|||
mod saved_conversation;
|
||||
mod streaming_diff;
|
||||
|
||||
mod embedded_scope;
|
||||
|
||||
pub use assistant_panel::AssistantPanel;
|
||||
use assistant_settings::{AssistantSettings, OpenAiModel, ZedDotDevModel};
|
||||
use chrono::{DateTime, Local};
|
||||
use client::{proto, Client};
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
pub(crate) use completion_provider::*;
|
||||
|
@ -26,7 +23,6 @@ use std::{
|
|||
actions!(
|
||||
assistant,
|
||||
[
|
||||
NewConversation,
|
||||
Assist,
|
||||
Split,
|
||||
CycleMessageRole,
|
||||
|
@ -35,6 +31,7 @@ actions!(
|
|||
ResetKey,
|
||||
InlineAssist,
|
||||
ToggleIncludeConversation,
|
||||
ToggleHistory,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -93,8 +90,8 @@ impl LanguageModel {
|
|||
|
||||
pub fn display_name(&self) -> String {
|
||||
match self {
|
||||
LanguageModel::OpenAi(model) => format!("openai/{}", model.display_name()),
|
||||
LanguageModel::ZedDotDev(model) => format!("zed.dev/{}", model.display_name()),
|
||||
LanguageModel::OpenAi(model) => model.display_name().into(),
|
||||
LanguageModel::ZedDotDev(model) => model.display_name().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +175,6 @@ pub struct LanguageModelChoiceDelta {
|
|||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
struct MessageMetadata {
|
||||
role: Role,
|
||||
sent_at: DateTime<Local>,
|
||||
status: MessageStatus,
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,91 +0,0 @@
|
|||
use editor::MultiBuffer;
|
||||
use gpui::{AppContext, Model, ModelContext, Subscription};
|
||||
|
||||
use crate::{assistant_panel::Conversation, LanguageModelRequestMessage, Role};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EmbeddedScope {
|
||||
active_buffer: Option<Model<MultiBuffer>>,
|
||||
active_buffer_enabled: bool,
|
||||
active_buffer_subscription: Option<Subscription>,
|
||||
}
|
||||
|
||||
impl EmbeddedScope {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
active_buffer: None,
|
||||
active_buffer_enabled: true,
|
||||
active_buffer_subscription: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_active_buffer(
|
||||
&mut self,
|
||||
buffer: Option<Model<MultiBuffer>>,
|
||||
cx: &mut ModelContext<Conversation>,
|
||||
) {
|
||||
self.active_buffer_subscription.take();
|
||||
|
||||
if let Some(active_buffer) = buffer.clone() {
|
||||
self.active_buffer_subscription =
|
||||
Some(cx.subscribe(&active_buffer, |conversation, _, e, cx| {
|
||||
if let multi_buffer::Event::Edited { .. } = e {
|
||||
conversation.count_remaining_tokens(cx)
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
self.active_buffer = buffer;
|
||||
}
|
||||
|
||||
pub fn active_buffer(&self) -> Option<&Model<MultiBuffer>> {
|
||||
self.active_buffer.as_ref()
|
||||
}
|
||||
|
||||
pub fn active_buffer_enabled(&self) -> bool {
|
||||
self.active_buffer_enabled
|
||||
}
|
||||
|
||||
pub fn set_active_buffer_enabled(&mut self, enabled: bool) {
|
||||
self.active_buffer_enabled = enabled;
|
||||
}
|
||||
|
||||
/// Provide a message for the language model based on the active buffer.
|
||||
pub fn message(&self, cx: &AppContext) -> Option<LanguageModelRequestMessage> {
|
||||
if !self.active_buffer_enabled {
|
||||
return None;
|
||||
}
|
||||
|
||||
let active_buffer = self.active_buffer.as_ref()?;
|
||||
let buffer = active_buffer.read(cx);
|
||||
|
||||
if let Some(singleton) = buffer.as_singleton() {
|
||||
let singleton = singleton.read(cx);
|
||||
|
||||
let filename = singleton
|
||||
.file()
|
||||
.map(|file| file.path().to_string_lossy())
|
||||
.unwrap_or("Untitled".into());
|
||||
|
||||
let text = singleton.text();
|
||||
|
||||
let language = singleton
|
||||
.language()
|
||||
.map(|l| {
|
||||
let name = l.code_fence_block_name();
|
||||
name.to_string()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let markdown =
|
||||
format!("User's active file `{filename}`:\n\n```{language}\n{text}```\n\n");
|
||||
|
||||
return Some(LanguageModelRequestMessage {
|
||||
role: Role::System,
|
||||
content: markdown,
|
||||
});
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
|
@ -200,6 +200,18 @@ impl FocusHandle {
|
|||
pub fn contains(&self, other: &Self, cx: &WindowContext) -> bool {
|
||||
self.id.contains(other.id, cx)
|
||||
}
|
||||
|
||||
/// Dispatch an action on the element that rendered this focus handle
|
||||
pub fn dispatch_action(&self, action: &dyn Action, cx: &mut WindowContext) {
|
||||
if let Some(node_id) = cx
|
||||
.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.focusable_node_id(self.id)
|
||||
{
|
||||
cx.dispatch_action_on_node(node_id, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FocusHandle {
|
||||
|
|
|
@ -189,8 +189,8 @@ impl TabSwitcherDelegate {
|
|||
let pane = pane.read(cx);
|
||||
let mut history_indices = HashMap::default();
|
||||
pane.activation_history().iter().rev().enumerate().for_each(
|
||||
|(history_index, entity_id)| {
|
||||
history_indices.insert(entity_id, history_index);
|
||||
|(history_index, history_entry)| {
|
||||
history_indices.insert(history_entry.entity_id, history_index);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ pub enum IconName {
|
|||
CopilotError,
|
||||
CopilotInit,
|
||||
Copy,
|
||||
CountdownTimer,
|
||||
Dash,
|
||||
Delete,
|
||||
Disconnected,
|
||||
|
@ -221,6 +222,7 @@ impl IconName {
|
|||
IconName::CopilotError => "icons/copilot_error.svg",
|
||||
IconName::CopilotInit => "icons/copilot_init.svg",
|
||||
IconName::Copy => "icons/copy.svg",
|
||||
IconName::CountdownTimer => "icons/countdown_timer.svg",
|
||||
IconName::Dash => "icons/dash.svg",
|
||||
IconName::Delete => "icons/delete.svg",
|
||||
IconName::Disconnected => "icons/disconnected.svg",
|
||||
|
|
|
@ -191,7 +191,8 @@ pub struct Pane {
|
|||
),
|
||||
focus_handle: FocusHandle,
|
||||
items: Vec<Box<dyn ItemHandle>>,
|
||||
activation_history: Vec<EntityId>,
|
||||
activation_history: Vec<ActivationHistoryEntry>,
|
||||
next_activation_timestamp: Arc<AtomicUsize>,
|
||||
zoomed: bool,
|
||||
was_focused: bool,
|
||||
active_item_index: usize,
|
||||
|
@ -219,6 +220,11 @@ pub struct Pane {
|
|||
double_click_dispatch_action: Box<dyn Action>,
|
||||
}
|
||||
|
||||
pub struct ActivationHistoryEntry {
|
||||
pub entity_id: EntityId,
|
||||
pub timestamp: usize,
|
||||
}
|
||||
|
||||
pub struct ItemNavHistory {
|
||||
history: NavHistory,
|
||||
item: Arc<dyn WeakItemHandle>,
|
||||
|
@ -296,6 +302,7 @@ impl Pane {
|
|||
focus_handle,
|
||||
items: Vec::new(),
|
||||
activation_history: Vec::new(),
|
||||
next_activation_timestamp: next_timestamp.clone(),
|
||||
was_focused: false,
|
||||
zoomed: false,
|
||||
active_item_index: 0,
|
||||
|
@ -506,7 +513,7 @@ impl Pane {
|
|||
self.active_item_index
|
||||
}
|
||||
|
||||
pub fn activation_history(&self) -> &Vec<EntityId> {
|
||||
pub fn activation_history(&self) -> &[ActivationHistoryEntry] {
|
||||
&self.activation_history
|
||||
}
|
||||
|
||||
|
@ -892,10 +899,13 @@ impl Pane {
|
|||
|
||||
if let Some(newly_active_item) = self.items.get(index) {
|
||||
self.activation_history
|
||||
.retain(|&previously_active_item_id| {
|
||||
previously_active_item_id != newly_active_item.item_id()
|
||||
});
|
||||
self.activation_history.push(newly_active_item.item_id());
|
||||
.retain(|entry| entry.entity_id != newly_active_item.item_id());
|
||||
self.activation_history.push(ActivationHistoryEntry {
|
||||
entity_id: newly_active_item.item_id(),
|
||||
timestamp: self
|
||||
.next_activation_timestamp
|
||||
.fetch_add(1, Ordering::SeqCst),
|
||||
});
|
||||
}
|
||||
|
||||
self.update_toolbar(cx);
|
||||
|
@ -1211,7 +1221,7 @@ impl Pane {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.activation_history
|
||||
.retain(|&history_entry| history_entry != self.items[item_index].item_id());
|
||||
.retain(|entry| entry.entity_id != self.items[item_index].item_id());
|
||||
|
||||
if item_index == self.active_item_index {
|
||||
let index_to_activate = self
|
||||
|
@ -1219,7 +1229,7 @@ impl Pane {
|
|||
.pop()
|
||||
.and_then(|last_activated_item| {
|
||||
self.items.iter().enumerate().find_map(|(index, item)| {
|
||||
(item.item_id() == last_activated_item).then_some(index)
|
||||
(item.item_id() == last_activated_item.entity_id).then_some(index)
|
||||
})
|
||||
})
|
||||
// We didn't have a valid activation history entry, so fallback
|
||||
|
|
|
@ -532,6 +532,9 @@ impl DelayedDebouncedEditAction {
|
|||
|
||||
pub enum Event {
|
||||
PaneAdded(View<Pane>),
|
||||
PaneRemoved,
|
||||
ItemAdded,
|
||||
ItemRemoved,
|
||||
ActiveItemChanged,
|
||||
ContactRequestedJoin(u64),
|
||||
WorkspaceCreated(WeakView<Workspace>),
|
||||
|
@ -2513,7 +2516,10 @@ impl Workspace {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
pane::Event::AddItem { item } => item.added_to_pane(self, pane, cx),
|
||||
pane::Event::AddItem { item } => {
|
||||
item.added_to_pane(self, pane, cx);
|
||||
cx.emit(Event::ItemAdded);
|
||||
}
|
||||
pane::Event::Split(direction) => {
|
||||
self.split_and_clone(pane, *direction, cx);
|
||||
}
|
||||
|
@ -2696,6 +2702,7 @@ impl Workspace {
|
|||
} else {
|
||||
self.active_item_path_changed(cx);
|
||||
}
|
||||
cx.emit(Event::PaneRemoved);
|
||||
}
|
||||
|
||||
pub fn panes(&self) -> &[View<Pane>] {
|
||||
|
|
Loading…
Reference in a new issue