Remove the nested Pane from the assistant

Since we don't want tabs, I think it would be better to render the toolbar
for ourselves directly and handle switching between conversations.

Co-Authored-By: Julia Risley <julia@zed.dev>
This commit is contained in:
Nathan Sobo 2023-06-20 18:51:37 -06:00
parent 7a051a0dcb
commit 23bc11f8b3
9 changed files with 189 additions and 177 deletions

1
Cargo.lock generated
View file

@ -109,6 +109,7 @@ dependencies = [
"isahc", "isahc",
"language", "language",
"menu", "menu",
"project",
"regex", "regex",
"schemars", "schemars",
"search", "search",

View file

@ -40,7 +40,8 @@
"cmd-o": "workspace::Open", "cmd-o": "workspace::Open",
"alt-cmd-o": "projects::OpenRecent", "alt-cmd-o": "projects::OpenRecent",
"ctrl-~": "workspace::NewTerminal", "ctrl-~": "workspace::NewTerminal",
"ctrl-`": "terminal_panel::ToggleFocus" "ctrl-`": "terminal_panel::ToggleFocus",
"shift-escape": "workspace::ToggleZoom"
} }
}, },
{ {
@ -235,8 +236,7 @@
"cmd-shift-g": "search::SelectPrevMatch", "cmd-shift-g": "search::SelectPrevMatch",
"alt-cmd-c": "search::ToggleCaseSensitive", "alt-cmd-c": "search::ToggleCaseSensitive",
"alt-cmd-w": "search::ToggleWholeWord", "alt-cmd-w": "search::ToggleWholeWord",
"alt-cmd-r": "search::ToggleRegex", "alt-cmd-r": "search::ToggleRegex"
"shift-escape": "workspace::ToggleZoom"
} }
}, },
// Bindings from VS Code // Bindings from VS Code

View file

@ -57,37 +57,37 @@
"show_whitespaces": "selection", "show_whitespaces": "selection",
// Scrollbar related settings // Scrollbar related settings
"scrollbar": { "scrollbar": {
// When to show the scrollbar in the editor. // When to show the scrollbar in the editor.
// This setting can take four values: // This setting can take four values:
// //
// 1. Show the scrollbar if there's important information or // 1. Show the scrollbar if there's important information or
// follow the system's configured behavior (default): // follow the system's configured behavior (default):
// "auto" // "auto"
// 2. Match the system's configured behavior: // 2. Match the system's configured behavior:
// "system" // "system"
// 3. Always show the scrollbar: // 3. Always show the scrollbar:
// "always" // "always"
// 4. Never show the scrollbar: // 4. Never show the scrollbar:
// "never" // "never"
"show": "auto", "show": "auto",
// Whether to show git diff indicators in the scrollbar. // Whether to show git diff indicators in the scrollbar.
"git_diff": true "git_diff": true
}, },
"project_panel": { "project_panel": {
// Whether to show the git status in the project panel. // Whether to show the git status in the project panel.
"git_status": true, "git_status": true,
// Where to dock project panel. Can be 'left' or 'right'. // Where to dock project panel. Can be 'left' or 'right'.
"dock": "left", "dock": "left",
// Default width of the project panel. // Default width of the project panel.
"default_width": 240 "default_width": 240
}, },
"assistant": { "assistant": {
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'. // Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
"dock": "right", "dock": "right",
// Default width when the assistant is docked to the left or right. // Default width when the assistant is docked to the left or right.
"default_width": 450, "default_width": 450,
// Default height when the assistant is docked to the bottom. // Default height when the assistant is docked to the bottom.
"default_height": 320 "default_height": 320
}, },
// Whether the screen sharing icon is shown in the os status bar. // Whether the screen sharing icon is shown in the os status bar.
"show_call_status_icon": true, "show_call_status_icon": true,

View file

@ -34,3 +34,4 @@ tiktoken-rs = "0.4"
[dev-dependencies] [dev-dependencies]
editor = { path = "../editor", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }

View file

@ -37,7 +37,7 @@ use util::{
use workspace::{ use workspace::{
dock::{DockPosition, Panel}, dock::{DockPosition, Panel},
item::Item, item::Item,
pane, Pane, Save, Workspace, Save, Workspace,
}; };
const OPENAI_API_URL: &'static str = "https://api.openai.com/v1"; const OPENAI_API_URL: &'static str = "https://api.openai.com/v1";
@ -66,21 +66,24 @@ pub fn init(cx: &mut AppContext) {
cx.add_action( cx.add_action(
|workspace: &mut Workspace, _: &NewContext, cx: &mut ViewContext<Workspace>| { |workspace: &mut Workspace, _: &NewContext, cx: &mut ViewContext<Workspace>| {
if let Some(this) = workspace.panel::<AssistantPanel>(cx) { if let Some(this) = workspace.panel::<AssistantPanel>(cx) {
this.update(cx, |this, cx| this.add_context(cx)) this.update(cx, |this, cx| {
this.add_conversation(cx);
})
} }
workspace.focus_panel::<AssistantPanel>(cx); workspace.focus_panel::<AssistantPanel>(cx);
}, },
); );
cx.add_action(AssistantEditor::assist); cx.add_action(ConversationEditor::assist);
cx.capture_action(AssistantEditor::cancel_last_assist); cx.capture_action(ConversationEditor::cancel_last_assist);
cx.capture_action(AssistantEditor::save); cx.capture_action(ConversationEditor::save);
cx.add_action(AssistantEditor::quote_selection); cx.add_action(ConversationEditor::quote_selection);
cx.capture_action(AssistantEditor::copy); cx.capture_action(ConversationEditor::copy);
cx.capture_action(AssistantEditor::split); cx.capture_action(ConversationEditor::split);
cx.capture_action(AssistantEditor::cycle_message_role); cx.capture_action(ConversationEditor::cycle_message_role);
cx.add_action(AssistantPanel::save_api_key); cx.add_action(AssistantPanel::save_api_key);
cx.add_action(AssistantPanel::reset_api_key); cx.add_action(AssistantPanel::reset_api_key);
cx.add_action(AssistantPanel::toggle_zoom);
cx.add_action( cx.add_action(
|workspace: &mut Workspace, _: &ToggleFocus, cx: &mut ViewContext<Workspace>| { |workspace: &mut Workspace, _: &ToggleFocus, cx: &mut ViewContext<Workspace>| {
workspace.toggle_panel_focus::<AssistantPanel>(cx); workspace.toggle_panel_focus::<AssistantPanel>(cx);
@ -88,6 +91,7 @@ pub fn init(cx: &mut AppContext) {
); );
} }
#[derive(Debug)]
pub enum AssistantPanelEvent { pub enum AssistantPanelEvent {
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
@ -99,14 +103,17 @@ pub enum AssistantPanelEvent {
pub struct AssistantPanel { pub struct AssistantPanel {
width: Option<f32>, width: Option<f32>,
height: Option<f32>, height: Option<f32>,
pane: ViewHandle<Pane>, active_conversation_index: usize,
conversation_editors: Vec<ViewHandle<ConversationEditor>>,
saved_conversations: Vec<SavedConversationMetadata>,
zoomed: bool,
has_focus: bool,
api_key: Rc<RefCell<Option<String>>>, api_key: Rc<RefCell<Option<String>>>,
api_key_editor: Option<ViewHandle<Editor>>, api_key_editor: Option<ViewHandle<Editor>>,
has_read_credentials: bool, has_read_credentials: bool,
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
subscriptions: Vec<Subscription>, subscriptions: Vec<Subscription>,
saved_conversations: Vec<SavedConversationMetadata>,
_watch_saved_conversations: Task<Result<()>>, _watch_saved_conversations: Task<Result<()>>,
} }
@ -125,61 +132,6 @@ impl AssistantPanel {
// TODO: deserialize state. // TODO: deserialize state.
workspace.update(&mut cx, |workspace, cx| { workspace.update(&mut cx, |workspace, cx| {
cx.add_view::<Self, _>(|cx| { cx.add_view::<Self, _>(|cx| {
let weak_self = cx.weak_handle();
let pane = cx.add_view(|cx| {
let mut pane = Pane::new(
workspace.weak_handle(),
workspace.project().clone(),
workspace.app_state().background_actions,
Default::default(),
cx,
);
pane.set_can_split(false, cx);
pane.set_can_navigate(false, cx);
pane.on_can_drop(move |_, _| false);
pane.set_render_tab_bar_buttons(cx, move |pane, cx| {
let weak_self = weak_self.clone();
Flex::row()
.with_child(Pane::render_tab_bar_button(
0,
"icons/plus_12.svg",
false,
Some(("New Context".into(), Some(Box::new(NewContext)))),
cx,
move |_, cx| {
let weak_self = weak_self.clone();
cx.window_context().defer(move |cx| {
if let Some(this) = weak_self.upgrade(cx) {
this.update(cx, |this, cx| this.add_context(cx));
}
})
},
None,
))
.with_child(Pane::render_tab_bar_button(
1,
if pane.is_zoomed() {
"icons/minimize_8.svg"
} else {
"icons/maximize_8.svg"
},
pane.is_zoomed(),
Some((
"Toggle Zoom".into(),
Some(Box::new(workspace::ToggleZoom)),
)),
cx,
move |pane, cx| pane.toggle_zoom(&Default::default(), cx),
None,
))
.into_any()
});
let buffer_search_bar = cx.add_view(search::BufferSearchBar::new);
pane.toolbar()
.update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));
pane
});
const CONVERSATION_WATCH_DURATION: Duration = Duration::from_millis(100); const CONVERSATION_WATCH_DURATION: Duration = Duration::from_millis(100);
let _watch_saved_conversations = cx.spawn(move |this, mut cx| async move { let _watch_saved_conversations = cx.spawn(move |this, mut cx| async move {
let mut events = fs let mut events = fs
@ -200,7 +152,11 @@ impl AssistantPanel {
}); });
let mut this = Self { let mut this = Self {
pane, active_conversation_index: 0,
conversation_editors: Default::default(),
saved_conversations,
zoomed: false,
has_focus: false,
api_key: Rc::new(RefCell::new(None)), api_key: Rc::new(RefCell::new(None)),
api_key_editor: None, api_key_editor: None,
has_read_credentials: false, has_read_credentials: false,
@ -209,22 +165,18 @@ impl AssistantPanel {
width: None, width: None,
height: None, height: None,
subscriptions: Default::default(), subscriptions: Default::default(),
saved_conversations,
_watch_saved_conversations, _watch_saved_conversations,
}; };
let mut old_dock_position = this.position(cx); let mut old_dock_position = this.position(cx);
this.subscriptions = vec![ this.subscriptions =
cx.observe(&this.pane, |_, _, cx| cx.notify()), vec![cx.observe_global::<SettingsStore, _>(move |this, cx| {
cx.subscribe(&this.pane, Self::handle_pane_event),
cx.observe_global::<SettingsStore, _>(move |this, cx| {
let new_dock_position = this.position(cx); let new_dock_position = this.position(cx);
if new_dock_position != old_dock_position { if new_dock_position != old_dock_position {
old_dock_position = new_dock_position; old_dock_position = new_dock_position;
cx.emit(AssistantPanelEvent::DockPositionChanged); cx.emit(AssistantPanelEvent::DockPositionChanged);
} }
}), })];
];
this this
}) })
@ -232,25 +184,14 @@ impl AssistantPanel {
}) })
} }
fn handle_pane_event( fn add_conversation(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<ConversationEditor> {
&mut self,
_pane: ViewHandle<Pane>,
event: &pane::Event,
cx: &mut ViewContext<Self>,
) {
match event {
pane::Event::ZoomIn => cx.emit(AssistantPanelEvent::ZoomIn),
pane::Event::ZoomOut => cx.emit(AssistantPanelEvent::ZoomOut),
pane::Event::Focus => cx.emit(AssistantPanelEvent::Focus),
pane::Event::Remove => cx.emit(AssistantPanelEvent::Close),
_ => {}
}
}
fn add_context(&mut self, cx: &mut ViewContext<Self>) {
let focus = self.has_focus(cx); let focus = self.has_focus(cx);
let editor = cx.add_view(|cx| { let editor = cx.add_view(|cx| {
AssistantEditor::new( if focus {
cx.focus_self();
}
ConversationEditor::new(
self.api_key.clone(), self.api_key.clone(),
self.languages.clone(), self.languages.clone(),
self.fs.clone(), self.fs.clone(),
@ -258,20 +199,23 @@ impl AssistantPanel {
) )
}); });
self.subscriptions self.subscriptions
.push(cx.subscribe(&editor, Self::handle_assistant_editor_event)); .push(cx.subscribe(&editor, Self::handle_conversation_editor_event));
self.pane.update(cx, |pane, cx| {
pane.add_item(Box::new(editor), true, focus, None, cx) self.active_conversation_index = self.conversation_editors.len();
}); self.conversation_editors.push(editor.clone());
cx.notify();
editor
} }
fn handle_assistant_editor_event( fn handle_conversation_editor_event(
&mut self, &mut self,
_: ViewHandle<AssistantEditor>, _: ViewHandle<ConversationEditor>,
event: &AssistantEditorEvent, event: &AssistantEditorEvent,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
match event { match event {
AssistantEditorEvent::TabContentChanged => self.pane.update(cx, |_, cx| cx.notify()), AssistantEditorEvent::TabContentChanged => cx.notify(),
} }
} }
@ -302,6 +246,19 @@ impl AssistantPanel {
cx.focus_self(); cx.focus_self();
cx.notify(); cx.notify();
} }
fn toggle_zoom(&mut self, _: &workspace::ToggleZoom, cx: &mut ViewContext<Self>) {
if self.zoomed {
cx.emit(AssistantPanelEvent::ZoomOut)
} else {
cx.emit(AssistantPanelEvent::ZoomIn)
}
}
fn active_conversation_editor(&self) -> Option<&ViewHandle<ConversationEditor>> {
self.conversation_editors
.get(self.active_conversation_index)
}
} }
fn build_api_key_editor(cx: &mut ViewContext<AssistantPanel>) -> ViewHandle<Editor> { fn build_api_key_editor(cx: &mut ViewContext<AssistantPanel>) -> ViewHandle<Editor> {
@ -345,20 +302,27 @@ impl View for AssistantPanel {
.with_style(style.api_key_prompt.container) .with_style(style.api_key_prompt.container)
.aligned() .aligned()
.into_any() .into_any()
} else if let Some(editor) = self.active_conversation_editor() {
ChildView::new(editor, cx).into_any()
} else { } else {
ChildView::new(&self.pane, cx).into_any() Empty::new().into_any()
} }
} }
fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) { fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
self.has_focus = true;
if cx.is_self_focused() { if cx.is_self_focused() {
if let Some(api_key_editor) = self.api_key_editor.as_ref() { if let Some(editor) = self.active_conversation_editor() {
cx.focus(editor);
} else if let Some(api_key_editor) = self.api_key_editor.as_ref() {
cx.focus(api_key_editor); cx.focus(api_key_editor);
} else {
cx.focus(&self.pane);
} }
} }
} }
fn focus_out(&mut self, _: gpui::AnyViewHandle, _: &mut ViewContext<Self>) {
self.has_focus = false;
}
} }
impl Panel for AssistantPanel { impl Panel for AssistantPanel {
@ -411,12 +375,13 @@ impl Panel for AssistantPanel {
matches!(event, AssistantPanelEvent::ZoomOut) matches!(event, AssistantPanelEvent::ZoomOut)
} }
fn is_zoomed(&self, cx: &WindowContext) -> bool { fn is_zoomed(&self, _: &WindowContext) -> bool {
self.pane.read(cx).is_zoomed() self.zoomed
} }
fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) { fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) {
self.pane.update(cx, |pane, cx| pane.set_zoomed(zoomed, cx)); self.zoomed = zoomed;
cx.notify();
} }
fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) { fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
@ -443,8 +408,8 @@ impl Panel for AssistantPanel {
} }
} }
if self.pane.read(cx).items_len() == 0 { if self.conversation_editors.is_empty() {
self.add_context(cx); self.add_conversation(cx);
} }
} }
} }
@ -469,12 +434,8 @@ impl Panel for AssistantPanel {
matches!(event, AssistantPanelEvent::Close) matches!(event, AssistantPanelEvent::Close)
} }
fn has_focus(&self, cx: &WindowContext) -> bool { fn has_focus(&self, _: &WindowContext) -> bool {
self.pane.read(cx).has_focus() self.has_focus
|| self
.api_key_editor
.as_ref()
.map_or(false, |editor| editor.is_focused(cx))
} }
fn is_focus_event(event: &Self::Event) -> bool { fn is_focus_event(event: &Self::Event) -> bool {
@ -494,7 +455,7 @@ struct Summary {
done: bool, done: bool,
} }
struct Assistant { struct Conversation {
buffer: ModelHandle<Buffer>, buffer: ModelHandle<Buffer>,
message_anchors: Vec<MessageAnchor>, message_anchors: Vec<MessageAnchor>,
messages_metadata: HashMap<MessageId, MessageMetadata>, messages_metadata: HashMap<MessageId, MessageMetadata>,
@ -513,11 +474,11 @@ struct Assistant {
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
} }
impl Entity for Assistant { impl Entity for Conversation {
type Event = AssistantEvent; type Event = AssistantEvent;
} }
impl Assistant { impl Conversation {
fn new( fn new(
api_key: Rc<RefCell<Option<String>>>, api_key: Rc<RefCell<Option<String>>>,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
@ -1080,7 +1041,7 @@ impl Assistant {
&mut self, &mut self,
debounce: Option<Duration>, debounce: Option<Duration>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
cx: &mut ModelContext<Assistant>, cx: &mut ModelContext<Conversation>,
) { ) {
self.pending_save = cx.spawn(|this, mut cx| async move { self.pending_save = cx.spawn(|this, mut cx| async move {
if let Some(debounce) = debounce { if let Some(debounce) = debounce {
@ -1158,8 +1119,8 @@ struct ScrollPosition {
cursor: Anchor, cursor: Anchor,
} }
struct AssistantEditor { struct ConversationEditor {
assistant: ModelHandle<Assistant>, assistant: ModelHandle<Conversation>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
editor: ViewHandle<Editor>, editor: ViewHandle<Editor>,
blocks: HashSet<BlockId>, blocks: HashSet<BlockId>,
@ -1167,14 +1128,14 @@ struct AssistantEditor {
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
} }
impl AssistantEditor { impl ConversationEditor {
fn new( fn new(
api_key: Rc<RefCell<Option<String>>>, api_key: Rc<RefCell<Option<String>>>,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Self { ) -> Self {
let assistant = cx.add_model(|cx| Assistant::new(api_key, language_registry, cx)); let assistant = cx.add_model(|cx| Conversation::new(api_key, language_registry, cx));
let editor = cx.add_view(|cx| { let editor = cx.add_view(|cx| {
let mut editor = Editor::for_buffer(assistant.read(cx).buffer.clone(), None, cx); let mut editor = Editor::for_buffer(assistant.read(cx).buffer.clone(), None, cx);
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx); editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
@ -1262,7 +1223,7 @@ impl AssistantEditor {
fn handle_assistant_event( fn handle_assistant_event(
&mut self, &mut self,
_: ModelHandle<Assistant>, _: ModelHandle<Conversation>,
event: &AssistantEvent, event: &AssistantEvent,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
@ -1501,20 +1462,15 @@ impl AssistantEditor {
if let Some(text) = text { if let Some(text) = text {
panel.update(cx, |panel, cx| { panel.update(cx, |panel, cx| {
if let Some(assistant) = panel let editor = panel
.pane .active_conversation_editor()
.read(cx) .cloned()
.active_item() .unwrap_or_else(|| panel.add_conversation(cx));
.and_then(|item| item.downcast::<AssistantEditor>()) editor.update(cx, |assistant, cx| {
.ok_or_else(|| anyhow!("no active context")) assistant
.log_err() .editor
{ .update(cx, |editor, cx| editor.insert(&text, cx))
assistant.update(cx, |assistant, cx| { });
assistant
.editor
.update(cx, |editor, cx| editor.insert(&text, cx))
});
}
}); });
} }
} }
@ -1592,11 +1548,11 @@ impl AssistantEditor {
} }
} }
impl Entity for AssistantEditor { impl Entity for ConversationEditor {
type Event = AssistantEditorEvent; type Event = AssistantEditorEvent;
} }
impl View for AssistantEditor { impl View for ConversationEditor {
fn ui_name() -> &'static str { fn ui_name() -> &'static str {
"AssistantEditor" "AssistantEditor"
} }
@ -1655,7 +1611,7 @@ impl View for AssistantEditor {
} }
} }
impl Item for AssistantEditor { impl Item for ConversationEditor {
fn tab_content<V: View>( fn tab_content<V: View>(
&self, &self,
_: Option<usize>, _: Option<usize>,
@ -1812,12 +1768,55 @@ async fn stream_completion(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use gpui::AppContext; use fs::FakeFs;
use gpui::{AppContext, TestAppContext};
use project::Project;
fn init_test(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
cx.update(|cx| {
cx.set_global(SettingsStore::test(cx));
theme::init((), cx);
language::init(cx);
editor::init_settings(cx);
crate::init(cx);
workspace::init_settings(cx);
Project::init_settings(cx);
});
}
#[gpui::test]
async fn test_panel(cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.background());
let project = Project::test(fs, [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let weak_workspace = workspace.downgrade();
let panel = cx
.spawn(|cx| async move { AssistantPanel::load(weak_workspace, cx).await })
.await
.unwrap();
workspace.update(cx, |workspace, cx| {
workspace.add_panel(panel.clone(), cx);
workspace.toggle_dock(DockPosition::Right, cx);
assert!(workspace.right_dock().read(cx).is_open());
cx.focus(&panel);
});
cx.dispatch_action(window_id, workspace::ToggleZoom);
workspace.read_with(cx, |workspace, cx| {
assert_eq!(workspace.zoomed_view(cx).unwrap(), panel);
})
}
#[gpui::test] #[gpui::test]
fn test_inserting_and_removing_messages(cx: &mut AppContext) { fn test_inserting_and_removing_messages(cx: &mut AppContext) {
let registry = Arc::new(LanguageRegistry::test()); let registry = Arc::new(LanguageRegistry::test());
let assistant = cx.add_model(|cx| Assistant::new(Default::default(), registry, cx)); let assistant = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
let buffer = assistant.read(cx).buffer.clone(); let buffer = assistant.read(cx).buffer.clone();
let message_1 = assistant.read(cx).message_anchors[0].clone(); let message_1 = assistant.read(cx).message_anchors[0].clone();
@ -1943,7 +1942,7 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_message_splitting(cx: &mut AppContext) { fn test_message_splitting(cx: &mut AppContext) {
let registry = Arc::new(LanguageRegistry::test()); let registry = Arc::new(LanguageRegistry::test());
let assistant = cx.add_model(|cx| Assistant::new(Default::default(), registry, cx)); let assistant = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
let buffer = assistant.read(cx).buffer.clone(); let buffer = assistant.read(cx).buffer.clone();
let message_1 = assistant.read(cx).message_anchors[0].clone(); let message_1 = assistant.read(cx).message_anchors[0].clone();
@ -2036,7 +2035,7 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_messages_for_offsets(cx: &mut AppContext) { fn test_messages_for_offsets(cx: &mut AppContext) {
let registry = Arc::new(LanguageRegistry::test()); let registry = Arc::new(LanguageRegistry::test());
let assistant = cx.add_model(|cx| Assistant::new(Default::default(), registry, cx)); let assistant = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
let buffer = assistant.read(cx).buffer.clone(); let buffer = assistant.read(cx).buffer.clone();
let message_1 = assistant.read(cx).message_anchors[0].clone(); let message_1 = assistant.read(cx).message_anchors[0].clone();
@ -2080,7 +2079,7 @@ mod tests {
); );
fn message_ids_for_offsets( fn message_ids_for_offsets(
assistant: &ModelHandle<Assistant>, assistant: &ModelHandle<Conversation>,
offsets: &[usize], offsets: &[usize],
cx: &AppContext, cx: &AppContext,
) -> Vec<MessageId> { ) -> Vec<MessageId> {
@ -2094,7 +2093,7 @@ mod tests {
} }
fn messages( fn messages(
assistant: &ModelHandle<Assistant>, assistant: &ModelHandle<Conversation>,
cx: &AppContext, cx: &AppContext,
) -> Vec<(MessageId, Role, Range<usize>)> { ) -> Vec<(MessageId, Role, Range<usize>)> {
assistant assistant

View file

@ -153,6 +153,7 @@ pub fn init(cx: &mut AppContext) {
); );
} }
#[derive(Debug)]
pub enum Event { pub enum Event {
OpenedEntry { OpenedEntry {
entry_id: ProjectEntryId, entry_id: ProjectEntryId,

View file

@ -25,6 +25,7 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(TerminalPanel::new_terminal); cx.add_action(TerminalPanel::new_terminal);
} }
#[derive(Debug)]
pub enum Event { pub enum Event {
Close, Close,
DockPositionChanged, DockPositionChanged,

View file

@ -249,7 +249,7 @@ impl Dock {
} }
} }
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) { pub(crate) fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) {
let subscriptions = [ let subscriptions = [
cx.observe(&panel, |_, _, cx| cx.notify()), cx.observe(&panel, |_, _, cx| cx.notify()),
cx.subscribe(&panel, |this, panel, event, cx| { cx.subscribe(&panel, |this, panel, event, cx| {
@ -603,6 +603,7 @@ pub mod test {
use super::*; use super::*;
use gpui::{ViewContext, WindowContext}; use gpui::{ViewContext, WindowContext};
#[derive(Debug)]
pub enum TestPanelEvent { pub enum TestPanelEvent {
PositionChanged, PositionChanged,
Activated, Activated,

View file

@ -859,7 +859,10 @@ impl Workspace {
&self.right_dock &self.right_dock
} }
pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>) { pub fn add_panel<T: Panel>(&mut self, panel: ViewHandle<T>, cx: &mut ViewContext<Self>)
where
T::Event: std::fmt::Debug,
{
let dock = match panel.position(cx) { let dock = match panel.position(cx) {
DockPosition::Left => &self.left_dock, DockPosition::Left => &self.left_dock,
DockPosition::Bottom => &self.bottom_dock, DockPosition::Bottom => &self.bottom_dock,
@ -1700,6 +1703,11 @@ impl Workspace {
cx.notify(); cx.notify();
} }
#[cfg(any(test, feature = "test-support"))]
pub fn zoomed_view(&self, cx: &AppContext) -> Option<AnyViewHandle> {
self.zoomed.and_then(|view| view.upgrade(cx))
}
fn dismiss_zoomed_items_to_reveal( fn dismiss_zoomed_items_to_reveal(
&mut self, &mut self,
dock_to_reveal: Option<DockPosition>, dock_to_reveal: Option<DockPosition>,