mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 22:42:17 +03:00
WIP dock split button and default item
This commit is contained in:
parent
d87fb20170
commit
b88abcacac
@ -298,7 +298,8 @@ async fn test_host_disconnect(
|
|||||||
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
|
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
|
||||||
assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
|
assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
|
||||||
|
|
||||||
let (_, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
|
let (_, workspace_b) =
|
||||||
|
cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
|
||||||
let editor_b = workspace_b
|
let editor_b = workspace_b
|
||||||
.update(cx_b, |workspace, cx| {
|
.update(cx_b, |workspace, cx| {
|
||||||
workspace.open_path((worktree_id, "b.txt"), true, cx)
|
workspace.open_path((worktree_id, "b.txt"), true, cx)
|
||||||
@ -2786,7 +2787,8 @@ async fn test_collaborating_with_code_actions(
|
|||||||
|
|
||||||
// Join the project as client B.
|
// Join the project as client B.
|
||||||
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
|
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
|
||||||
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
|
let (_window_b, workspace_b) =
|
||||||
|
cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
|
||||||
let editor_b = workspace_b
|
let editor_b = workspace_b
|
||||||
.update(cx_b, |workspace, cx| {
|
.update(cx_b, |workspace, cx| {
|
||||||
workspace.open_path((worktree_id, "main.rs"), true, cx)
|
workspace.open_path((worktree_id, "main.rs"), true, cx)
|
||||||
@ -3001,7 +3003,8 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
|
|||||||
let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
|
let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
|
||||||
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
|
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
|
||||||
|
|
||||||
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
|
let (_window_b, workspace_b) =
|
||||||
|
cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
|
||||||
let editor_b = workspace_b
|
let editor_b = workspace_b
|
||||||
.update(cx_b, |workspace, cx| {
|
.update(cx_b, |workspace, cx| {
|
||||||
workspace.open_path((worktree_id, "one.rs"), true, cx)
|
workspace.open_path((worktree_id, "one.rs"), true, cx)
|
||||||
@ -5224,6 +5227,7 @@ impl TestServer {
|
|||||||
fs: fs.clone(),
|
fs: fs.clone(),
|
||||||
build_window_options: Default::default,
|
build_window_options: Default::default,
|
||||||
initialize_workspace: |_, _, _| unimplemented!(),
|
initialize_workspace: |_, _, _| unimplemented!(),
|
||||||
|
default_item_factory: |_, _| unimplemented!(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Channel::init(&client);
|
Channel::init(&client);
|
||||||
@ -5459,7 +5463,9 @@ impl TestClient {
|
|||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
) -> ViewHandle<Workspace> {
|
) -> ViewHandle<Workspace> {
|
||||||
let (_, root_view) = cx.add_window(|_| EmptyView);
|
let (_, root_view) = cx.add_window(|_| EmptyView);
|
||||||
cx.add_view(&root_view, |cx| Workspace::new(project.clone(), cx))
|
cx.add_view(&root_view, |cx| {
|
||||||
|
Workspace::new(project.clone(), |_, _| unimplemented!(), cx)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn simulate_host(
|
async fn simulate_host(
|
||||||
|
@ -350,7 +350,8 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
let editor = cx.add_view(&workspace, |cx| {
|
let editor = cx.add_view(&workspace, |cx| {
|
||||||
let mut editor = Editor::single_line(None, cx);
|
let mut editor = Editor::single_line(None, cx);
|
||||||
editor.set_text("abc", cx);
|
editor.set_text("abc", cx);
|
||||||
|
@ -1247,7 +1247,8 @@ mod tests {
|
|||||||
.0
|
.0
|
||||||
.read_with(cx, |worktree, _| worktree.id().to_proto());
|
.read_with(cx, |worktree, _| worktree.id().to_proto());
|
||||||
|
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
let panel = cx.add_view(&workspace, |cx| {
|
let panel = cx.add_view(&workspace, |cx| {
|
||||||
ContactsPanel::new(
|
ContactsPanel::new(
|
||||||
user_store.clone(),
|
user_store.clone(),
|
||||||
|
@ -776,7 +776,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
// Create some diagnostics
|
// Create some diagnostics
|
||||||
project.update(cx, |project, cx| {
|
project.update(cx, |project, cx| {
|
||||||
|
@ -7100,7 +7100,7 @@ mod tests {
|
|||||||
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
|
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
|
||||||
cx.set_global(Settings::test(cx));
|
cx.set_global(Settings::test(cx));
|
||||||
use workspace::Item;
|
use workspace::Item;
|
||||||
let (_, pane) = cx.add_window(Default::default(), Pane::new);
|
let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(false, cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
||||||
|
|
||||||
cx.add_view(&pane, |cx| {
|
cx.add_view(&pane, |cx| {
|
||||||
|
@ -364,7 +364,8 @@ impl<'a> EditorLspTestContext<'a> {
|
|||||||
.insert_tree("/root", json!({ "dir": { file_name: "" }}))
|
.insert_tree("/root", json!({ "dir": { file_name: "" }}))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
project
|
project
|
||||||
.update(cx, |project, cx| {
|
.update(cx, |project, cx| {
|
||||||
project.find_or_create_local_worktree("/root", true, cx)
|
project.find_or_create_local_worktree("/root", true, cx)
|
||||||
|
@ -316,7 +316,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
cx.dispatch_action(window_id, Toggle);
|
cx.dispatch_action(window_id, Toggle);
|
||||||
|
|
||||||
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
|
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
|
||||||
@ -370,7 +371,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
let (_, finder) =
|
let (_, finder) =
|
||||||
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
||||||
|
|
||||||
@ -444,7 +446,8 @@ mod tests {
|
|||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
let (_, finder) =
|
let (_, finder) =
|
||||||
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
||||||
finder
|
finder
|
||||||
@ -468,7 +471,8 @@ mod tests {
|
|||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
let (_, finder) =
|
let (_, finder) =
|
||||||
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
||||||
|
|
||||||
@ -520,7 +524,8 @@ mod tests {
|
|||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
let (_, finder) =
|
let (_, finder) =
|
||||||
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
||||||
|
|
||||||
@ -558,7 +563,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
let (_, finder) =
|
let (_, finder) =
|
||||||
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
|
||||||
finder
|
finder
|
||||||
|
@ -1243,7 +1243,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
|
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
|
let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
visible_entries_as_strings(&panel, 0..50, cx),
|
visible_entries_as_strings(&panel, 0..50, cx),
|
||||||
@ -1335,7 +1336,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
|
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
|
let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
|
||||||
|
|
||||||
select_path(&panel, "root1", cx);
|
select_path(&panel, "root1", cx);
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
use gpui::{ModelHandle, ViewContext};
|
use gpui::{ModelHandle, ViewContext};
|
||||||
use settings::{Settings, WorkingDirectory};
|
use workspace::Workspace;
|
||||||
use workspace::{dock::Dock, Workspace};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{terminal_container_view::DeployModal, Event, Terminal};
|
||||||
terminal_container_view::{
|
|
||||||
get_working_directory, DeployModal, TerminalContainer, TerminalContainerContent,
|
|
||||||
},
|
|
||||||
Event, Terminal,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewContext<Workspace>) {
|
pub fn deploy_modal(_workspace: &mut Workspace, _: &DeployModal, _cx: &mut ViewContext<Workspace>) {
|
||||||
// let window = cx.window_id();
|
// let window = cx.window_id();
|
||||||
|
|
||||||
// // Pull the terminal connection out of the global if it has been stored
|
// // Pull the terminal connection out of the global if it has been stored
|
||||||
@ -62,10 +56,10 @@ pub fn deploy_modal(workspace: &mut Workspace, _: &DeployModal, cx: &mut ViewCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_event(
|
pub fn on_event(
|
||||||
workspace: &mut Workspace,
|
_workspace: &mut Workspace,
|
||||||
_: ModelHandle<Terminal>,
|
_: ModelHandle<Terminal>,
|
||||||
event: &Event,
|
_event: &Event,
|
||||||
cx: &mut ViewContext<Workspace>,
|
_cx: &mut ViewContext<Workspace>,
|
||||||
) {
|
) {
|
||||||
// Dismiss the modal if the terminal quit
|
// Dismiss the modal if the terminal quit
|
||||||
// if let Event::CloseTerminal = event {
|
// if let Event::CloseTerminal = event {
|
||||||
|
@ -21,7 +21,9 @@ impl<'a> TerminalTestContext<'a> {
|
|||||||
let params = self.cx.update(AppState::test);
|
let params = self.cx.update(AppState::test);
|
||||||
|
|
||||||
let project = Project::test(params.fs.clone(), [], self.cx).await;
|
let project = Project::test(params.fs.clone(), [], self.cx).await;
|
||||||
let (_, workspace) = self.cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) = self
|
||||||
|
.cx
|
||||||
|
.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
(project, workspace)
|
(project, workspace)
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,8 @@ impl<'a> VimTestContext<'a> {
|
|||||||
.insert_tree("/root", json!({ "dir": { "test.txt": "" } }))
|
.insert_tree("/root", json!({ "dir": { "test.txt": "" } }))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
// Setup search toolbars
|
// Setup search toolbars
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
@ -1,59 +1,117 @@
|
|||||||
use std::sync::Arc;
|
use gpui::{
|
||||||
|
actions,
|
||||||
use gpui::{elements::ChildView, Element, ElementBox, Entity, View, ViewContext, ViewHandle};
|
elements::{ChildView, MouseEventHandler, Svg},
|
||||||
|
impl_internal_actions, CursorStyle, Element, ElementBox, Entity, MouseButton,
|
||||||
|
MutableAppContext, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use settings::Settings;
|
||||||
use theme::Theme;
|
use theme::Theme;
|
||||||
|
|
||||||
use crate::{Pane, StatusItemView, Workspace};
|
use crate::{pane, ItemHandle, Pane, StatusItemView, Workspace};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Default, Copy, Clone)]
|
#[derive(PartialEq, Clone, Deserialize)]
|
||||||
pub enum DockPosition {
|
pub struct MoveDock(pub DockAnchor);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
pub struct AddDefaultItemToDock;
|
||||||
|
|
||||||
|
actions!(workspace, [ToggleDock]);
|
||||||
|
impl_internal_actions!(workspace, [MoveDock, AddDefaultItemToDock]);
|
||||||
|
|
||||||
|
pub fn init(cx: &mut MutableAppContext) {
|
||||||
|
cx.add_action(Dock::toggle);
|
||||||
|
cx.add_action(Dock::move_dock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Default, Copy, Clone, Deserialize)]
|
||||||
|
pub enum DockAnchor {
|
||||||
#[default]
|
#[default]
|
||||||
Bottom,
|
Bottom,
|
||||||
Right,
|
Right,
|
||||||
Fullscreen,
|
Expanded,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum DockPosition {
|
||||||
|
Shown(DockAnchor),
|
||||||
|
Hidden(DockAnchor),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DockPosition {
|
||||||
|
fn default() -> Self {
|
||||||
|
DockPosition::Hidden(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DockPosition {
|
||||||
|
fn toggle(self) -> Self {
|
||||||
|
match self {
|
||||||
|
DockPosition::Shown(anchor) => DockPosition::Hidden(anchor),
|
||||||
|
DockPosition::Hidden(anchor) => DockPosition::Shown(anchor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visible(&self) -> Option<DockAnchor> {
|
||||||
|
match self {
|
||||||
|
DockPosition::Shown(anchor) => Some(*anchor),
|
||||||
|
DockPosition::Hidden(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide(self) -> Self {
|
||||||
|
match self {
|
||||||
|
DockPosition::Shown(anchor) => DockPosition::Hidden(anchor),
|
||||||
|
DockPosition::Hidden(_) => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type DefaultItemFactory =
|
||||||
|
fn(&mut Workspace, &mut ViewContext<Workspace>) -> Box<dyn ItemHandle>;
|
||||||
|
|
||||||
pub struct Dock {
|
pub struct Dock {
|
||||||
position: Option<DockPosition>,
|
position: DockPosition,
|
||||||
pane: ViewHandle<Pane>,
|
pane: ViewHandle<Pane>,
|
||||||
|
default_item_factory: DefaultItemFactory,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dock {
|
impl Dock {
|
||||||
pub fn new(cx: &mut ViewContext<Workspace>) -> Self {
|
pub fn new(cx: &mut ViewContext<Workspace>, default_item_factory: DefaultItemFactory) -> Self {
|
||||||
let pane = cx.add_view(Pane::new);
|
let pane = cx.add_view(|cx| Pane::new(true, cx));
|
||||||
|
|
||||||
|
cx.subscribe(&pane.clone(), |workspace, _, event, cx| {
|
||||||
|
if let pane::Event::Remove = event {
|
||||||
|
workspace.dock.hide();
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pane,
|
pane,
|
||||||
position: None,
|
position: Default::default(),
|
||||||
|
default_item_factory,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, _theme: &Theme, position: DockPosition) -> Option<ElementBox> {
|
pub fn pane(&self) -> ViewHandle<Pane> {
|
||||||
if self.position.is_some() && self.position.unwrap() == position {
|
self.pane.clone()
|
||||||
Some(ChildView::new(self.pane.clone()).boxed())
|
}
|
||||||
} else {
|
|
||||||
None
|
fn hide(&mut self) {
|
||||||
|
self.position = self.position.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_not_empty(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
||||||
|
let pane = workspace.dock.pane.clone();
|
||||||
|
if !pane.read(cx).items().next().is_none() {
|
||||||
|
let item_to_add = (workspace.dock.default_item_factory)(workspace, cx);
|
||||||
|
Pane::add_item(workspace, &pane, item_to_add, true, true, None, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ToggleDock {
|
fn toggle(workspace: &mut Workspace, _: &ToggleDock, cx: &mut ViewContext<Workspace>) {
|
||||||
dock: Arc<Dock>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToggleDock {
|
|
||||||
pub fn new(dock: Arc<Dock>, _cx: &mut ViewContext<Self>) -> Self {
|
|
||||||
Self { dock }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entity for ToggleDock {
|
|
||||||
type Event = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for ToggleDock {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"Dock Toggle"
|
|
||||||
}
|
|
||||||
// Shift-escape ON
|
// Shift-escape ON
|
||||||
// Get or insert the dock's last focused terminal
|
// Get or insert the dock's last focused terminal
|
||||||
// Open the dock in fullscreen
|
// Open the dock in fullscreen
|
||||||
@ -68,16 +126,85 @@ impl View for ToggleDock {
|
|||||||
// If the dock is hidden, show it
|
// If the dock is hidden, show it
|
||||||
// If the dock was full screen, open it in last position (bottom or right)
|
// If the dock was full screen, open it in last position (bottom or right)
|
||||||
// If the dock was bottom or right, re-open it in that context (and with the previous % width)
|
// If the dock was bottom or right, re-open it in that context (and with the previous % width)
|
||||||
// On hover, change color and background
|
|
||||||
// On shown, change color and background
|
workspace.dock.position = workspace.dock.position.toggle();
|
||||||
// On hidden, change color and background
|
if workspace.dock.position.visible().is_some() {
|
||||||
// Show tool tip
|
Self::ensure_not_empty(workspace, cx);
|
||||||
fn render(&mut self, _cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
|
}
|
||||||
todo!()
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_dock(
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
&MoveDock(new_anchor): &MoveDock,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) {
|
||||||
|
// Clear the previous position if the dock is not visible.
|
||||||
|
workspace.dock.position = DockPosition::Shown(new_anchor);
|
||||||
|
Self::ensure_not_empty(workspace, cx);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, _theme: &Theme, anchor: DockAnchor) -> Option<ElementBox> {
|
||||||
|
self.position
|
||||||
|
.visible()
|
||||||
|
.filter(|current_anchor| *current_anchor == anchor)
|
||||||
|
.map(|_| ChildView::new(self.pane.clone()).boxed())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatusItemView for ToggleDock {
|
pub struct ToggleDockButton {
|
||||||
|
workspace: WeakViewHandle<Workspace>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToggleDockButton {
|
||||||
|
pub fn new(workspace: WeakViewHandle<Workspace>, _cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
Self { workspace }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for ToggleDockButton {
|
||||||
|
type Event = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for ToggleDockButton {
|
||||||
|
fn ui_name() -> &'static str {
|
||||||
|
"Dock Toggle"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
|
||||||
|
let dock_is_open = self
|
||||||
|
.workspace
|
||||||
|
.upgrade(cx)
|
||||||
|
.map(|workspace| workspace.read(cx).dock.position.visible().is_some())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
MouseEventHandler::new::<Self, _, _>(0, cx, |state, cx| {
|
||||||
|
let theme = &cx
|
||||||
|
.global::<Settings>()
|
||||||
|
.theme
|
||||||
|
.workspace
|
||||||
|
.status_bar
|
||||||
|
.sidebar_buttons;
|
||||||
|
let style = theme.item.style_for(state, dock_is_open);
|
||||||
|
|
||||||
|
Svg::new("icons/terminal_16.svg")
|
||||||
|
.with_color(style.icon_color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(style.icon_size)
|
||||||
|
.with_height(style.icon_size)
|
||||||
|
.contained()
|
||||||
|
.with_style(style.container)
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
|
.on_click(MouseButton::Left, |_, cx| cx.dispatch_action(ToggleDock))
|
||||||
|
// TODO: Add tooltip
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusItemView for ToggleDockButton {
|
||||||
fn set_active_pane_item(
|
fn set_active_pane_item(
|
||||||
&mut self,
|
&mut self,
|
||||||
_active_pane_item: Option<&dyn crate::ItemHandle>,
|
_active_pane_item: Option<&dyn crate::ItemHandle>,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
use super::{ItemHandle, SplitDirection};
|
use super::{ItemHandle, SplitDirection};
|
||||||
use crate::{toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace};
|
use crate::{
|
||||||
|
dock::{DockAnchor, MoveDock},
|
||||||
|
toolbar::Toolbar,
|
||||||
|
Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace,
|
||||||
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
use context_menu::{ContextMenu, ContextMenuItem};
|
use context_menu::{ContextMenu, ContextMenuItem};
|
||||||
@ -76,13 +80,27 @@ pub struct DeploySplitMenu {
|
|||||||
position: Vector2F,
|
position: Vector2F,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct DeployDockMenu {
|
||||||
|
position: Vector2F,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct DeployNewMenu {
|
pub struct DeployNewMenu {
|
||||||
position: Vector2F,
|
position: Vector2F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_actions!(pane, [GoBack, GoForward, ActivateItem]);
|
impl_actions!(pane, [GoBack, GoForward, ActivateItem]);
|
||||||
impl_internal_actions!(pane, [CloseItem, DeploySplitMenu, DeployNewMenu, MoveItem]);
|
impl_internal_actions!(
|
||||||
|
pane,
|
||||||
|
[
|
||||||
|
CloseItem,
|
||||||
|
DeploySplitMenu,
|
||||||
|
DeployNewMenu,
|
||||||
|
DeployDockMenu,
|
||||||
|
MoveItem
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
|
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
|
||||||
|
|
||||||
@ -141,6 +159,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||||||
cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx));
|
cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx));
|
||||||
cx.add_action(Pane::deploy_split_menu);
|
cx.add_action(Pane::deploy_split_menu);
|
||||||
cx.add_action(Pane::deploy_new_menu);
|
cx.add_action(Pane::deploy_new_menu);
|
||||||
|
cx.add_action(Pane::deploy_dock_menu);
|
||||||
cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
|
cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
|
||||||
Pane::reopen_closed_item(workspace, cx).detach();
|
Pane::reopen_closed_item(workspace, cx).detach();
|
||||||
});
|
});
|
||||||
@ -186,6 +205,7 @@ pub struct Pane {
|
|||||||
nav_history: Rc<RefCell<NavHistory>>,
|
nav_history: Rc<RefCell<NavHistory>>,
|
||||||
toolbar: ViewHandle<Toolbar>,
|
toolbar: ViewHandle<Toolbar>,
|
||||||
context_menu: ViewHandle<ContextMenu>,
|
context_menu: ViewHandle<ContextMenu>,
|
||||||
|
is_dock: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ItemNavHistory {
|
pub struct ItemNavHistory {
|
||||||
@ -235,7 +255,7 @@ pub enum ReorderBehavior {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Pane {
|
impl Pane {
|
||||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(is_dock: bool, cx: &mut ViewContext<Self>) -> Self {
|
||||||
let handle = cx.weak_handle();
|
let handle = cx.weak_handle();
|
||||||
let context_menu = cx.add_view(ContextMenu::new);
|
let context_menu = cx.add_view(ContextMenu::new);
|
||||||
Self {
|
Self {
|
||||||
@ -254,6 +274,7 @@ impl Pane {
|
|||||||
})),
|
})),
|
||||||
toolbar: cx.add_view(|_| Toolbar::new(handle)),
|
toolbar: cx.add_view(|_| Toolbar::new(handle)),
|
||||||
context_menu,
|
context_menu,
|
||||||
|
is_dock,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,6 +997,20 @@ impl Pane {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deploy_dock_menu(&mut self, action: &DeployDockMenu, cx: &mut ViewContext<Self>) {
|
||||||
|
self.context_menu.update(cx, |menu, cx| {
|
||||||
|
menu.show(
|
||||||
|
action.position,
|
||||||
|
vec![
|
||||||
|
ContextMenuItem::item("Move Dock Right", MoveDock(DockAnchor::Right)),
|
||||||
|
ContextMenuItem::item("Move Dock Bottom", MoveDock(DockAnchor::Bottom)),
|
||||||
|
ContextMenuItem::item("Move Dock Maximized", MoveDock(DockAnchor::Expanded)),
|
||||||
|
],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn deploy_new_menu(&mut self, action: &DeployNewMenu, cx: &mut ViewContext<Self>) {
|
fn deploy_new_menu(&mut self, action: &DeployNewMenu, cx: &mut ViewContext<Self>) {
|
||||||
self.context_menu.update(cx, |menu, cx| {
|
self.context_menu.update(cx, |menu, cx| {
|
||||||
menu.show(
|
menu.show(
|
||||||
@ -1320,6 +1355,8 @@ impl View for Pane {
|
|||||||
|
|
||||||
let this = cx.handle();
|
let this = cx.handle();
|
||||||
|
|
||||||
|
let is_dock = self.is_dock;
|
||||||
|
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
EventHandler::new(if let Some(active_item) = self.active_item() {
|
EventHandler::new(if let Some(active_item) = self.active_item() {
|
||||||
@ -1382,10 +1419,16 @@ impl View for Pane {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
.on_down(MouseButton::Left, |e, cx| {
|
.on_down(MouseButton::Left, move |e, cx| {
|
||||||
|
if is_dock {
|
||||||
|
cx.dispatch_action(DeployDockMenu {
|
||||||
|
position: e.position,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
cx.dispatch_action(DeploySplitMenu {
|
cx.dispatch_action(DeploySplitMenu {
|
||||||
position: e.position,
|
position: e.position,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.boxed(),
|
.boxed(),
|
||||||
])
|
])
|
||||||
@ -1570,7 +1613,8 @@ mod tests {
|
|||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, None, cx).await;
|
let project = Project::test(fs, None, cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, crate::tests::default_item_factory, cx));
|
||||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||||
|
|
||||||
// 1. Add with a destination index
|
// 1. Add with a destination index
|
||||||
@ -1658,7 +1702,8 @@ mod tests {
|
|||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, None, cx).await;
|
let project = Project::test(fs, None, cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, crate::tests::default_item_factory, cx));
|
||||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||||
|
|
||||||
// 1. Add with a destination index
|
// 1. Add with a destination index
|
||||||
@ -1734,7 +1779,8 @@ mod tests {
|
|||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, None, cx).await;
|
let project = Project::test(fs, None, cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, crate::tests::default_item_factory, cx));
|
||||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||||
|
|
||||||
// singleton view
|
// singleton view
|
||||||
|
@ -74,8 +74,7 @@ impl WaitingRoom {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let project_id = contact.projects[project_index].id;
|
let project_id = contact.projects[project_index].id;
|
||||||
let client = app_state.client.clone();
|
let client = app_state.client.clone();
|
||||||
let _join_task =
|
let _join_task = cx.spawn_weak({
|
||||||
cx.spawn_weak({
|
|
||||||
let contact = contact.clone();
|
let contact = contact.clone();
|
||||||
|this, mut cx| async move {
|
|this, mut cx| async move {
|
||||||
let project = Project::remote(
|
let project = Project::remote(
|
||||||
@ -95,17 +94,20 @@ impl WaitingRoom {
|
|||||||
match project {
|
match project {
|
||||||
Ok(project) => {
|
Ok(project) => {
|
||||||
cx.replace_root_view(|cx| {
|
cx.replace_root_view(|cx| {
|
||||||
let mut workspace = Workspace::new(project, cx);
|
let mut workspace =
|
||||||
|
Workspace::new(project, app_state.default_item_factory, cx);
|
||||||
(app_state.initialize_workspace)(
|
(app_state.initialize_workspace)(
|
||||||
&mut workspace,
|
&mut workspace,
|
||||||
&app_state,
|
&app_state,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
workspace.toggle_sidebar(Side::Left, cx);
|
workspace.toggle_sidebar(Side::Left, cx);
|
||||||
if let Some((host_peer_id, _)) =
|
if let Some((host_peer_id, _)) = workspace
|
||||||
workspace.project.read(cx).collaborators().iter().find(
|
.project
|
||||||
|(_, collaborator)| collaborator.replica_id == 0,
|
.read(cx)
|
||||||
)
|
.collaborators()
|
||||||
|
.iter()
|
||||||
|
.find(|(_, collaborator)| collaborator.replica_id == 0)
|
||||||
{
|
{
|
||||||
if let Some(follow) = workspace
|
if let Some(follow) = workspace
|
||||||
.toggle_follow(&ToggleFollow(*host_peer_id), cx)
|
.toggle_follow(&ToggleFollow(*host_peer_id), cx)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
pub mod dock;
|
|
||||||
/// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in
|
/// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in
|
||||||
/// which the workspace uses to change the activated pane.
|
/// which the workspace uses to change the activated pane.
|
||||||
///
|
///
|
||||||
/// This may cause issues when you're trying to write tests that use workspace focus to add items at
|
/// This may cause issues when you're trying to write tests that use workspace focus to add items at
|
||||||
/// specific locations.
|
/// specific locations.
|
||||||
|
pub mod dock;
|
||||||
pub mod pane;
|
pub mod pane;
|
||||||
pub mod pane_group;
|
pub mod pane_group;
|
||||||
pub mod searchable;
|
pub mod searchable;
|
||||||
@ -18,7 +18,7 @@ use client::{
|
|||||||
};
|
};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::{hash_map, HashMap, HashSet};
|
use collections::{hash_map, HashMap, HashSet};
|
||||||
use dock::{Dock, DockPosition, ToggleDock};
|
use dock::{DefaultItemFactory, Dock, DockAnchor, ToggleDockButton};
|
||||||
use drag_and_drop::DragAndDrop;
|
use drag_and_drop::DragAndDrop;
|
||||||
use futures::{channel::oneshot, FutureExt};
|
use futures::{channel::oneshot, FutureExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
@ -147,6 +147,7 @@ impl_actions!(workspace, [ToggleProjectOnline, ActivatePane]);
|
|||||||
|
|
||||||
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
|
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
|
||||||
pane::init(cx);
|
pane::init(cx);
|
||||||
|
dock::init(cx);
|
||||||
|
|
||||||
cx.add_global_action(open);
|
cx.add_global_action(open);
|
||||||
cx.add_global_action({
|
cx.add_global_action({
|
||||||
@ -262,6 +263,7 @@ pub struct AppState {
|
|||||||
pub fs: Arc<dyn fs::Fs>,
|
pub fs: Arc<dyn fs::Fs>,
|
||||||
pub build_window_options: fn() -> WindowOptions<'static>,
|
pub build_window_options: fn() -> WindowOptions<'static>,
|
||||||
pub initialize_workspace: fn(&mut Workspace, &Arc<AppState>, &mut ViewContext<Workspace>),
|
pub initialize_workspace: fn(&mut Workspace, &Arc<AppState>, &mut ViewContext<Workspace>),
|
||||||
|
pub default_item_factory: DefaultItemFactory,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash)]
|
#[derive(Eq, PartialEq, Hash)]
|
||||||
@ -867,6 +869,7 @@ impl AppState {
|
|||||||
project_store,
|
project_store,
|
||||||
initialize_workspace: |_, _, _| {},
|
initialize_workspace: |_, _, _| {},
|
||||||
build_window_options: Default::default,
|
build_window_options: Default::default,
|
||||||
|
default_item_factory: |_, _| unimplemented!(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -920,7 +923,11 @@ enum FollowerItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
pub fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
dock_default_factory: DefaultItemFactory,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Self {
|
||||||
cx.observe_fullscreen(|_, _, cx| cx.notify()).detach();
|
cx.observe_fullscreen(|_, _, cx| cx.notify()).detach();
|
||||||
|
|
||||||
cx.observe_window_activation(Self::on_window_activation_changed)
|
cx.observe_window_activation(Self::on_window_activation_changed)
|
||||||
@ -947,14 +954,14 @@ impl Workspace {
|
|||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let pane = cx.add_view(Pane::new);
|
let center_pane = cx.add_view(|cx| Pane::new(false, cx));
|
||||||
let pane_id = pane.id();
|
let pane_id = center_pane.id();
|
||||||
cx.subscribe(&pane, move |this, _, event, cx| {
|
cx.subscribe(¢er_pane, move |this, _, event, cx| {
|
||||||
this.handle_pane_event(pane_id, event, cx)
|
this.handle_pane_event(pane_id, event, cx)
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
cx.focus(&pane);
|
cx.focus(¢er_pane);
|
||||||
cx.emit(Event::PaneAdded(pane.clone()));
|
cx.emit(Event::PaneAdded(center_pane.clone()));
|
||||||
|
|
||||||
let fs = project.read(cx).fs().clone();
|
let fs = project.read(cx).fs().clone();
|
||||||
let user_store = project.read(cx).user_store();
|
let user_store = project.read(cx).user_store();
|
||||||
@ -977,19 +984,18 @@ impl Workspace {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let weak_self = cx.weak_handle();
|
let weak_self = cx.weak_handle();
|
||||||
|
|
||||||
cx.emit_global(WorkspaceCreated(weak_self.clone()));
|
cx.emit_global(WorkspaceCreated(weak_self.clone()));
|
||||||
|
|
||||||
let dock = Dock::new(cx);
|
let dock = Dock::new(cx, dock_default_factory);
|
||||||
|
|
||||||
let left_sidebar = cx.add_view(|_| Sidebar::new(Side::Left));
|
let left_sidebar = cx.add_view(|_| Sidebar::new(Side::Left));
|
||||||
let right_sidebar = cx.add_view(|_| Sidebar::new(Side::Right));
|
let right_sidebar = cx.add_view(|_| Sidebar::new(Side::Right));
|
||||||
let left_sidebar_buttons = cx.add_view(|cx| SidebarButtons::new(left_sidebar.clone(), cx));
|
let left_sidebar_buttons = cx.add_view(|cx| SidebarButtons::new(left_sidebar.clone(), cx));
|
||||||
let toggle_dock = cx.add_view(|cx| ToggleDock::new(Arc::new(dock), cx));
|
let toggle_dock = cx.add_view(|cx| ToggleDockButton::new(weak_self.clone(), cx));
|
||||||
let right_sidebar_buttons =
|
let right_sidebar_buttons =
|
||||||
cx.add_view(|cx| SidebarButtons::new(right_sidebar.clone(), cx));
|
cx.add_view(|cx| SidebarButtons::new(right_sidebar.clone(), cx));
|
||||||
let status_bar = cx.add_view(|cx| {
|
let status_bar = cx.add_view(|cx| {
|
||||||
let mut status_bar = StatusBar::new(&pane.clone(), cx);
|
let mut status_bar = StatusBar::new(¢er_pane.clone(), cx);
|
||||||
status_bar.add_left_item(left_sidebar_buttons, cx);
|
status_bar.add_left_item(left_sidebar_buttons, cx);
|
||||||
status_bar.add_right_item(right_sidebar_buttons, cx);
|
status_bar.add_right_item(right_sidebar_buttons, cx);
|
||||||
status_bar.add_right_item(toggle_dock, cx);
|
status_bar.add_right_item(toggle_dock, cx);
|
||||||
@ -1003,11 +1009,11 @@ impl Workspace {
|
|||||||
let mut this = Workspace {
|
let mut this = Workspace {
|
||||||
modal: None,
|
modal: None,
|
||||||
weak_self,
|
weak_self,
|
||||||
center: PaneGroup::new(pane.clone()),
|
center: PaneGroup::new(center_pane.clone()),
|
||||||
dock,
|
dock,
|
||||||
panes: vec![pane.clone()],
|
panes: vec![center_pane.clone()],
|
||||||
panes_by_item: Default::default(),
|
panes_by_item: Default::default(),
|
||||||
active_pane: pane.clone(),
|
active_pane: center_pane.clone(),
|
||||||
status_bar,
|
status_bar,
|
||||||
notifications: Default::default(),
|
notifications: Default::default(),
|
||||||
client,
|
client,
|
||||||
@ -1081,6 +1087,7 @@ impl Workspace {
|
|||||||
app_state.fs.clone(),
|
app_state.fs.clone(),
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
|
app_state.default_item_factory,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
|
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
|
||||||
@ -1532,7 +1539,7 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
|
fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> ViewHandle<Pane> {
|
||||||
let pane = cx.add_view(Pane::new);
|
let pane = cx.add_view(|cx| Pane::new(false, cx));
|
||||||
let pane_id = pane.id();
|
let pane_id = pane.id();
|
||||||
cx.subscribe(&pane, move |this, _, event, cx| {
|
cx.subscribe(&pane, move |this, _, event, cx| {
|
||||||
this.handle_pane_event(pane_id, event, cx)
|
this.handle_pane_event(pane_id, event, cx)
|
||||||
@ -1549,6 +1556,10 @@ impl Workspace {
|
|||||||
Pane::add_item(self, &active_pane, item, true, true, None, cx);
|
Pane::add_item(self, &active_pane, item, true, true, None, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_item_to_dock(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
||||||
|
Pane::add_item(self, &self.dock.pane(), item, true, true, None, cx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn open_path(
|
pub fn open_path(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: impl Into<ProjectPath>,
|
path: impl Into<ProjectPath>,
|
||||||
@ -2573,7 +2584,7 @@ impl View for Workspace {
|
|||||||
)
|
)
|
||||||
.with_children(
|
.with_children(
|
||||||
self.dock
|
self.dock
|
||||||
.render(&theme, DockPosition::Bottom)
|
.render(&theme, DockAnchor::Bottom)
|
||||||
.map(|dock| {
|
.map(|dock| {
|
||||||
FlexItem::new(dock)
|
FlexItem::new(dock)
|
||||||
.flex(1., true)
|
.flex(1., true)
|
||||||
@ -2587,7 +2598,7 @@ impl View for Workspace {
|
|||||||
)
|
)
|
||||||
.with_children(
|
.with_children(
|
||||||
self.dock
|
self.dock
|
||||||
.render(&theme, DockPosition::Right)
|
.render(&theme, DockAnchor::Right)
|
||||||
.map(|dock| FlexItem::new(dock).flex(1., true).boxed()),
|
.map(|dock| FlexItem::new(dock).flex(1., true).boxed()),
|
||||||
)
|
)
|
||||||
.with_children(
|
.with_children(
|
||||||
@ -2603,7 +2614,7 @@ impl View for Workspace {
|
|||||||
)
|
)
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
})
|
||||||
.with_children(self.dock.render(&theme, DockPosition::Fullscreen))
|
.with_children(self.dock.render(&theme, DockAnchor::Expanded))
|
||||||
.with_children(self.modal.as_ref().map(|m| {
|
.with_children(self.modal.as_ref().map(|m| {
|
||||||
ChildView::new(m)
|
ChildView::new(m)
|
||||||
.contained()
|
.contained()
|
||||||
@ -2811,7 +2822,7 @@ pub fn open_paths(
|
|||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
new_project = Some(project.clone());
|
new_project = Some(project.clone());
|
||||||
let mut workspace = Workspace::new(project, cx);
|
let mut workspace = Workspace::new(project, app_state.default_item_factory, cx);
|
||||||
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
|
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
|
||||||
if contains_directory {
|
if contains_directory {
|
||||||
workspace.toggle_sidebar(Side::Left, cx);
|
workspace.toggle_sidebar(Side::Left, cx);
|
||||||
@ -2872,6 +2883,7 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
|
|||||||
app_state.fs.clone(),
|
app_state.fs.clone(),
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
|
app_state.default_item_factory,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
(app_state.initialize_workspace)(&mut workspace, app_state, cx);
|
(app_state.initialize_workspace)(&mut workspace, app_state, cx);
|
||||||
@ -2889,6 +2901,13 @@ mod tests {
|
|||||||
use project::{FakeFs, Project, ProjectEntryId};
|
use project::{FakeFs, Project, ProjectEntryId};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
pub fn default_item_factory(
|
||||||
|
_workspace: &mut Workspace,
|
||||||
|
_cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Box<dyn ItemHandle> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_tab_disambiguation(cx: &mut TestAppContext) {
|
async fn test_tab_disambiguation(cx: &mut TestAppContext) {
|
||||||
cx.foreground().forbid_parking();
|
cx.foreground().forbid_parking();
|
||||||
@ -2896,7 +2915,8 @@ mod tests {
|
|||||||
|
|
||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
|
||||||
|
|
||||||
// Adding an item with no ambiguity renders the tab without detail.
|
// Adding an item with no ambiguity renders the tab without detail.
|
||||||
let item1 = cx.add_view(&workspace, |_| {
|
let item1 = cx.add_view(&workspace, |_| {
|
||||||
@ -2960,7 +2980,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(fs, ["root1".as_ref()], cx).await;
|
let project = Project::test(fs, ["root1".as_ref()], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
|
||||||
let worktree_id = project.read_with(cx, |project, cx| {
|
let worktree_id = project.read_with(cx, |project, cx| {
|
||||||
project.worktrees(cx).next().unwrap().read(cx).id()
|
project.worktrees(cx).next().unwrap().read(cx).id()
|
||||||
});
|
});
|
||||||
@ -3056,7 +3077,8 @@ mod tests {
|
|||||||
fs.insert_tree("/root", json!({ "one": "" })).await;
|
fs.insert_tree("/root", json!({ "one": "" })).await;
|
||||||
|
|
||||||
let project = Project::test(fs, ["root".as_ref()], cx).await;
|
let project = Project::test(fs, ["root".as_ref()], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
|
||||||
|
|
||||||
// When there are no dirty items, there's nothing to do.
|
// When there are no dirty items, there's nothing to do.
|
||||||
let item1 = cx.add_view(&workspace, |_| TestItem::new());
|
let item1 = cx.add_view(&workspace, |_| TestItem::new());
|
||||||
@ -3096,7 +3118,8 @@ mod tests {
|
|||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, None, cx).await;
|
let project = Project::test(fs, None, cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
|
||||||
|
|
||||||
let item1 = cx.add_view(&workspace, |_| {
|
let item1 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
@ -3191,7 +3214,8 @@ mod tests {
|
|||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
|
||||||
|
|
||||||
// Create several workspace items with single project entries, and two
|
// Create several workspace items with single project entries, and two
|
||||||
// workspace items with multiple project entries.
|
// workspace items with multiple project entries.
|
||||||
@ -3292,7 +3316,8 @@ mod tests {
|
|||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
|
||||||
|
|
||||||
let item = cx.add_view(&workspace, |_| {
|
let item = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
@ -3409,7 +3434,7 @@ mod tests {
|
|||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
|
||||||
|
|
||||||
let item = cx.add_view(&workspace, |_| {
|
let item = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
|
@ -19,20 +19,21 @@ use futures::{
|
|||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
FutureExt, SinkExt, StreamExt,
|
FutureExt, SinkExt, StreamExt,
|
||||||
};
|
};
|
||||||
use gpui::{executor::Background, App, AssetSource, AsyncAppContext, Task};
|
use gpui::{executor::Background, App, AssetSource, AsyncAppContext, Task, ViewContext};
|
||||||
use isahc::{config::Configurable, AsyncBody, Request};
|
use isahc::{config::Configurable, AsyncBody, Request};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::{Fs, ProjectStore};
|
use project::{Fs, ProjectStore};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::{self, KeymapFileContent, Settings, SettingsFileContent};
|
use settings::{self, KeymapFileContent, Settings, SettingsFileContent, WorkingDirectory};
|
||||||
use smol::process::Command;
|
use smol::process::Command;
|
||||||
use std::{env, ffi::OsStr, fs, panic, path::PathBuf, sync::Arc, thread, time::Duration};
|
use std::{env, ffi::OsStr, fs, panic, path::PathBuf, sync::Arc, thread, time::Duration};
|
||||||
|
use terminal::terminal_container_view::{get_working_directory, TerminalContainer};
|
||||||
|
|
||||||
use theme::ThemeRegistry;
|
use theme::ThemeRegistry;
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
use workspace::{self, AppState, NewFile, OpenPaths};
|
use workspace::{self, AppState, ItemHandle, NewFile, OpenPaths, Workspace};
|
||||||
use zed::{
|
use zed::{
|
||||||
self, build_window_options,
|
self, build_window_options,
|
||||||
fs::RealFs,
|
fs::RealFs,
|
||||||
@ -148,6 +149,7 @@ fn main() {
|
|||||||
fs,
|
fs,
|
||||||
build_window_options,
|
build_window_options,
|
||||||
initialize_workspace,
|
initialize_workspace,
|
||||||
|
default_item_factory,
|
||||||
});
|
});
|
||||||
auto_update::init(db, http, client::ZED_SERVER_URL.clone(), cx);
|
auto_update::init(db, http, client::ZED_SERVER_URL.clone(), cx);
|
||||||
workspace::init(app_state.clone(), cx);
|
workspace::init(app_state.clone(), cx);
|
||||||
@ -591,3 +593,20 @@ async fn handle_cli_connection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_item_factory(
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Box<dyn ItemHandle> {
|
||||||
|
let strategy = cx
|
||||||
|
.global::<Settings>()
|
||||||
|
.terminal_overrides
|
||||||
|
.working_directory
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(WorkingDirectory::CurrentProjectDirectory);
|
||||||
|
|
||||||
|
let working_directory = get_working_directory(workspace, cx, strategy);
|
||||||
|
|
||||||
|
let terminal_handle = cx.add_view(|cx| TerminalContainer::new(working_directory, false, cx));
|
||||||
|
Box::new(terminal_handle)
|
||||||
|
}
|
||||||
|
@ -723,7 +723,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
||||||
let file1 = entries[0].clone();
|
let file1 = entries[0].clone();
|
||||||
@ -842,7 +843,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/dir1".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/dir1".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
// Open a file within an existing worktree.
|
// Open a file within an existing worktree.
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
@ -1001,7 +1003,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
// Open a file within an existing worktree.
|
// Open a file within an existing worktree.
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
@ -1043,7 +1046,8 @@ mod tests {
|
|||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
project.update(cx, |project, _| project.languages().add(rust_lang()));
|
project.update(cx, |project, _| project.languages().add(rust_lang()));
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
|
let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
|
||||||
|
|
||||||
// Create a new untitled buffer
|
// Create a new untitled buffer
|
||||||
@ -1132,7 +1136,8 @@ mod tests {
|
|||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||||
project.update(cx, |project, _| project.languages().add(rust_lang()));
|
project.update(cx, |project, _| project.languages().add(rust_lang()));
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
// Create a new untitled buffer
|
// Create a new untitled buffer
|
||||||
cx.dispatch_action(window_id, NewFile);
|
cx.dispatch_action(window_id, NewFile);
|
||||||
@ -1185,7 +1190,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
||||||
let file1 = entries[0].clone();
|
let file1 = entries[0].clone();
|
||||||
@ -1258,7 +1264,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
|
|
||||||
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
||||||
let file1 = entries[0].clone();
|
let file1 = entries[0].clone();
|
||||||
@ -1522,7 +1529,8 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) =
|
||||||
|
cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
|
||||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||||
|
|
||||||
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
let entries = cx.read(|cx| workspace.file_project_paths(cx));
|
||||||
|
Loading…
Reference in New Issue
Block a user