assistant: Extract ContextEditor
and ContextHistory
to their own modules (#23422)
This PR extracts the `ContextEditor` and `ContextHistory` implementations into their own modules so that it's clearer which parts depend on other constructs in the `assistant` crate. Release Notes: - N/A
This commit is contained in:
parent
c825bb492d
commit
51b6cbf9ac
6 changed files with 3830 additions and 3799 deletions
|
@ -2,6 +2,8 @@
|
|||
|
||||
pub mod assistant_panel;
|
||||
mod context;
|
||||
mod context_editor;
|
||||
mod context_history;
|
||||
pub mod context_store;
|
||||
mod inline_assistant;
|
||||
mod patch;
|
||||
|
|
File diff suppressed because it is too large
Load diff
3547
crates/assistant/src/context_editor.rs
Normal file
3547
crates/assistant/src/context_editor.rs
Normal file
File diff suppressed because it is too large
Load diff
249
crates/assistant/src/context_history.rs
Normal file
249
crates/assistant/src/context_history.rs
Normal file
|
@ -0,0 +1,249 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use gpui::{
|
||||
AppContext, EventEmitter, FocusHandle, FocusableView, Model, Subscription, Task, View, WeakView,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::Project;
|
||||
use ui::utils::{format_distance_from_now, DateTimeType};
|
||||
use ui::{prelude::*, Avatar, ListItem, ListItemSpacing};
|
||||
use workspace::Item;
|
||||
|
||||
use crate::context_editor::DEFAULT_TAB_TITLE;
|
||||
use crate::{AssistantPanel, ContextStore, RemoteContextMetadata, SavedContextMetadata};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ContextMetadata {
|
||||
Remote(RemoteContextMetadata),
|
||||
Saved(SavedContextMetadata),
|
||||
}
|
||||
|
||||
enum SavedContextPickerEvent {
|
||||
Confirmed(ContextMetadata),
|
||||
}
|
||||
|
||||
pub struct ContextHistory {
|
||||
picker: View<Picker<SavedContextPickerDelegate>>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
assistant_panel: WeakView<AssistantPanel>,
|
||||
}
|
||||
|
||||
impl ContextHistory {
|
||||
pub fn new(
|
||||
project: Model<Project>,
|
||||
context_store: Model<ContextStore>,
|
||||
assistant_panel: WeakView<AssistantPanel>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let picker = cx.new_view(|cx| {
|
||||
Picker::uniform_list(
|
||||
SavedContextPickerDelegate::new(project, context_store.clone()),
|
||||
cx,
|
||||
)
|
||||
.modal(false)
|
||||
.max_height(None)
|
||||
});
|
||||
|
||||
let _subscriptions = vec![
|
||||
cx.observe(&context_store, |this, _, cx| {
|
||||
this.picker.update(cx, |picker, cx| picker.refresh(cx));
|
||||
}),
|
||||
cx.subscribe(&picker, Self::handle_picker_event),
|
||||
];
|
||||
|
||||
Self {
|
||||
picker,
|
||||
_subscriptions,
|
||||
assistant_panel,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_picker_event(
|
||||
&mut self,
|
||||
_: View<Picker<SavedContextPickerDelegate>>,
|
||||
event: &SavedContextPickerEvent,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let SavedContextPickerEvent::Confirmed(context) = event;
|
||||
self.assistant_panel
|
||||
.update(cx, |assistant_panel, cx| match context {
|
||||
ContextMetadata::Remote(metadata) => {
|
||||
assistant_panel
|
||||
.open_remote_context(metadata.id.clone(), cx)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
ContextMetadata::Saved(metadata) => {
|
||||
assistant_panel
|
||||
.open_saved_context(metadata.path.clone(), cx)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ContextHistory {
|
||||
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div().size_full().child(self.picker.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FocusableView for ContextHistory {
|
||||
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
||||
self.picker.focus_handle(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<()> for ContextHistory {}
|
||||
|
||||
impl Item for ContextHistory {
|
||||
type Event = ();
|
||||
|
||||
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
|
||||
Some("History".into())
|
||||
}
|
||||
}
|
||||
|
||||
struct SavedContextPickerDelegate {
|
||||
store: Model<ContextStore>,
|
||||
project: Model<Project>,
|
||||
matches: Vec<ContextMetadata>,
|
||||
selected_index: usize,
|
||||
}
|
||||
|
||||
impl EventEmitter<SavedContextPickerEvent> for Picker<SavedContextPickerDelegate> {}
|
||||
|
||||
impl SavedContextPickerDelegate {
|
||||
fn new(project: Model<Project>, store: Model<ContextStore>) -> Self {
|
||||
Self {
|
||||
project,
|
||||
store,
|
||||
matches: Vec::new(),
|
||||
selected_index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PickerDelegate for SavedContextPickerDelegate {
|
||||
type ListItem = ListItem;
|
||||
|
||||
fn match_count(&self) -> usize {
|
||||
self.matches.len()
|
||||
}
|
||||
|
||||
fn selected_index(&self) -> usize {
|
||||
self.selected_index
|
||||
}
|
||||
|
||||
fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
|
||||
self.selected_index = ix;
|
||||
}
|
||||
|
||||
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
|
||||
"Search...".into()
|
||||
}
|
||||
|
||||
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
|
||||
let search = self.store.read(cx).search(query, cx);
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let matches = search.await;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let host_contexts = this.delegate.store.read(cx).host_contexts();
|
||||
this.delegate.matches = host_contexts
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(ContextMetadata::Remote)
|
||||
.chain(matches.into_iter().map(ContextMetadata::Saved))
|
||||
.collect();
|
||||
this.delegate.selected_index = 0;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
}
|
||||
|
||||
fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
|
||||
if let Some(metadata) = self.matches.get(self.selected_index) {
|
||||
cx.emit(SavedContextPickerEvent::Confirmed(metadata.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
|
||||
|
||||
fn render_match(
|
||||
&self,
|
||||
ix: usize,
|
||||
selected: bool,
|
||||
cx: &mut ViewContext<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let context = self.matches.get(ix)?;
|
||||
let item = match context {
|
||||
ContextMetadata::Remote(context) => {
|
||||
let host_user = self.project.read(cx).host().and_then(|collaborator| {
|
||||
self.project
|
||||
.read(cx)
|
||||
.user_store()
|
||||
.read(cx)
|
||||
.get_cached_user(collaborator.user_id)
|
||||
});
|
||||
div()
|
||||
.flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex().flex_1().overflow_x_hidden().child(
|
||||
Label::new(context.summary.clone().unwrap_or(DEFAULT_TAB_TITLE.into()))
|
||||
.size(LabelSize::Small),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.children(if let Some(host_user) = host_user {
|
||||
vec![
|
||||
Avatar::new(host_user.avatar_uri.clone()).into_any_element(),
|
||||
Label::new(format!("Shared by @{}", host_user.github_login))
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small)
|
||||
.into_any_element(),
|
||||
]
|
||||
} else {
|
||||
vec![Label::new("Shared by host")
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small)
|
||||
.into_any_element()]
|
||||
}),
|
||||
)
|
||||
}
|
||||
ContextMetadata::Saved(context) => div()
|
||||
.flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_1()
|
||||
.child(Label::new(context.title.clone()).size(LabelSize::Small))
|
||||
.overflow_x_hidden(),
|
||||
)
|
||||
.child(
|
||||
Label::new(format_distance_from_now(
|
||||
DateTimeType::Local(context.mtime),
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
))
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small),
|
||||
),
|
||||
};
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
.inset(true)
|
||||
.spacing(ListItemSpacing::Sparse)
|
||||
.toggle_state(selected)
|
||||
.child(item),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::assistant_panel::ContextEditor;
|
||||
use crate::context_editor::ContextEditor;
|
||||
use anyhow::Result;
|
||||
use assistant_slash_command::AfterCompletion;
|
||||
pub use assistant_slash_command::SlashCommand;
|
||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakView};
|
|||
use picker::{Picker, PickerDelegate, PickerEditorPosition};
|
||||
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger, Tooltip};
|
||||
|
||||
use crate::assistant_panel::ContextEditor;
|
||||
use crate::context_editor::ContextEditor;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub(super) struct SlashCommandSelector<T: PopoverTrigger> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue