crates/assistant2/src/assistant.rs 🔗
@@ -3,6 +3,7 @@ mod assistant_panel;
mod assistant_settings;
mod context;
mod context_picker;
+mod context_strip;
mod inline_assistant;
mod message_editor;
mod prompts;
Marshall Bowers created
This PR factors a `ContextStrip` view out of the `MessageEditor` so that
we can use it in other places.
Release Notes:
- N/A
crates/assistant2/src/assistant.rs | 1
crates/assistant2/src/context_picker.rs | 14
crates/assistant2/src/context_picker/fetch_context_picker.rs | 18
crates/assistant2/src/context_picker/file_context_picker.rs | 46 +-
crates/assistant2/src/context_picker/thread_context_picker.rs | 18
crates/assistant2/src/context_strip.rs | 101 +++++
crates/assistant2/src/message_editor.rs | 85 ---
7 files changed, 156 insertions(+), 127 deletions(-)
@@ -3,6 +3,7 @@ mod assistant_panel;
mod assistant_settings;
mod context;
mod context_picker;
+mod context_strip;
mod inline_assistant;
mod message_editor;
mod prompts;
@@ -16,7 +16,7 @@ use workspace::Workspace;
use crate::context_picker::fetch_context_picker::FetchContextPicker;
use crate::context_picker::file_context_picker::FileContextPicker;
use crate::context_picker::thread_context_picker::ThreadContextPicker;
-use crate::message_editor::MessageEditor;
+use crate::context_strip::ContextStrip;
use crate::thread_store::ThreadStore;
#[derive(Debug, Clone)]
@@ -36,14 +36,14 @@ impl ContextPicker {
pub fn new(
workspace: WeakView<Workspace>,
thread_store: WeakModel<ThreadStore>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
cx: &mut ViewContext<Self>,
) -> Self {
let delegate = ContextPickerDelegate {
context_picker: cx.view().downgrade(),
workspace,
thread_store,
- message_editor,
+ context_strip,
entries: vec![
ContextPickerEntry {
name: "directory".into(),
@@ -122,7 +122,7 @@ pub(crate) struct ContextPickerDelegate {
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
thread_store: WeakModel<ThreadStore>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
entries: Vec<ContextPickerEntry>,
selected_ix: usize,
}
@@ -161,7 +161,7 @@ impl PickerDelegate for ContextPickerDelegate {
FileContextPicker::new(
self.context_picker.clone(),
self.workspace.clone(),
- self.message_editor.clone(),
+ self.context_strip.clone(),
cx,
)
}));
@@ -171,7 +171,7 @@ impl PickerDelegate for ContextPickerDelegate {
FetchContextPicker::new(
self.context_picker.clone(),
self.workspace.clone(),
- self.message_editor.clone(),
+ self.context_strip.clone(),
cx,
)
}));
@@ -181,7 +181,7 @@ impl PickerDelegate for ContextPickerDelegate {
ThreadContextPicker::new(
self.thread_store.clone(),
self.context_picker.clone(),
- self.message_editor.clone(),
+ self.context_strip.clone(),
cx,
)
}));
@@ -13,7 +13,7 @@ use workspace::Workspace;
use crate::context::ContextKind;
use crate::context_picker::ContextPicker;
-use crate::message_editor::MessageEditor;
+use crate::context_strip::ContextStrip;
pub struct FetchContextPicker {
picker: View<Picker<FetchContextPickerDelegate>>,
@@ -23,10 +23,10 @@ impl FetchContextPicker {
pub fn new(
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
cx: &mut ViewContext<Self>,
) -> Self {
- let delegate = FetchContextPickerDelegate::new(context_picker, workspace, message_editor);
+ let delegate = FetchContextPickerDelegate::new(context_picker, workspace, context_strip);
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
Self { picker }
@@ -55,7 +55,7 @@ enum ContentType {
pub struct FetchContextPickerDelegate {
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
url: String,
}
@@ -63,12 +63,12 @@ impl FetchContextPickerDelegate {
pub fn new(
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
) -> Self {
FetchContextPickerDelegate {
context_picker,
workspace,
- message_editor,
+ context_strip,
url: String::new(),
}
}
@@ -189,9 +189,9 @@ impl PickerDelegate for FetchContextPickerDelegate {
this.update(&mut cx, |this, cx| {
this.delegate
- .message_editor
- .update(cx, |message_editor, _cx| {
- message_editor.insert_context(ContextKind::FetchedUrl, url, text);
+ .context_strip
+ .update(cx, |context_strip, _cx| {
+ context_strip.insert_context(ContextKind::FetchedUrl, url, text);
})
})??;
@@ -14,7 +14,7 @@ use workspace::Workspace;
use crate::context::ContextKind;
use crate::context_picker::ContextPicker;
-use crate::message_editor::MessageEditor;
+use crate::context_strip::ContextStrip;
pub struct FileContextPicker {
picker: View<Picker<FileContextPickerDelegate>>,
@@ -24,10 +24,10 @@ impl FileContextPicker {
pub fn new(
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
cx: &mut ViewContext<Self>,
) -> Self {
- let delegate = FileContextPickerDelegate::new(context_picker, workspace, message_editor);
+ let delegate = FileContextPickerDelegate::new(context_picker, workspace, context_strip);
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
Self { picker }
@@ -49,7 +49,7 @@ impl Render for FileContextPicker {
pub struct FileContextPickerDelegate {
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
matches: Vec<PathMatch>,
selected_index: usize,
}
@@ -58,12 +58,12 @@ impl FileContextPickerDelegate {
pub fn new(
context_picker: WeakView<ContextPicker>,
workspace: WeakView<Workspace>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
) -> Self {
Self {
context_picker,
workspace,
- message_editor,
+ context_strip,
matches: Vec::new(),
selected_index: 0,
}
@@ -214,24 +214,22 @@ impl PickerDelegate for FileContextPickerDelegate {
let buffer = open_buffer_task.await?;
this.update(&mut cx, |this, cx| {
- this.delegate
- .message_editor
- .update(cx, |message_editor, cx| {
- let mut text = String::new();
- text.push_str(&codeblock_fence_for_path(Some(&path), None));
- text.push_str(&buffer.read(cx).text());
- if !text.ends_with('\n') {
- text.push('\n');
- }
-
- text.push_str("```\n");
-
- message_editor.insert_context(
- ContextKind::File,
- path.to_string_lossy().to_string(),
- text,
- );
- })
+ this.delegate.context_strip.update(cx, |context_strip, cx| {
+ let mut text = String::new();
+ text.push_str(&codeblock_fence_for_path(Some(&path), None));
+ text.push_str(&buffer.read(cx).text());
+ if !text.ends_with('\n') {
+ text.push('\n');
+ }
+
+ text.push_str("```\n");
+
+ context_strip.insert_context(
+ ContextKind::File,
+ path.to_string_lossy().to_string(),
+ text,
+ );
+ })
})??;
anyhow::Ok(())
@@ -7,7 +7,7 @@ use ui::{prelude::*, ListItem};
use crate::context::ContextKind;
use crate::context_picker::ContextPicker;
-use crate::message_editor::MessageEditor;
+use crate::context_strip::ContextStrip;
use crate::thread::ThreadId;
use crate::thread_store::ThreadStore;
@@ -19,11 +19,11 @@ impl ThreadContextPicker {
pub fn new(
thread_store: WeakModel<ThreadStore>,
context_picker: WeakView<ContextPicker>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
cx: &mut ViewContext<Self>,
) -> Self {
let delegate =
- ThreadContextPickerDelegate::new(thread_store, context_picker, message_editor);
+ ThreadContextPickerDelegate::new(thread_store, context_picker, context_strip);
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
ThreadContextPicker { picker }
@@ -51,7 +51,7 @@ struct ThreadContextEntry {
pub struct ThreadContextPickerDelegate {
thread_store: WeakModel<ThreadStore>,
context_picker: WeakView<ContextPicker>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
matches: Vec<ThreadContextEntry>,
selected_index: usize,
}
@@ -60,12 +60,12 @@ impl ThreadContextPickerDelegate {
pub fn new(
thread_store: WeakModel<ThreadStore>,
context_picker: WeakView<ContextPicker>,
- message_editor: WeakView<MessageEditor>,
+ context_strip: WeakView<ContextStrip>,
) -> Self {
ThreadContextPickerDelegate {
thread_store,
context_picker,
- message_editor,
+ context_strip,
matches: Vec::new(),
selected_index: 0,
}
@@ -157,8 +157,8 @@ impl PickerDelegate for ThreadContextPickerDelegate {
return;
};
- self.message_editor
- .update(cx, |message_editor, cx| {
+ self.context_strip
+ .update(cx, |context_strip, cx| {
let text = thread.update(cx, |thread, _cx| {
let mut text = String::new();
@@ -177,7 +177,7 @@ impl PickerDelegate for ThreadContextPickerDelegate {
text
});
- message_editor.insert_context(ContextKind::Thread, entry.summary.clone(), text);
+ context_strip.insert_context(ContextKind::Thread, entry.summary.clone(), text);
})
.ok();
}
@@ -0,0 +1,101 @@
+use std::rc::Rc;
+
+use gpui::{View, WeakModel, WeakView};
+use ui::{prelude::*, IconButtonShape, PopoverMenu, PopoverMenuHandle, Tooltip};
+use workspace::Workspace;
+
+use crate::context::{Context, ContextId, ContextKind};
+use crate::context_picker::ContextPicker;
+use crate::thread_store::ThreadStore;
+use crate::ui::ContextPill;
+
+pub struct ContextStrip {
+ context: Vec<Context>,
+ next_context_id: ContextId,
+ context_picker: View<ContextPicker>,
+ pub(crate) context_picker_handle: PopoverMenuHandle<ContextPicker>,
+}
+
+impl ContextStrip {
+ pub fn new(
+ workspace: WeakView<Workspace>,
+ thread_store: WeakModel<ThreadStore>,
+ cx: &mut ViewContext<Self>,
+ ) -> Self {
+ let weak_self = cx.view().downgrade();
+
+ Self {
+ context: Vec::new(),
+ next_context_id: ContextId(0),
+ context_picker: cx.new_view(|cx| {
+ ContextPicker::new(workspace.clone(), thread_store.clone(), weak_self, cx)
+ }),
+ context_picker_handle: PopoverMenuHandle::default(),
+ }
+ }
+
+ pub fn drain(&mut self) -> Vec<Context> {
+ self.context.drain(..).collect()
+ }
+
+ pub fn insert_context(
+ &mut self,
+ kind: ContextKind,
+ name: impl Into<SharedString>,
+ text: impl Into<SharedString>,
+ ) {
+ self.context.push(Context {
+ id: self.next_context_id.post_inc(),
+ name: name.into(),
+ kind,
+ text: text.into(),
+ });
+ }
+}
+
+impl Render for ContextStrip {
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+ let context_picker = self.context_picker.clone();
+
+ h_flex()
+ .flex_wrap()
+ .gap_2()
+ .child(
+ PopoverMenu::new("context-picker")
+ .menu(move |_cx| Some(context_picker.clone()))
+ .trigger(
+ IconButton::new("add-context", IconName::Plus)
+ .shape(IconButtonShape::Square)
+ .icon_size(IconSize::Small),
+ )
+ .attach(gpui::AnchorCorner::TopLeft)
+ .anchor(gpui::AnchorCorner::BottomLeft)
+ .offset(gpui::Point {
+ x: px(0.0),
+ y: px(-16.0),
+ })
+ .with_handle(self.context_picker_handle.clone()),
+ )
+ .children(self.context.iter().map(|context| {
+ ContextPill::new(context.clone()).on_remove({
+ let context = context.clone();
+ Rc::new(cx.listener(move |this, _event, cx| {
+ this.context.retain(|other| other.id != context.id);
+ cx.notify();
+ }))
+ })
+ }))
+ .when(!self.context.is_empty(), |parent| {
+ parent.child(
+ IconButton::new("remove-all-context", IconName::Eraser)
+ .shape(IconButtonShape::Square)
+ .icon_size(IconSize::Small)
+ .tooltip(move |cx| Tooltip::text("Remove All Context", cx))
+ .on_click(cx.listener(|this, _event, cx| {
+ this.context.clear();
+ cx.notify();
+ })),
+ )
+ })
+ }
+}
@@ -1,31 +1,21 @@
-use std::rc::Rc;
-
use editor::{Editor, EditorElement, EditorStyle};
use gpui::{AppContext, FocusableView, Model, TextStyle, View, WeakModel, WeakView};
use language_model::{LanguageModelRegistry, LanguageModelRequestTool};
use language_model_selector::{LanguageModelSelector, LanguageModelSelectorPopoverMenu};
use settings::Settings;
use theme::ThemeSettings;
-use ui::{
- prelude::*, ButtonLike, CheckboxWithLabel, ElevationIndex, IconButtonShape, KeyBinding,
- PopoverMenu, PopoverMenuHandle, Tooltip,
-};
+use ui::{prelude::*, ButtonLike, CheckboxWithLabel, ElevationIndex, KeyBinding, Tooltip};
use workspace::Workspace;
-use crate::context::{Context, ContextId, ContextKind};
-use crate::context_picker::ContextPicker;
+use crate::context_strip::ContextStrip;
use crate::thread::{RequestKind, Thread};
use crate::thread_store::ThreadStore;
-use crate::ui::ContextPill;
use crate::{Chat, ToggleModelSelector};
pub struct MessageEditor {
thread: Model<Thread>,
editor: View<Editor>,
- context: Vec<Context>,
- next_context_id: ContextId,
- context_picker: View<ContextPicker>,
- pub(crate) context_picker_handle: PopoverMenuHandle<ContextPicker>,
+ context_strip: View<ContextStrip>,
language_model_selector: View<LanguageModelSelector>,
use_tools: bool,
}
@@ -37,7 +27,6 @@ impl MessageEditor {
thread: Model<Thread>,
cx: &mut ViewContext<Self>,
) -> Self {
- let weak_self = cx.view().downgrade();
Self {
thread,
editor: cx.new_view(|cx| {
@@ -46,12 +35,8 @@ impl MessageEditor {
editor
}),
- context: Vec::new(),
- next_context_id: ContextId(0),
- context_picker: cx.new_view(|cx| {
- ContextPicker::new(workspace.clone(), thread_store.clone(), weak_self, cx)
- }),
- context_picker_handle: PopoverMenuHandle::default(),
+ context_strip: cx
+ .new_view(|cx| ContextStrip::new(workspace.clone(), thread_store.clone(), cx)),
language_model_selector: cx.new_view(|cx| {
LanguageModelSelector::new(
|model, _cx| {
@@ -64,20 +49,6 @@ impl MessageEditor {
}
}
- pub fn insert_context(
- &mut self,
- kind: ContextKind,
- name: impl Into<SharedString>,
- text: impl Into<SharedString>,
- ) {
- self.context.push(Context {
- id: self.next_context_id.post_inc(),
- name: name.into(),
- kind,
- text: text.into(),
- });
- }
-
fn chat(&mut self, _: &Chat, cx: &mut ViewContext<Self>) {
self.send_to_model(RequestKind::Chat, cx);
}
@@ -104,7 +75,7 @@ impl MessageEditor {
editor.clear(cx);
text
});
- let context = self.context.drain(..).collect::<Vec<_>>();
+ let context = self.context_strip.update(cx, |this, _cx| this.drain());
self.thread.update(cx, |thread, cx| {
thread.insert_user_message(user_message, context, cx);
@@ -190,7 +161,6 @@ impl Render for MessageEditor {
let font_size = TextSize::Default.rems(cx);
let line_height = font_size.to_pixels(cx.rem_size()) * 1.3;
let focus_handle = self.editor.focus_handle(cx);
- let context_picker = self.context_picker.clone();
v_flex()
.key_context("MessageEditor")
@@ -199,48 +169,7 @@ impl Render for MessageEditor {
.gap_2()
.p_2()
.bg(cx.theme().colors().editor_background)
- .child(
- h_flex()
- .flex_wrap()
- .gap_2()
- .child(
- PopoverMenu::new("context-picker")
- .menu(move |_cx| Some(context_picker.clone()))
- .trigger(
- IconButton::new("add-context", IconName::Plus)
- .shape(IconButtonShape::Square)
- .icon_size(IconSize::Small),
- )
- .attach(gpui::AnchorCorner::TopLeft)
- .anchor(gpui::AnchorCorner::BottomLeft)
- .offset(gpui::Point {
- x: px(0.0),
- y: px(-16.0),
- })
- .with_handle(self.context_picker_handle.clone()),
- )
- .children(self.context.iter().map(|context| {
- ContextPill::new(context.clone()).on_remove({
- let context = context.clone();
- Rc::new(cx.listener(move |this, _event, cx| {
- this.context.retain(|other| other.id != context.id);
- cx.notify();
- }))
- })
- }))
- .when(!self.context.is_empty(), |parent| {
- parent.child(
- IconButton::new("remove-all-context", IconName::Eraser)
- .shape(IconButtonShape::Square)
- .icon_size(IconSize::Small)
- .tooltip(move |cx| Tooltip::text("Remove All Context", cx))
- .on_click(cx.listener(|this, _event, cx| {
- this.context.clear();
- cx.notify();
- })),
- )
- }),
- )
+ .child(self.context_strip.clone())
.child({
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {