From a0c0f1ebcd4497e0025d72ed16b48471a7c24ee2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Jun 2024 11:40:54 +0200 Subject: [PATCH] Rename conversations to contexts (#12724) This just changes nomenclature within the codebase and should have no external effect. Release Notes: - N/A --- assets/keymaps/default-linux.json | 2 +- assets/keymaps/default-macos.json | 2 +- crates/assistant/src/assistant.rs | 4 +- crates/assistant/src/assistant_panel.rs | 740 ++++++++---------- ...conversation_store.rs => context_store.rs} | 85 +- crates/assistant/src/inline_assistant.rs | 14 +- crates/assistant/src/slash_command.rs | 6 +- crates/util/src/paths.rs | 2 +- 8 files changed, 400 insertions(+), 455 deletions(-) rename crates/assistant/src/{conversation_store.rs => context_store.rs} (66%) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index f3832fae6f..34b5cd5caf 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -545,7 +545,7 @@ } }, { - "context": "ConversationEditor > Editor", + "context": "ContextEditor > Editor", "bindings": { "ctrl-enter": "assistant::Assist", "ctrl-s": "workspace::Save", diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 9ad328608f..8f20a43834 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -228,7 +228,7 @@ } }, { - "context": "ConversationEditor > Editor", + "context": "ContextEditor > Editor", "bindings": { "cmd-enter": "assistant::Assist", "cmd-s": "workspace::Save", diff --git a/crates/assistant/src/assistant.rs b/crates/assistant/src/assistant.rs index b3b6b8d4f8..0c9c4a4c11 100644 --- a/crates/assistant/src/assistant.rs +++ b/crates/assistant/src/assistant.rs @@ -1,7 +1,7 @@ pub mod assistant_panel; pub mod assistant_settings; mod completion_provider; -mod conversation_store; +mod context_store; mod inline_assistant; mod model_selector; mod prompt_library; @@ -17,7 +17,7 @@ use assistant_slash_command::SlashCommandRegistry; use client::{proto, Client}; use command_palette_hooks::CommandPaletteFilter; pub(crate) use completion_provider::*; -pub(crate) use conversation_store::*; +pub(crate) use context_store::*; use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal}; pub(crate) use inline_assistant::*; pub(crate) use model_selector::*; diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index ad8312990e..e7fadd70a7 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -6,11 +6,10 @@ use crate::{ default_command::DefaultSlashCommand, SlashCommandCompletionProvider, SlashCommandLine, SlashCommandRegistry, }, - ApplyEdit, Assist, CompletionProvider, ConfirmCommand, ConversationStore, CycleMessageRole, + ApplyEdit, Assist, CompletionProvider, ConfirmCommand, ContextStore, CycleMessageRole, InlineAssist, InlineAssistant, LanguageModelRequest, LanguageModelRequestMessage, MessageId, - MessageMetadata, MessageStatus, ModelSelector, QuoteSelection, ResetKey, Role, - SavedConversation, SavedConversationMetadata, SavedMessage, Split, ToggleFocus, ToggleHistory, - ToggleModelSelector, + MessageMetadata, MessageStatus, ModelSelector, QuoteSelection, ResetKey, Role, SavedContext, + SavedContextMetadata, SavedMessage, Split, ToggleFocus, ToggleHistory, ToggleModelSelector, }; use anyhow::{anyhow, Result}; use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection}; @@ -30,10 +29,10 @@ use futures::future::Shared; use futures::{FutureExt, StreamExt}; use gpui::{ div, point, rems, Action, AnyElement, AnyView, AppContext, AsyncAppContext, AsyncWindowContext, - ClipboardItem, Context, Empty, EventEmitter, FocusHandle, FocusableView, InteractiveElement, - IntoElement, Model, ModelContext, ParentElement, Pixels, Render, SharedString, - StatefulInteractiveElement, Styled, Subscription, Task, UpdateGlobal, View, ViewContext, - VisualContext, WeakView, WindowContext, + ClipboardItem, Context as _, Empty, EventEmitter, FocusHandle, FocusableView, + InteractiveElement, IntoElement, Model, ModelContext, ParentElement, Pixels, Render, + SharedString, StatefulInteractiveElement, Styled, Subscription, Task, UpdateGlobal, View, + ViewContext, VisualContext, WeakView, WindowContext, }; use language::{ language_settings::SoftWrap, AnchorRangeExt, AutoindentMode, Buffer, LanguageRegistry, @@ -58,7 +57,7 @@ use ui::{ popover_menu, prelude::*, ButtonLike, ContextMenu, ElevationIndex, KeyBinding, ListItem, ListItemSpacing, PopoverMenuHandle, Tab, TabBar, Tooltip, }; -use util::{paths::CONVERSATIONS_DIR, post_inc, ResultExt, TryFutureExt}; +use util::{paths::CONTEXTS_DIR, post_inc, ResultExt, TryFutureExt}; use uuid::Uuid; use workspace::NewFile; use workspace::{ @@ -81,8 +80,7 @@ pub fn init(cx: &mut AppContext) { }) .register_action(AssistantPanel::inline_assist) .register_action(AssistantPanel::cancel_last_inline_assist) - // .register_action(ConversationEditor::insert_active_prompt) - .register_action(ConversationEditor::quote_selection); + .register_action(ContextEditor::quote_selection); }, ) .detach(); @@ -92,10 +90,10 @@ pub struct AssistantPanel { workspace: WeakView, width: Option, height: Option, - active_conversation_editor: Option, - show_saved_conversations: bool, - conversation_store: Model, - saved_conversation_picker: View>, + active_context_editor: Option, + show_saved_contexts: bool, + context_store: Model, + saved_context_picker: View>, zoomed: bool, focus_handle: FocusHandle, toolbar: View, @@ -108,20 +106,20 @@ pub struct AssistantPanel { model_menu_handle: PopoverMenuHandle, } -struct SavedConversationPickerDelegate { - store: Model, - matches: Vec, +struct SavedContextPickerDelegate { + store: Model, + matches: Vec, selected_index: usize, } -enum SavedConversationPickerEvent { +enum SavedContextPickerEvent { Confirmed { path: PathBuf }, } -impl EventEmitter for Picker {} +impl EventEmitter for Picker {} -impl SavedConversationPickerDelegate { - fn new(store: Model) -> Self { +impl SavedContextPickerDelegate { + fn new(store: Model) -> Self { Self { store, matches: Vec::new(), @@ -130,7 +128,7 @@ impl SavedConversationPickerDelegate { } } -impl PickerDelegate for SavedConversationPickerDelegate { +impl PickerDelegate for SavedContextPickerDelegate { type ListItem = ListItem; fn match_count(&self) -> usize { @@ -164,7 +162,7 @@ impl PickerDelegate for SavedConversationPickerDelegate { fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext>) { if let Some(metadata) = self.matches.get(self.selected_index) { - cx.emit(SavedConversationPickerEvent::Confirmed { + cx.emit(SavedContextPickerEvent::Confirmed { path: metadata.path.clone(), }) } @@ -178,7 +176,7 @@ impl PickerDelegate for SavedConversationPickerDelegate { selected: bool, _cx: &mut ViewContext>, ) -> Option { - let conversation = self.matches.get(ix)?; + let context = self.matches.get(ix)?; Some( ListItem::new(ix) .inset(true) @@ -190,18 +188,18 @@ impl PickerDelegate for SavedConversationPickerDelegate { .w_full() .gap_2() .child( - Label::new(conversation.mtime.format("%F %I:%M%p").to_string()) + Label::new(context.mtime.format("%F %I:%M%p").to_string()) .color(Color::Muted) .size(LabelSize::Small), ) - .child(Label::new(conversation.title.clone()).size(LabelSize::Small)), + .child(Label::new(context.title.clone()).size(LabelSize::Small)), ), ) } } -struct ActiveConversationEditor { - editor: View, +struct ActiveContextEditor { + editor: View, _subscriptions: Vec, } @@ -212,9 +210,7 @@ impl AssistantPanel { ) -> Task>> { cx.spawn(|mut cx| async move { let fs = workspace.update(&mut cx, |workspace, _| workspace.app_state().fs.clone())?; - let conversation_store = cx - .update(|cx| ConversationStore::new(fs.clone(), cx))? - .await?; + let context_store = cx.update(|cx| ContextStore::new(fs.clone(), cx))?.await?; // TODO: deserialize state. let workspace_handle = workspace.clone(); @@ -227,9 +223,9 @@ impl AssistantPanel { toolbar }); - let saved_conversation_picker = cx.new_view(|cx| { + let saved_context_picker = cx.new_view(|cx| { Picker::uniform_list( - SavedConversationPickerDelegate::new(conversation_store.clone()), + SavedContextPickerDelegate::new(context_store.clone()), cx, ) .modal(false) @@ -249,13 +245,13 @@ impl AssistantPanel { CompletionProvider::global(cx).settings_version(); } }), - cx.observe(&conversation_store, |this, _, cx| { - this.saved_conversation_picker + cx.observe(&context_store, |this, _, cx| { + this.saved_context_picker .update(cx, |picker, cx| picker.refresh(cx)); }), cx.subscribe( - &saved_conversation_picker, - Self::handle_saved_conversation_picker_event, + &saved_context_picker, + Self::handle_saved_context_picker_event, ), ]; @@ -266,10 +262,10 @@ impl AssistantPanel { Self { workspace: workspace_handle, - active_conversation_editor: None, - show_saved_conversations: false, - saved_conversation_picker, - conversation_store, + active_context_editor: None, + show_saved_contexts: false, + saved_context_picker, + context_store, zoomed: false, focus_handle, toolbar, @@ -293,10 +289,10 @@ impl AssistantPanel { .update(cx, |toolbar, cx| toolbar.focus_changed(true, cx)); cx.notify(); if self.focus_handle.is_focused(cx) { - if self.show_saved_conversations { - cx.focus_view(&self.saved_conversation_picker); - } else if let Some(conversation) = self.active_conversation_editor() { - cx.focus_view(conversation); + if self.show_saved_contexts { + cx.focus_view(&self.saved_context_picker); + } else if let Some(context) = self.active_context_editor() { + cx.focus_view(context); } } } @@ -315,18 +311,16 @@ impl AssistantPanel { if self.is_authenticated(cx) { self.authentication_prompt = None; - if let Some(editor) = self.active_conversation_editor() { - editor.update(cx, |active_conversation, cx| { - active_conversation - .conversation - .update(cx, |conversation, cx| { - conversation.completion_provider_changed(cx) - }) + if let Some(editor) = self.active_context_editor() { + editor.update(cx, |active_context, cx| { + active_context + .context + .update(cx, |context, cx| context.completion_provider_changed(cx)) }) } - if self.active_conversation_editor().is_none() { - self.new_conversation(cx); + if self.active_context_editor().is_none() { + self.new_context(cx); } cx.notify(); } else if self.authentication_prompt.is_none() @@ -340,16 +334,15 @@ impl AssistantPanel { } } - fn handle_saved_conversation_picker_event( + fn handle_saved_context_picker_event( &mut self, - _picker: View>, - event: &SavedConversationPickerEvent, + _picker: View>, + event: &SavedContextPickerEvent, cx: &mut ViewContext, ) { match event { - SavedConversationPickerEvent::Confirmed { path } => { - self.open_conversation(path.clone(), cx) - .detach_and_log_err(cx); + SavedContextPickerEvent::Confirmed { path } => { + self.open_context(path.clone(), cx).detach_and_log_err(cx); } } } @@ -368,30 +361,29 @@ impl AssistantPanel { return; }; - let conversation_editor = - assistant - .read(cx) - .active_conversation_editor() - .and_then(|editor| { - let editor = &editor.read(cx).editor; - if editor.read(cx).is_focused(cx) { - Some(editor.clone()) - } else { - None - } - }); + let context_editor = assistant + .read(cx) + .active_context_editor() + .and_then(|editor| { + let editor = &editor.read(cx).editor; + if editor.read(cx).is_focused(cx) { + Some(editor.clone()) + } else { + None + } + }); - let include_conversation; + let include_context; let active_editor; - if let Some(conversation_editor) = conversation_editor { - active_editor = conversation_editor; - include_conversation = false; + if let Some(context_editor) = context_editor { + active_editor = context_editor; + include_context = false; } else if let Some(workspace_editor) = workspace .active_item(cx) .and_then(|item| item.act_as::(cx)) { active_editor = workspace_editor; - include_conversation = true; + include_context = true; } else { return; }; @@ -401,7 +393,7 @@ impl AssistantPanel { assistant.assist( &active_editor, Some(cx.view().downgrade()), - include_conversation, + include_context, cx, ) }) @@ -414,12 +406,7 @@ impl AssistantPanel { if assistant.update(&mut cx, |assistant, cx| assistant.is_authenticated(cx))? { cx.update(|cx| { InlineAssistant::update_global(cx, |assistant, cx| { - assistant.assist( - &active_editor, - Some(workspace), - include_conversation, - cx, - ) + assistant.assist(&active_editor, Some(workspace), include_context, cx) }) })? } else { @@ -447,11 +434,11 @@ impl AssistantPanel { } } - fn new_conversation(&mut self, cx: &mut ViewContext) -> Option> { + fn new_context(&mut self, cx: &mut ViewContext) -> Option> { let workspace = self.workspace.upgrade()?; let editor = cx.new_view(|cx| { - ConversationEditor::new( + ContextEditor::new( self.languages.clone(), self.slash_commands.clone(), self.fs.clone(), @@ -460,46 +447,41 @@ impl AssistantPanel { ) }); - self.show_conversation(editor.clone(), cx); + self.show_context(editor.clone(), cx); Some(editor) } - fn show_conversation( - &mut self, - conversation_editor: View, - cx: &mut ViewContext, - ) { + fn show_context(&mut self, context_editor: View, cx: &mut ViewContext) { let mut subscriptions = Vec::new(); - subscriptions - .push(cx.subscribe(&conversation_editor, Self::handle_conversation_editor_event)); + subscriptions.push(cx.subscribe(&context_editor, Self::handle_context_editor_event)); - let conversation = conversation_editor.read(cx).conversation.clone(); - subscriptions.push(cx.observe(&conversation, |_, _, cx| cx.notify())); + let context = context_editor.read(cx).context.clone(); + subscriptions.push(cx.observe(&context, |_, _, cx| cx.notify())); - let editor = conversation_editor.read(cx).editor.clone(); + let editor = context_editor.read(cx).editor.clone(); self.toolbar.update(cx, |toolbar, cx| { toolbar.set_active_item(Some(&editor), cx); }); if self.focus_handle.contains_focused(cx) { cx.focus_view(&editor); } - self.active_conversation_editor = Some(ActiveConversationEditor { - editor: conversation_editor, + self.active_context_editor = Some(ActiveContextEditor { + editor: context_editor, _subscriptions: subscriptions, }); - self.show_saved_conversations = false; + self.show_saved_contexts = false; cx.notify(); } - fn handle_conversation_editor_event( + fn handle_context_editor_event( &mut self, - _: View, - event: &ConversationEditorEvent, + _: View, + event: &ContextEditorEvent, cx: &mut ViewContext, ) { match event { - ConversationEditorEvent::TabContentChanged => cx.notify(), + ContextEditorEvent::TabContentChanged => cx.notify(), } } @@ -512,7 +494,7 @@ impl AssistantPanel { } fn toggle_history(&mut self, _: &ToggleHistory, cx: &mut ViewContext) { - if self.show_saved_conversations { + if self.show_saved_contexts { self.hide_history(cx); } else { self.show_history(cx); @@ -520,17 +502,17 @@ impl AssistantPanel { } fn show_history(&mut self, cx: &mut ViewContext) { - cx.focus_view(&self.saved_conversation_picker); - if !self.show_saved_conversations { - self.show_saved_conversations = true; + cx.focus_view(&self.saved_context_picker); + if !self.show_saved_contexts { + self.show_saved_contexts = true; cx.notify(); } } fn hide_history(&mut self, cx: &mut ViewContext) { - if let Some(editor) = self.active_conversation_editor() { + if let Some(editor) = self.active_context_editor() { cx.focus_view(&editor); - self.show_saved_conversations = false; + self.show_saved_contexts = false; cx.notify(); } } @@ -590,24 +572,19 @@ impl AssistantPanel { } fn insert_command(&mut self, name: &str, cx: &mut ViewContext) { - if let Some(conversation_editor) = self.active_conversation_editor() { - conversation_editor.update(cx, |conversation_editor, cx| { - conversation_editor.insert_command(name, cx) + if let Some(context_editor) = self.active_context_editor() { + context_editor.update(cx, |context_editor, cx| { + context_editor.insert_command(name, cx) }); } } - fn active_conversation_editor(&self) -> Option<&View> { - Some(&self.active_conversation_editor.as_ref()?.editor) + fn active_context_editor(&self) -> Option<&View> { + Some(&self.active_context_editor.as_ref()?.editor) } - pub fn active_conversation(&self, cx: &AppContext) -> Option> { - Some( - self.active_conversation_editor()? - .read(cx) - .conversation - .clone(), - ) + pub fn active_context(&self, cx: &AppContext) -> Option> { + Some(self.active_context_editor()?.read(cx).context.clone()) } fn render_popover_button(&self, cx: &mut ViewContext) -> impl IntoElement { @@ -708,30 +685,28 @@ impl AssistantPanel { } fn render_send_button(&self, cx: &mut ViewContext) -> Option { - self.active_conversation_editor - .as_ref() - .map(|conversation| { - let focus_handle = conversation.editor.focus_handle(cx); - ButtonLike::new("send_button") - .style(ButtonStyle::Filled) - .layer(ElevationIndex::ModalSurface) - .children( - KeyBinding::for_action_in(&Assist, &focus_handle, cx) - .map(|binding| binding.into_any_element()), - ) - .child(Label::new("Send")) - .on_click(cx.listener(|this, _event, cx| { - if let Some(active_editor) = this.active_conversation_editor() { - active_editor.update(cx, |editor, cx| editor.assist(&Assist, cx)); - } - })) - }) + self.active_context_editor.as_ref().map(|context| { + let focus_handle = context.editor.focus_handle(cx); + ButtonLike::new("send_button") + .style(ButtonStyle::Filled) + .layer(ElevationIndex::ModalSurface) + .children( + KeyBinding::for_action_in(&Assist, &focus_handle, cx) + .map(|binding| binding.into_any_element()), + ) + .child(Label::new("Send")) + .on_click(cx.listener(|this, _event, cx| { + if let Some(active_editor) = this.active_context_editor() { + active_editor.update(cx, |editor, cx| editor.assist(&Assist, cx)); + } + })) + }) } - fn open_conversation(&mut self, path: PathBuf, cx: &mut ViewContext) -> Task> { + fn open_context(&mut self, path: PathBuf, cx: &mut ViewContext) -> Task> { cx.focus(&self.focus_handle); - let saved_conversation = self.conversation_store.read(cx).load(path.clone(), cx); + let saved_context = self.context_store.read(cx).load(path.clone(), cx); let fs = self.fs.clone(); let workspace = self.workspace.clone(); let slash_commands = self.slash_commands.clone(); @@ -746,9 +721,9 @@ impl AssistantPanel { .flatten(); cx.spawn(|this, mut cx| async move { - let saved_conversation = saved_conversation.await?; - let conversation = Conversation::deserialize( - saved_conversation, + let saved_context = saved_context.await?; + let context = Context::deserialize( + saved_context, path.clone(), languages, slash_commands, @@ -762,15 +737,9 @@ impl AssistantPanel { .upgrade() .ok_or_else(|| anyhow!("workspace dropped"))?; let editor = cx.new_view(|cx| { - ConversationEditor::for_conversation( - conversation, - fs, - workspace, - lsp_adapter_delegate, - cx, - ) + ContextEditor::for_context(context, fs, workspace, lsp_adapter_delegate, cx) }); - this.show_conversation(editor, cx); + this.show_context(editor, cx); anyhow::Ok(()) })??; Ok(()) @@ -788,7 +757,7 @@ impl AssistantPanel { fn render_signed_in(&mut self, cx: &mut ViewContext) -> impl IntoElement { let header = TabBar::new("assistant_header") .start_child(h_flex().gap_1().child(self.render_popover_button(cx))) - .children(self.active_conversation_editor().map(|editor| { + .children(self.active_context_editor().map(|editor| { h_flex() .h(rems(Tab::CONTAINER_HEIGHT_IN_REMS)) .flex_1() @@ -804,8 +773,8 @@ impl AssistantPanel { .end_child( h_flex() .gap_2() - .when_some(self.active_conversation_editor(), |this, editor| { - let conversation = editor.read(cx).conversation.clone(); + .when_some(self.active_context_editor(), |this, editor| { + let context = editor.read(cx).context.clone(); this.child( h_flex() .gap_1() @@ -813,7 +782,7 @@ impl AssistantPanel { self.model_menu_handle.clone(), self.fs.clone(), )) - .children(self.render_remaining_tokens(&conversation, cx)), + .children(self.render_remaining_tokens(&context, cx)), ) .child( ui::Divider::vertical() @@ -840,7 +809,7 @@ impl AssistantPanel { ), ); - let contents = if self.active_conversation_editor().is_some() { + let contents = if self.active_context_editor().is_some() { let mut registrar = DivRegistrar::new( |panel, cx| panel.toolbar.read(cx).item_of_type::(), cx, @@ -855,7 +824,7 @@ impl AssistantPanel { .key_context("AssistantPanel") .size_full() .on_action(cx.listener(|this, _: &workspace::NewFile, cx| { - this.new_conversation(cx); + this.new_context(cx); })) .on_action(cx.listener(AssistantPanel::toggle_zoom)) .on_action(cx.listener(AssistantPanel::toggle_history)) @@ -873,12 +842,12 @@ impl AssistantPanel { Some(self.toolbar.clone()) }) .child(contents.flex_1().child( - if self.show_saved_conversations || self.active_conversation_editor().is_none() { + if self.show_saved_contexts || self.active_context_editor().is_none() { div() .size_full() - .child(self.saved_conversation_picker.clone()) + .child(self.saved_context_picker.clone()) .into_any_element() - } else if let Some(editor) = self.active_conversation_editor() { + } else if let Some(editor) = self.active_context_editor() { let editor = editor.clone(); div() .size_full() @@ -901,10 +870,10 @@ impl AssistantPanel { fn render_remaining_tokens( &self, - conversation: &Model, + context: &Model, cx: &mut ViewContext, ) -> Option { - let remaining_tokens = conversation.read(cx).remaining_tokens(cx)?; + let remaining_tokens = context.read(cx).remaining_tokens(cx)?; let remaining_tokens_color = if remaining_tokens <= 0 { Color::Error } else if remaining_tokens <= 500 { @@ -991,8 +960,8 @@ impl Panel for AssistantPanel { cx.spawn(|this, mut cx| async move { load_credentials.await?; this.update(&mut cx, |this, cx| { - if this.is_authenticated(cx) && this.active_conversation_editor().is_none() { - this.new_conversation(cx); + if this.is_authenticated(cx) && this.active_context_editor().is_none() { + this.new_context(cx); } }) }) @@ -1027,7 +996,7 @@ impl FocusableView for AssistantPanel { } #[derive(Clone)] -enum ConversationEvent { +enum ContextEvent { MessagesEdited, SummaryChanged, EditSuggestionsChanged, @@ -1049,7 +1018,7 @@ struct Summary { done: bool, } -pub struct Conversation { +pub struct Context { id: Option, buffer: Model, edit_suggestions: Vec, @@ -1073,9 +1042,9 @@ pub struct Conversation { language_registry: Arc, } -impl EventEmitter for Conversation {} +impl EventEmitter for Context {} -impl Conversation { +impl Context { fn new( language_registry: Arc, slash_command_registry: Arc, @@ -1131,11 +1100,11 @@ impl Conversation { this } - fn serialize(&self, cx: &AppContext) -> SavedConversation { - SavedConversation { + fn serialize(&self, cx: &AppContext) -> SavedContext { + SavedContext { id: self.id.clone(), - zed: "conversation".into(), - version: SavedConversation::VERSION.into(), + zed: "context".into(), + version: SavedContext::VERSION.into(), text: self.buffer.read(cx).text(), message_metadata: self.messages_metadata.clone(), messages: self @@ -1155,14 +1124,14 @@ impl Conversation { #[allow(clippy::too_many_arguments)] async fn deserialize( - saved_conversation: SavedConversation, + saved_context: SavedContext, path: PathBuf, language_registry: Arc, slash_command_registry: Arc, telemetry: Option>, cx: &mut AsyncAppContext, ) -> Result> { - let id = match saved_conversation.id { + let id = match saved_context.id { Some(id) => Some(id), None => Some(Uuid::new_v4().to_string()), }; @@ -1171,8 +1140,8 @@ impl Conversation { let mut message_anchors = Vec::new(); let mut next_message_id = MessageId(0); let buffer = cx.new_model(|cx| { - let mut buffer = Buffer::local(saved_conversation.text, cx); - for message in saved_conversation.messages { + let mut buffer = Buffer::local(saved_context.text, cx); + for message in saved_context.messages { message_anchors.push(MessageAnchor { id: message.id, start: buffer.anchor_before(message.start), @@ -1197,13 +1166,13 @@ impl Conversation { let mut this = Self { id, message_anchors, - messages_metadata: saved_conversation.message_metadata, + messages_metadata: saved_context.message_metadata, next_message_id, edit_suggestions: Vec::new(), pending_slash_commands: Vec::new(), edits_since_last_slash_command_parse, summary: Some(Summary { - text: saved_conversation.summary, + text: saved_context.summary, done: true, }), pending_summary: Task::ready(None), @@ -1249,7 +1218,7 @@ impl Conversation { self.count_remaining_tokens(cx); self.reparse_edit_suggestions(cx); self.reparse_slash_commands(cx); - cx.emit(ConversationEvent::MessagesEdited); + cx.emit(ContextEvent::MessagesEdited); } } @@ -1346,7 +1315,7 @@ impl Conversation { } if !updated.is_empty() || !removed.is_empty() { - cx.emit(ConversationEvent::PendingSlashCommandsUpdated { removed, updated }); + cx.emit(ContextEvent::PendingSlashCommandsUpdated { removed, updated }); } } @@ -1405,7 +1374,7 @@ impl Conversation { self.edit_suggestions .splice(start_ix..end_ix, new_edit_suggestions); }); - cx.emit(ConversationEvent::EditSuggestionsChanged); + cx.emit(ContextEvent::EditSuggestionsChanged); cx.notify(); } @@ -1499,7 +1468,7 @@ impl Conversation { }) .collect::>(); sections.sort_by(|a, b| a.range.cmp(&b.range, buffer)); - ConversationEvent::SlashCommandFinished { + ContextEvent::SlashCommandFinished { output_range: buffer.anchor_after(start) ..buffer.anchor_before(new_end), sections, @@ -1514,7 +1483,7 @@ impl Conversation { { pending_command.status = PendingSlashCommandStatus::Error(error.to_string()); - cx.emit(ConversationEvent::PendingSlashCommandsUpdated { + cx.emit(ContextEvent::PendingSlashCommandsUpdated { removed: vec![pending_command.source_range.clone()], updated: vec![pending_command.clone()], }); @@ -1529,7 +1498,7 @@ impl Conversation { pending_command.status = PendingSlashCommandStatus::Running { _task: insert_output_task.shared(), }; - cx.emit(ConversationEvent::PendingSlashCommandsUpdated { + cx.emit(ContextEvent::PendingSlashCommandsUpdated { removed: vec![pending_command.source_range.clone()], updated: vec![pending_command.clone()], }); @@ -1644,7 +1613,7 @@ impl Conversation { message_start_offset..message_new_end_offset }); this.reparse_edit_suggestions_in_range(message_range, cx); - cx.emit(ConversationEvent::StreamedCompletion); + cx.emit(ContextEvent::StreamedCompletion); Some(()) })?; @@ -1687,7 +1656,7 @@ impl Conversation { ); } - cx.emit(ConversationEvent::MessagesEdited); + cx.emit(ContextEvent::MessagesEdited); } }) .ok(); @@ -1725,7 +1694,7 @@ impl Conversation { for id in ids { if let Some(metadata) = self.messages_metadata.get_mut(&id) { metadata.role.cycle(); - cx.emit(ConversationEvent::MessagesEdited); + cx.emit(ContextEvent::MessagesEdited); cx.notify(); } } @@ -1768,7 +1737,7 @@ impl Conversation { .insert(next_message_ix, message.clone()); self.messages_metadata .insert(message.id, MessageMetadata { role, status }); - cx.emit(ConversationEvent::MessagesEdited); + cx.emit(ContextEvent::MessagesEdited); Some(message) } else { None @@ -1846,7 +1815,7 @@ impl Conversation { } let selection = if let Some(prefix_end) = prefix_end { - cx.emit(ConversationEvent::MessagesEdited); + cx.emit(ContextEvent::MessagesEdited); MessageAnchor { id: MessageId(post_inc(&mut self.next_message_id.0)), start: self.buffer.read(cx).anchor_before(prefix_end), @@ -1875,7 +1844,7 @@ impl Conversation { }; if !edited_buffer { - cx.emit(ConversationEvent::MessagesEdited); + cx.emit(ContextEvent::MessagesEdited); } new_messages } else { @@ -1894,8 +1863,7 @@ impl Conversation { .map(|message| message.to_request_message(self.buffer.read(cx))) .chain(Some(LanguageModelRequestMessage { role: Role::User, - content: "Summarize the conversation into a short title without punctuation." - .into(), + content: "Summarize the context into a short title without punctuation.".into(), })); let request = LanguageModelRequest { model: CompletionProvider::global(cx).model(), @@ -1915,7 +1883,7 @@ impl Conversation { this.update(&mut cx, |this, cx| { let summary = this.summary.get_or_insert(Default::default()); summary.text.extend(lines.next()); - cx.emit(ConversationEvent::SummaryChanged); + cx.emit(ContextEvent::SummaryChanged); })?; // Stop if the LLM generated multiple lines. @@ -1927,7 +1895,7 @@ impl Conversation { this.update(&mut cx, |this, cx| { if let Some(summary) = this.summary.as_mut() { summary.done = true; - cx.emit(ConversationEvent::SummaryChanged); + cx.emit(ContextEvent::SummaryChanged); } })?; @@ -2014,7 +1982,7 @@ impl Conversation { &mut self, debounce: Option, fs: Arc, - cx: &mut ModelContext, + cx: &mut ModelContext, ) { self.pending_save = cx.spawn(|this, mut cx| async move { if let Some(debounce) = debounce { @@ -2036,14 +2004,14 @@ impl Conversation { })?; if let Some(summary) = summary { - let conversation = this.read_with(&cx, |this, cx| this.serialize(cx))?; + let context = this.read_with(&cx, |this, cx| this.serialize(cx))?; let path = if let Some(old_path) = old_path { old_path } else { let mut discriminant = 1; let mut new_path; loop { - new_path = CONVERSATIONS_DIR.join(&format!( + new_path = CONTEXTS_DIR.join(&format!( "{} - {}.zed.json", summary.trim(), discriminant @@ -2057,8 +2025,8 @@ impl Conversation { new_path }; - fs.create_dir(CONVERSATIONS_DIR.as_ref()).await?; - fs.atomic_write(path.clone(), serde_json::to_string(&conversation).unwrap()) + fs.create_dir(CONTEXTS_DIR.as_ref()).await?; + fs.atomic_write(path.clone(), serde_json::to_string(&context).unwrap()) .await?; this.update(&mut cx, |this, _| this.path = Some(path))?; } @@ -2181,7 +2149,7 @@ struct PendingCompletion { _task: Task<()>, } -enum ConversationEditorEvent { +enum ContextEditorEvent { TabContentChanged, } @@ -2191,8 +2159,8 @@ struct ScrollPosition { cursor: Anchor, } -pub struct ConversationEditor { - conversation: Model, +pub struct ContextEditor { + context: Model, fs: Arc, workspace: WeakView, slash_command_registry: Arc, @@ -2204,7 +2172,7 @@ pub struct ConversationEditor { _subscriptions: Vec, } -impl ConversationEditor { +impl ContextEditor { fn new( language_registry: Arc, slash_command_registry: Arc, @@ -2216,8 +2184,8 @@ impl ConversationEditor { let project = workspace.read(cx).project().clone(); let lsp_adapter_delegate = make_lsp_adapter_delegate(&project, cx).log_err(); - let conversation = cx.new_model(|cx| { - Conversation::new( + let context = cx.new_model(|cx| { + Context::new( language_registry, slash_command_registry, Some(telemetry), @@ -2225,20 +2193,19 @@ impl ConversationEditor { ) }); - let mut this = - Self::for_conversation(conversation, fs, workspace, lsp_adapter_delegate, cx); + let mut this = Self::for_context(context, fs, workspace, lsp_adapter_delegate, cx); this.insert_default_prompt(cx); this } - fn for_conversation( - conversation: Model, + fn for_context( + context: Model, fs: Arc, workspace: View, lsp_adapter_delegate: Option>, cx: &mut ViewContext, ) -> Self { - let slash_command_registry = conversation.read(cx).slash_command_registry.clone(); + let slash_command_registry = context.read(cx).slash_command_registry.clone(); let completion_provider = SlashCommandCompletionProvider::new( slash_command_registry.clone(), @@ -2247,7 +2214,7 @@ impl ConversationEditor { ); let editor = cx.new_view(|cx| { - let mut editor = Editor::for_buffer(conversation.read(cx).buffer.clone(), None, cx); + let mut editor = Editor::for_buffer(context.read(cx).buffer.clone(), None, cx); editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx); editor.set_show_line_numbers(false, cx); editor.set_show_git_diff_gutter(false, cx); @@ -2259,13 +2226,13 @@ impl ConversationEditor { }); let _subscriptions = vec![ - cx.observe(&conversation, |_, _, cx| cx.notify()), - cx.subscribe(&conversation, Self::handle_conversation_event), + cx.observe(&context, |_, _, cx| cx.notify()), + cx.subscribe(&context, Self::handle_context_event), cx.subscribe(&editor, Self::handle_editor_event), ]; let mut this = Self { - conversation, + context, editor, slash_command_registry, lsp_adapter_delegate, @@ -2286,14 +2253,14 @@ impl ConversationEditor { editor.insert(&format!("/{command_name}"), cx) }); self.split(&Split, cx); - let command = self.conversation.update(cx, |conversation, cx| { - conversation + let command = self.context.update(cx, |context, cx| { + context .messages_metadata .get_mut(&MessageId::default()) .unwrap() .role = Role::System; - conversation.reparse_slash_commands(cx); - conversation.pending_slash_commands[0].clone() + context.reparse_slash_commands(cx); + context.pending_slash_commands[0].clone() }); self.run_command( @@ -2309,20 +2276,20 @@ impl ConversationEditor { fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { let cursors = self.cursors(cx); - let user_messages = self.conversation.update(cx, |conversation, cx| { - let selected_messages = conversation + let user_messages = self.context.update(cx, |context, cx| { + let selected_messages = context .messages_for_offsets(cursors, cx) .into_iter() .map(|message| message.id) .collect(); - conversation.assist(selected_messages, cx) + context.assist(selected_messages, cx) }); let new_selections = user_messages .iter() .map(|message| { let cursor = message .start - .to_offset(self.conversation.read(cx).buffer.read(cx)); + .to_offset(self.context.read(cx).buffer.read(cx)); cursor..cursor }) .collect::>(); @@ -2341,8 +2308,8 @@ impl ConversationEditor { fn cancel_last_assist(&mut self, _: &editor::actions::Cancel, cx: &mut ViewContext) { if !self - .conversation - .update(cx, |conversation, _| conversation.cancel_last_assist()) + .context + .update(cx, |context, _| context.cancel_last_assist()) { cx.propagate(); } @@ -2350,13 +2317,13 @@ impl ConversationEditor { fn cycle_message_role(&mut self, _: &CycleMessageRole, cx: &mut ViewContext) { let cursors = self.cursors(cx); - self.conversation.update(cx, |conversation, cx| { - let messages = conversation + self.context.update(cx, |context, cx| { + let messages = context .messages_for_offsets(cursors, cx) .into_iter() .map(|message| message.id) .collect(); - conversation.cycle_message_roles(messages, cx) + context.cycle_message_roles(messages, cx) }); } @@ -2407,11 +2374,11 @@ impl ConversationEditor { let selections = self.editor.read(cx).selections.disjoint_anchors(); let mut commands_by_range = HashMap::default(); let workspace = self.workspace.clone(); - self.conversation.update(cx, |conversation, cx| { - conversation.reparse_slash_commands(cx); + self.context.update(cx, |context, cx| { + context.reparse_slash_commands(cx); for selection in selections.iter() { if let Some(command) = - conversation.pending_command_for_position(selection.head().text_anchor, cx) + context.pending_command_for_position(selection.head().text_anchor, cx) { commands_by_range .entry(command.source_range.clone()) @@ -2450,8 +2417,8 @@ impl ConversationEditor { if let Some(lsp_adapter_delegate) = self.lsp_adapter_delegate.clone() { let argument = argument.map(ToString::to_string); let output = command.run(argument.as_deref(), workspace, lsp_adapter_delegate, cx); - self.conversation.update(cx, |conversation, cx| { - conversation.insert_command_output( + self.context.update(cx, |context, cx| { + context.insert_command_output( command_range, output, insert_trailing_newline, @@ -2462,27 +2429,27 @@ impl ConversationEditor { } } - fn handle_conversation_event( + fn handle_context_event( &mut self, - _: Model, - event: &ConversationEvent, + _: Model, + event: &ContextEvent, cx: &mut ViewContext, ) { - let conversation_editor = cx.view().downgrade(); + let context_editor = cx.view().downgrade(); match event { - ConversationEvent::MessagesEdited => { + ContextEvent::MessagesEdited => { self.update_message_headers(cx); - self.conversation.update(cx, |conversation, cx| { - conversation.save(Some(Duration::from_millis(500)), self.fs.clone(), cx); + self.context.update(cx, |context, cx| { + context.save(Some(Duration::from_millis(500)), self.fs.clone(), cx); }); } - ConversationEvent::EditSuggestionsChanged => { + ContextEvent::EditSuggestionsChanged => { self.editor.update(cx, |editor, cx| { let buffer = editor.buffer().read(cx).snapshot(cx); let excerpt_id = *buffer.as_singleton().unwrap().0; - let conversation = self.conversation.read(cx); - let highlighted_rows = conversation + let context = self.context.read(cx); + let highlighted_rows = context .edit_suggestions .iter() .map(|suggestion| { @@ -2511,13 +2478,13 @@ impl ConversationEditor { } }); } - ConversationEvent::SummaryChanged => { - cx.emit(ConversationEditorEvent::TabContentChanged); - self.conversation.update(cx, |conversation, cx| { - conversation.save(None, self.fs.clone(), cx); + ContextEvent::SummaryChanged => { + cx.emit(ContextEditorEvent::TabContentChanged); + self.context.update(cx, |context, cx| { + context.save(None, self.fs.clone(), cx); }); } - ConversationEvent::StreamedCompletion => { + ContextEvent::StreamedCompletion => { self.editor.update(cx, |editor, cx| { if let Some(scroll_position) = self.scroll_position { let snapshot = editor.snapshot(cx); @@ -2531,7 +2498,7 @@ impl ConversationEditor { } }); } - ConversationEvent::PendingSlashCommandsUpdated { removed, updated } => { + ContextEvent::PendingSlashCommandsUpdated { removed, updated } => { self.editor.update(cx, |editor, cx| { let buffer = editor.buffer().read(cx).snapshot(cx); let excerpt_id = *buffer.as_singleton().unwrap().0; @@ -2547,12 +2514,12 @@ impl ConversationEditor { updated.iter().map(|command| { let workspace = self.workspace.clone(); let confirm_command = Arc::new({ - let conversation_editor = conversation_editor.clone(); + let context_editor = context_editor.clone(); let command = command.clone(); move |cx: &mut WindowContext| { - conversation_editor - .update(cx, |conversation_editor, cx| { - conversation_editor.run_command( + context_editor + .update(cx, |context_editor, cx| { + context_editor.run_command( command.source_range.clone(), &command.name, command.argument.as_deref(), @@ -2602,7 +2569,7 @@ impl ConversationEditor { ); }) } - ConversationEvent::SlashCommandFinished { + ContextEvent::SlashCommandFinished { output_range, sections, run_commands_in_output, @@ -2610,9 +2577,9 @@ impl ConversationEditor { self.insert_slash_command_output_sections(sections.iter().cloned(), cx); if *run_commands_in_output { - let commands = self.conversation.update(cx, |conversation, cx| { - conversation.reparse_slash_commands(cx); - conversation + let commands = self.context.update(cx, |context, cx| { + context.reparse_slash_commands(cx); + context .pending_commands_for_range(output_range.clone(), cx) .to_vec() }); @@ -2742,7 +2709,7 @@ impl ConversationEditor { let excerpt_id = *buffer.as_singleton().unwrap().0; let old_blocks = std::mem::take(&mut self.blocks); let new_blocks = self - .conversation + .context .read(cx) .messages(cx) .map(|message| BlockProperties { @@ -2752,7 +2719,7 @@ impl ConversationEditor { height: 2, style: BlockStyle::Sticky, render: Box::new({ - let conversation = self.conversation.clone(); + let context = self.context.clone(); move |cx| { let message_id = message.id; let sender = ButtonLike::new("role") @@ -2771,10 +2738,10 @@ impl ConversationEditor { ) }) .on_click({ - let conversation = conversation.clone(); + let context = context.clone(); move |_, cx| { - conversation.update(cx, |conversation, cx| { - conversation.cycle_message_roles( + context.update(cx, |context, cx| { + context.cycle_message_roles( HashSet::from_iter(Some(message_id)), cx, ) @@ -2864,16 +2831,16 @@ impl ConversationEditor { if let Some(text) = text { panel.update(cx, |_, cx| { - // Wait to create a new conversation until the workspace is no longer + // Wait to create a new context until the workspace is no longer // being updated. cx.defer(move |panel, cx| { - if let Some(conversation) = panel - .active_conversation_editor() + if let Some(context) = panel + .active_context_editor() .cloned() - .or_else(|| panel.new_conversation(cx)) + .or_else(|| panel.new_context(cx)) { - conversation.update(cx, |conversation, cx| { - conversation + context.update(cx, |context, cx| { + context .editor .update(cx, |editor, cx| editor.insert(&text, cx)) }); @@ -2885,12 +2852,12 @@ impl ConversationEditor { fn copy(&mut self, _: &editor::actions::Copy, cx: &mut ViewContext) { let editor = self.editor.read(cx); - let conversation = self.conversation.read(cx); + let context = self.context.read(cx); if editor.selections.count() == 1 { let selection = editor.selections.newest::(cx); let mut copied_text = String::new(); let mut spanned_messages = 0; - for message in conversation.messages(cx) { + for message in context.messages(cx) { if message.offset_range.start >= selection.range().end { break; } else if message.offset_range.end >= selection.range().start { @@ -2899,7 +2866,7 @@ impl ConversationEditor { if !range.is_empty() { spanned_messages += 1; write!(&mut copied_text, "## {}\n\n", message.role).unwrap(); - for chunk in conversation.buffer.read(cx).text_for_range(range) { + for chunk in context.buffer.read(cx).text_for_range(range) { copied_text.push_str(chunk); } copied_text.push('\n'); @@ -2917,14 +2884,14 @@ impl ConversationEditor { } fn split(&mut self, _: &Split, cx: &mut ViewContext) { - self.conversation.update(cx, |conversation, cx| { + self.context.update(cx, |context, cx| { let selections = self.editor.read(cx).selections.disjoint_anchors(); for selection in selections.as_ref() { let buffer = self.editor.read(cx).buffer().read(cx).snapshot(cx); let range = selection .map(|endpoint| endpoint.to_offset(&buffer)) .range(); - conversation.split_message(range, cx); + context.split_message(range, cx); } }); } @@ -2940,13 +2907,13 @@ impl ConversationEditor { new_text: String, } - let conversation = self.conversation.read(cx); - let conversation_buffer = conversation.buffer.read(cx); - let conversation_buffer_snapshot = conversation_buffer.snapshot(); + let context = self.context.read(cx); + let context_buffer = context.buffer.read(cx); + let context_buffer_snapshot = context_buffer.snapshot(); let selections = self.editor.read(cx).selections.disjoint_anchors(); let mut selections = selections.iter().peekable(); - let selected_suggestions = conversation + let selected_suggestions = context .edit_suggestions .iter() .filter(|suggestion| { @@ -2954,7 +2921,7 @@ impl ConversationEditor { if selection .end .text_anchor - .cmp(&suggestion.source_range.start, conversation_buffer) + .cmp(&suggestion.source_range.start, context_buffer) .is_lt() { selections.next(); @@ -2963,7 +2930,7 @@ impl ConversationEditor { if selection .start .text_anchor - .cmp(&suggestion.source_range.end, conversation_buffer) + .cmp(&suggestion.source_range.end, context_buffer) .is_gt() { break; @@ -3002,19 +2969,17 @@ impl ConversationEditor { .entry(buffer.clone()) .or_insert_with(|| (buffer.read(cx).snapshot(), Vec::new())); - let mut lines = conversation_buffer_snapshot + let mut lines = context_buffer_snapshot .as_rope() .chunks_in_range( - suggestion - .source_range - .to_offset(&conversation_buffer_snapshot), + suggestion.source_range.to_offset(&context_buffer_snapshot), ) .lines(); if let Some(suggestion) = parse_next_edit_suggestion(&mut lines) { - let old_text = conversation_buffer_snapshot + let old_text = context_buffer_snapshot .text_for_range(suggestion.old_text_range) .collect(); - let new_text = conversation_buffer_snapshot + let new_text = context_buffer_snapshot .text_for_range(suggestion.new_text_range) .collect(); edits.push(Edit { old_text, new_text }); @@ -3099,13 +3064,12 @@ impl ConversationEditor { } fn save(&mut self, _: &Save, cx: &mut ViewContext) { - self.conversation.update(cx, |conversation, cx| { - conversation.save(None, self.fs.clone(), cx) - }); + self.context + .update(cx, |context, cx| context.save(None, self.fs.clone(), cx)); } fn title(&self, cx: &AppContext) -> String { - self.conversation + self.context .read(cx) .summary .as_ref() @@ -3114,20 +3078,20 @@ impl ConversationEditor { } } -impl EventEmitter for ConversationEditor {} +impl EventEmitter for ContextEditor {} -impl Render for ConversationEditor { +impl Render for ContextEditor { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { div() - .key_context("ConversationEditor") - .capture_action(cx.listener(ConversationEditor::cancel_last_assist)) - .capture_action(cx.listener(ConversationEditor::save)) - .capture_action(cx.listener(ConversationEditor::copy)) - .capture_action(cx.listener(ConversationEditor::cycle_message_role)) - .capture_action(cx.listener(ConversationEditor::confirm_command)) - .on_action(cx.listener(ConversationEditor::assist)) - .on_action(cx.listener(ConversationEditor::split)) - .on_action(cx.listener(ConversationEditor::apply_edit)) + .key_context("ContextEditor") + .capture_action(cx.listener(ContextEditor::cancel_last_assist)) + .capture_action(cx.listener(ContextEditor::save)) + .capture_action(cx.listener(ContextEditor::copy)) + .capture_action(cx.listener(ContextEditor::cycle_message_role)) + .capture_action(cx.listener(ContextEditor::confirm_command)) + .on_action(cx.listener(ContextEditor::assist)) + .on_action(cx.listener(ContextEditor::split)) + .on_action(cx.listener(ContextEditor::apply_edit)) .size_full() .v_flex() .child( @@ -3139,7 +3103,7 @@ impl Render for ConversationEditor { } } -impl FocusableView for ConversationEditor { +impl FocusableView for ContextEditor { fn focus_handle(&self, cx: &AppContext) -> FocusHandle { self.editor.focus_handle(cx) } @@ -3259,23 +3223,22 @@ mod tests { init(cx); let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone())); - let conversation = - cx.new_model(|cx| Conversation::new(registry, Default::default(), None, cx)); - let buffer = conversation.read(cx).buffer.clone(); + let context = cx.new_model(|cx| Context::new(registry, Default::default(), None, cx)); + let buffer = context.read(cx).buffer.clone(); - let message_1 = conversation.read(cx).message_anchors[0].clone(); + let message_1 = context.read(cx).message_anchors[0].clone(); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![(message_1.id, Role::User, 0..0)] ); - let message_2 = conversation.update(cx, |conversation, cx| { - conversation + let message_2 = context.update(cx, |context, cx| { + context .insert_message_after(message_1.id, Role::Assistant, MessageStatus::Done, cx) .unwrap() }); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..1), (message_2.id, Role::Assistant, 1..1) @@ -3286,20 +3249,20 @@ mod tests { buffer.edit([(0..0, "1"), (1..1, "2")], None, cx) }); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..2), (message_2.id, Role::Assistant, 2..3) ] ); - let message_3 = conversation.update(cx, |conversation, cx| { - conversation + let message_3 = context.update(cx, |context, cx| { + context .insert_message_after(message_2.id, Role::User, MessageStatus::Done, cx) .unwrap() }); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..2), (message_2.id, Role::Assistant, 2..4), @@ -3307,13 +3270,13 @@ mod tests { ] ); - let message_4 = conversation.update(cx, |conversation, cx| { - conversation + let message_4 = context.update(cx, |context, cx| { + context .insert_message_after(message_2.id, Role::User, MessageStatus::Done, cx) .unwrap() }); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..2), (message_2.id, Role::Assistant, 2..4), @@ -3326,7 +3289,7 @@ mod tests { buffer.edit([(4..4, "C"), (5..5, "D")], None, cx) }); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..2), (message_2.id, Role::Assistant, 2..4), @@ -3338,7 +3301,7 @@ mod tests { // Deleting across message boundaries merges the messages. buffer.update(cx, |buffer, cx| buffer.edit([(1..4, "")], None, cx)); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..3), (message_3.id, Role::User, 3..4), @@ -3348,7 +3311,7 @@ mod tests { // Undoing the deletion should also undo the merge. buffer.update(cx, |buffer, cx| buffer.undo(cx)); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..2), (message_2.id, Role::Assistant, 2..4), @@ -3360,7 +3323,7 @@ mod tests { // Redoing the deletion should also redo the merge. buffer.update(cx, |buffer, cx| buffer.redo(cx)); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..3), (message_3.id, Role::User, 3..4), @@ -3368,13 +3331,13 @@ mod tests { ); // Ensure we can still insert after a merged message. - let message_5 = conversation.update(cx, |conversation, cx| { - conversation + let message_5 = context.update(cx, |context, cx| { + context .insert_message_after(message_1.id, Role::System, MessageStatus::Done, cx) .unwrap() }); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..3), (message_5.id, Role::System, 3..4), @@ -3391,13 +3354,12 @@ mod tests { init(cx); let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone())); - let conversation = - cx.new_model(|cx| Conversation::new(registry, Default::default(), None, cx)); - let buffer = conversation.read(cx).buffer.clone(); + let context = cx.new_model(|cx| Context::new(registry, Default::default(), None, cx)); + let buffer = context.read(cx).buffer.clone(); - let message_1 = conversation.read(cx).message_anchors[0].clone(); + let message_1 = context.read(cx).message_anchors[0].clone(); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![(message_1.id, Role::User, 0..0)] ); @@ -3405,28 +3367,26 @@ mod tests { buffer.edit([(0..0, "aaa\nbbb\nccc\nddd\n")], None, cx) }); - let (_, message_2) = - conversation.update(cx, |conversation, cx| conversation.split_message(3..3, cx)); + let (_, message_2) = context.update(cx, |context, cx| context.split_message(3..3, cx)); let message_2 = message_2.unwrap(); // We recycle newlines in the middle of a split message assert_eq!(buffer.read(cx).text(), "aaa\nbbb\nccc\nddd\n"); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..4), (message_2.id, Role::User, 4..16), ] ); - let (_, message_3) = - conversation.update(cx, |conversation, cx| conversation.split_message(3..3, cx)); + let (_, message_3) = context.update(cx, |context, cx| context.split_message(3..3, cx)); let message_3 = message_3.unwrap(); // We don't recycle newlines at the end of a split message assert_eq!(buffer.read(cx).text(), "aaa\n\nbbb\nccc\nddd\n"); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..4), (message_3.id, Role::User, 4..5), @@ -3434,12 +3394,11 @@ mod tests { ] ); - let (_, message_4) = - conversation.update(cx, |conversation, cx| conversation.split_message(9..9, cx)); + let (_, message_4) = context.update(cx, |context, cx| context.split_message(9..9, cx)); let message_4 = message_4.unwrap(); assert_eq!(buffer.read(cx).text(), "aaa\n\nbbb\nccc\nddd\n"); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..4), (message_3.id, Role::User, 4..5), @@ -3448,12 +3407,11 @@ mod tests { ] ); - let (_, message_5) = - conversation.update(cx, |conversation, cx| conversation.split_message(9..9, cx)); + let (_, message_5) = context.update(cx, |context, cx| context.split_message(9..9, cx)); let message_5 = message_5.unwrap(); assert_eq!(buffer.read(cx).text(), "aaa\n\nbbb\n\nccc\nddd\n"); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..4), (message_3.id, Role::User, 4..5), @@ -3463,14 +3421,13 @@ mod tests { ] ); - let (message_6, message_7) = conversation.update(cx, |conversation, cx| { - conversation.split_message(14..16, cx) - }); + let (message_6, message_7) = + context.update(cx, |context, cx| context.split_message(14..16, cx)); let message_6 = message_6.unwrap(); let message_7 = message_7.unwrap(); assert_eq!(buffer.read(cx).text(), "aaa\n\nbbb\n\nccc\ndd\nd\n"); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..4), (message_3.id, Role::User, 4..5), @@ -3490,34 +3447,33 @@ mod tests { cx.set_global(settings_store); init(cx); let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone())); - let conversation = - cx.new_model(|cx| Conversation::new(registry, Default::default(), None, cx)); - let buffer = conversation.read(cx).buffer.clone(); + let context = cx.new_model(|cx| Context::new(registry, Default::default(), None, cx)); + let buffer = context.read(cx).buffer.clone(); - let message_1 = conversation.read(cx).message_anchors[0].clone(); + let message_1 = context.read(cx).message_anchors[0].clone(); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![(message_1.id, Role::User, 0..0)] ); buffer.update(cx, |buffer, cx| buffer.edit([(0..0, "aaa")], None, cx)); - let message_2 = conversation - .update(cx, |conversation, cx| { - conversation.insert_message_after(message_1.id, Role::User, MessageStatus::Done, cx) + let message_2 = context + .update(cx, |context, cx| { + context.insert_message_after(message_1.id, Role::User, MessageStatus::Done, cx) }) .unwrap(); buffer.update(cx, |buffer, cx| buffer.edit([(4..4, "bbb")], None, cx)); - let message_3 = conversation - .update(cx, |conversation, cx| { - conversation.insert_message_after(message_2.id, Role::User, MessageStatus::Done, cx) + let message_3 = context + .update(cx, |context, cx| { + context.insert_message_after(message_2.id, Role::User, MessageStatus::Done, cx) }) .unwrap(); buffer.update(cx, |buffer, cx| buffer.edit([(8..8, "ccc")], None, cx)); assert_eq!(buffer.read(cx).text(), "aaa\nbbb\nccc"); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..4), (message_2.id, Role::User, 4..8), @@ -3526,22 +3482,22 @@ mod tests { ); assert_eq!( - message_ids_for_offsets(&conversation, &[0, 4, 9], cx), + message_ids_for_offsets(&context, &[0, 4, 9], cx), [message_1.id, message_2.id, message_3.id] ); assert_eq!( - message_ids_for_offsets(&conversation, &[0, 1, 11], cx), + message_ids_for_offsets(&context, &[0, 1, 11], cx), [message_1.id, message_3.id] ); - let message_4 = conversation - .update(cx, |conversation, cx| { - conversation.insert_message_after(message_3.id, Role::User, MessageStatus::Done, cx) + let message_4 = context + .update(cx, |context, cx| { + context.insert_message_after(message_3.id, Role::User, MessageStatus::Done, cx) }) .unwrap(); assert_eq!(buffer.read(cx).text(), "aaa\nbbb\nccc\n"); assert_eq!( - messages(&conversation, cx), + messages(&context, cx), vec![ (message_1.id, Role::User, 0..4), (message_2.id, Role::User, 4..8), @@ -3550,16 +3506,16 @@ mod tests { ] ); assert_eq!( - message_ids_for_offsets(&conversation, &[0, 4, 8, 12], cx), + message_ids_for_offsets(&context, &[0, 4, 8, 12], cx), [message_1.id, message_2.id, message_3.id, message_4.id] ); fn message_ids_for_offsets( - conversation: &Model, + context: &Model, offsets: &[usize], cx: &AppContext, ) -> Vec { - conversation + context .read(cx) .messages_for_offsets(offsets.iter().copied(), cx) .into_iter() @@ -3596,15 +3552,15 @@ mod tests { slash_command_registry.register_command(active_command::ActiveSlashCommand, false); let registry = Arc::new(LanguageRegistry::test(cx.executor())); - let conversation = cx - .new_model(|cx| Conversation::new(registry.clone(), slash_command_registry, None, cx)); + let context = + cx.new_model(|cx| Context::new(registry.clone(), slash_command_registry, None, cx)); let output_ranges = Rc::new(RefCell::new(HashSet::default())); - conversation.update(cx, |_, cx| { - cx.subscribe(&conversation, { + context.update(cx, |_, cx| { + cx.subscribe(&context, { let ranges = output_ranges.clone(); move |_, _, event, _| match event { - ConversationEvent::PendingSlashCommandsUpdated { removed, updated } => { + ContextEvent::PendingSlashCommandsUpdated { removed, updated } => { for range in removed { ranges.borrow_mut().remove(range); } @@ -3618,7 +3574,7 @@ mod tests { .detach(); }); - let buffer = conversation.read_with(cx, |conversation, _| conversation.buffer.clone()); + let buffer = context.read_with(cx, |context, _| context.buffer.clone()); // Insert a slash command buffer.update(cx, |buffer, cx| { @@ -3770,18 +3726,17 @@ mod tests { cx.set_global(CompletionProvider::Fake(FakeCompletionProvider::default())); cx.update(init); let registry = Arc::new(LanguageRegistry::test(cx.executor())); - let conversation = - cx.new_model(|cx| Conversation::new(registry.clone(), Default::default(), None, cx)); - let buffer = conversation.read_with(cx, |conversation, _| conversation.buffer.clone()); - let message_0 = - conversation.read_with(cx, |conversation, _| conversation.message_anchors[0].id); - let message_1 = conversation.update(cx, |conversation, cx| { - conversation + let context = + cx.new_model(|cx| Context::new(registry.clone(), Default::default(), None, cx)); + let buffer = context.read_with(cx, |context, _| context.buffer.clone()); + let message_0 = context.read_with(cx, |context, _| context.message_anchors[0].id); + let message_1 = context.update(cx, |context, cx| { + context .insert_message_after(message_0, Role::Assistant, MessageStatus::Done, cx) .unwrap() }); - let message_2 = conversation.update(cx, |conversation, cx| { - conversation + let message_2 = context.update(cx, |context, cx| { + context .insert_message_after(message_1.id, Role::System, MessageStatus::Done, cx) .unwrap() }); @@ -3789,15 +3744,15 @@ mod tests { buffer.edit([(0..0, "a"), (1..1, "b\nc")], None, cx); buffer.finalize_last_transaction(); }); - let _message_3 = conversation.update(cx, |conversation, cx| { - conversation + let _message_3 = context.update(cx, |context, cx| { + context .insert_message_after(message_2.id, Role::System, MessageStatus::Done, cx) .unwrap() }); buffer.update(cx, |buffer, cx| buffer.undo(cx)); assert_eq!(buffer.read_with(cx, |buffer, _| buffer.text()), "a\nb\nc\n"); assert_eq!( - cx.read(|cx| messages(&conversation, cx)), + cx.read(|cx| messages(&context, cx)), [ (message_0, Role::User, 0..2), (message_1.id, Role::Assistant, 2..6), @@ -3805,8 +3760,8 @@ mod tests { ] ); - let deserialized_conversation = Conversation::deserialize( - conversation.read_with(cx, |conversation, cx| conversation.serialize(cx)), + let deserialized_context = Context::deserialize( + context.read_with(cx, |context, cx| context.serialize(cx)), Default::default(), registry.clone(), Default::default(), @@ -3816,13 +3771,13 @@ mod tests { .await .unwrap(); let deserialized_buffer = - deserialized_conversation.read_with(cx, |conversation, _| conversation.buffer.clone()); + deserialized_context.read_with(cx, |context, _| context.buffer.clone()); assert_eq!( deserialized_buffer.read_with(cx, |buffer, _| buffer.text()), "a\nb\nc\n" ); assert_eq!( - cx.read(|cx| messages(&deserialized_conversation, cx)), + cx.read(|cx| messages(&deserialized_context, cx)), [ (message_0, Role::User, 0..2), (message_1.id, Role::Assistant, 2..6), @@ -3831,11 +3786,8 @@ mod tests { ); } - fn messages( - conversation: &Model, - cx: &AppContext, - ) -> Vec<(MessageId, Role, Range)> { - conversation + fn messages(context: &Model, cx: &AppContext) -> Vec<(MessageId, Role, Range)> { + context .read(cx) .messages(cx) .map(|message| (message.id, message.role, message.offset_range)) diff --git a/crates/assistant/src/conversation_store.rs b/crates/assistant/src/context_store.rs similarity index 66% rename from crates/assistant/src/conversation_store.rs rename to crates/assistant/src/context_store.rs index 4e683ff475..7eb366709f 100644 --- a/crates/assistant/src/conversation_store.rs +++ b/crates/assistant/src/context_store.rs @@ -9,7 +9,7 @@ use regex::Regex; use serde::{Deserialize, Serialize}; use std::{cmp::Reverse, ffi::OsStr, path::PathBuf, sync::Arc, time::Duration}; use ui::Context; -use util::{paths::CONVERSATIONS_DIR, ResultExt, TryFutureExt}; +use util::{paths::CONTEXTS_DIR, ResultExt, TryFutureExt}; #[derive(Serialize, Deserialize)] pub struct SavedMessage { @@ -18,7 +18,7 @@ pub struct SavedMessage { } #[derive(Serialize, Deserialize)] -pub struct SavedConversation { +pub struct SavedContext { pub id: Option, pub zed: String, pub version: String, @@ -28,12 +28,12 @@ pub struct SavedConversation { pub summary: String, } -impl SavedConversation { +impl SavedContext { pub const VERSION: &'static str = "0.2.0"; } #[derive(Serialize, Deserialize)] -struct SavedConversationV0_1_0 { +struct SavedContextV0_1_0 { id: Option, zed: String, version: String, @@ -46,28 +46,26 @@ struct SavedConversationV0_1_0 { } #[derive(Clone)] -pub struct SavedConversationMetadata { +pub struct SavedContextMetadata { pub title: String, pub path: PathBuf, pub mtime: chrono::DateTime, } -pub struct ConversationStore { - conversations_metadata: Vec, +pub struct ContextStore { + contexts_metadata: Vec, fs: Arc, _watch_updates: Task>, } -impl ConversationStore { +impl ContextStore { pub fn new(fs: Arc, cx: &mut AppContext) -> Task>> { cx.spawn(|mut cx| async move { - const CONVERSATION_WATCH_DURATION: Duration = Duration::from_millis(100); - let (mut events, _) = fs - .watch(&CONVERSATIONS_DIR, CONVERSATION_WATCH_DURATION) - .await; + const CONTEXT_WATCH_DURATION: Duration = Duration::from_millis(100); + let (mut events, _) = fs.watch(&CONTEXTS_DIR, CONTEXT_WATCH_DURATION).await; let this = cx.new_model(|cx: &mut ModelContext| Self { - conversations_metadata: Vec::new(), + contexts_metadata: Vec::new(), fs, _watch_updates: cx.spawn(|this, mut cx| { async move { @@ -88,46 +86,41 @@ impl ConversationStore { }) } - pub fn load(&self, path: PathBuf, cx: &AppContext) -> Task> { + pub fn load(&self, path: PathBuf, cx: &AppContext) -> Task> { let fs = self.fs.clone(); cx.background_executor().spawn(async move { - let saved_conversation = fs.load(&path).await?; - let saved_conversation_json = - serde_json::from_str::(&saved_conversation)?; - match saved_conversation_json + let saved_context = fs.load(&path).await?; + let saved_context_json = serde_json::from_str::(&saved_context)?; + match saved_context_json .get("version") .ok_or_else(|| anyhow!("version not found"))? { serde_json::Value::String(version) => match version.as_str() { - SavedConversation::VERSION => Ok(serde_json::from_value::( - saved_conversation_json, - )?), + SavedContext::VERSION => { + Ok(serde_json::from_value::(saved_context_json)?) + } "0.1.0" => { - let saved_conversation = serde_json::from_value::( - saved_conversation_json, - )?; - Ok(SavedConversation { - id: saved_conversation.id, - zed: saved_conversation.zed, - version: saved_conversation.version, - text: saved_conversation.text, - messages: saved_conversation.messages, - message_metadata: saved_conversation.message_metadata, - summary: saved_conversation.summary, + let saved_context = + serde_json::from_value::(saved_context_json)?; + Ok(SavedContext { + id: saved_context.id, + zed: saved_context.zed, + version: saved_context.version, + text: saved_context.text, + messages: saved_context.messages, + message_metadata: saved_context.message_metadata, + summary: saved_context.summary, }) } - _ => Err(anyhow!( - "unrecognized saved conversation version: {}", - version - )), + _ => Err(anyhow!("unrecognized saved context version: {}", version)), }, - _ => Err(anyhow!("version not found on saved conversation")), + _ => Err(anyhow!("version not found on saved context")), } }) } - pub fn search(&self, query: String, cx: &AppContext) -> Task> { - let metadata = self.conversations_metadata.clone(); + pub fn search(&self, query: String, cx: &AppContext) -> Task> { + let metadata = self.contexts_metadata.clone(); let executor = cx.background_executor().clone(); cx.background_executor().spawn(async move { if query.is_empty() { @@ -159,10 +152,10 @@ impl ConversationStore { fn reload(&mut self, cx: &mut ModelContext) -> Task> { let fs = self.fs.clone(); cx.spawn(|this, mut cx| async move { - fs.create_dir(&CONVERSATIONS_DIR).await?; + fs.create_dir(&CONTEXTS_DIR).await?; - let mut paths = fs.read_dir(&CONVERSATIONS_DIR).await?; - let mut conversations = Vec::::new(); + let mut paths = fs.read_dir(&CONTEXTS_DIR).await?; + let mut contexts = Vec::::new(); while let Some(path) = paths.next().await { let path = path?; if path.extension() != Some(OsStr::new("json")) { @@ -178,13 +171,13 @@ impl ConversationStore { .and_then(|name| name.to_str()) .zip(metadata) { - // This is used to filter out conversations saved by the new assistant. + // This is used to filter out contexts saved by the new assistant. if !re.is_match(file_name) { continue; } if let Some(title) = re.replace(file_name, "").lines().next() { - conversations.push(SavedConversationMetadata { + contexts.push(SavedContextMetadata { title: title.to_string(), path, mtime: metadata.mtime.into(), @@ -192,10 +185,10 @@ impl ConversationStore { } } } - conversations.sort_unstable_by_key(|conversation| Reverse(conversation.mtime)); + contexts.sort_unstable_by_key(|context| Reverse(context.mtime)); this.update(&mut cx, |this, cx| { - this.conversations_metadata = conversations; + this.contexts_metadata = contexts; cx.notify(); }) }) diff --git a/crates/assistant/src/inline_assistant.rs b/crates/assistant/src/inline_assistant.rs index 133b045fca..a40c4d1338 100644 --- a/crates/assistant/src/inline_assistant.rs +++ b/crates/assistant/src/inline_assistant.rs @@ -66,7 +66,7 @@ impl InlineAssistant { &mut self, editor: &View, workspace: Option>, - include_conversation: bool, + include_context: bool, cx: &mut WindowContext, ) { let selection = editor.read(cx).selections.newest_anchor().clone(); @@ -144,7 +144,7 @@ impl InlineAssistant { self.pending_assists.insert( inline_assist_id, PendingInlineAssist { - include_conversation, + include_context, editor: editor.downgrade(), inline_assist_editor: Some((block_id, inline_assist_editor.clone())), codegen: codegen.clone(), @@ -375,11 +375,11 @@ impl InlineAssistant { return; }; - let conversation = if pending_assist.include_conversation { + let context = if pending_assist.include_context { pending_assist.workspace.as_ref().and_then(|workspace| { let workspace = workspace.upgrade()?.read(cx); let assistant_panel = workspace.panel::(cx)?; - assistant_panel.read(cx).active_conversation(cx) + assistant_panel.read(cx).active_context(cx) }) } else { None @@ -461,8 +461,8 @@ impl InlineAssistant { }); let mut messages = Vec::new(); - if let Some(conversation) = conversation { - let request = conversation.read(cx).to_completion_request(cx); + if let Some(context) = context { + let request = context.read(cx).to_completion_request(cx); messages = request.messages; } let model = CompletionProvider::global(cx).model(); @@ -818,7 +818,7 @@ struct PendingInlineAssist { codegen: Model, _subscriptions: Vec, workspace: Option>, - include_conversation: bool, + include_context: bool, } #[derive(Debug)] diff --git a/crates/assistant/src/slash_command.rs b/crates/assistant/src/slash_command.rs index 0e2c861a5b..aba2859e77 100644 --- a/crates/assistant/src/slash_command.rs +++ b/crates/assistant/src/slash_command.rs @@ -1,4 +1,4 @@ -use crate::assistant_panel::ConversationEditor; +use crate::assistant_panel::ContextEditor; use anyhow::Result; pub use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandRegistry}; use editor::{CompletionProvider, Editor}; @@ -29,7 +29,7 @@ pub mod tabs_command; pub(crate) struct SlashCommandCompletionProvider { commands: Arc, cancel_flag: Mutex>, - editor: Option>, + editor: Option>, workspace: Option>, } @@ -43,7 +43,7 @@ pub(crate) struct SlashCommandLine { impl SlashCommandCompletionProvider { pub fn new( commands: Arc, - editor: Option>, + editor: Option>, workspace: Option>, ) -> Self { Self { diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 45b17d23f8..f0fd8dc5b9 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -21,7 +21,7 @@ lazy_static::lazy_static! { } else { HOME.join(".config").join("zed") }; - pub static ref CONVERSATIONS_DIR: PathBuf = if cfg!(target_os = "macos") { + pub static ref CONTEXTS_DIR: PathBuf = if cfg!(target_os = "macos") { CONFIG_DIR.join("conversations") } else { SUPPORT_DIR.join("conversations")