Remove AppState from workspace actions

This allows those actions to be bound to keystrokes in the keymap.
Also, remove the WorkspaceParams struct, simplify how Workspaces are
constructed.
This commit is contained in:
Max Brunsfeld 2022-05-19 14:37:26 -07:00
parent 7445197f4d
commit ef0b584532
21 changed files with 335 additions and 486 deletions

1
Cargo.lock generated
View File

@ -907,6 +907,7 @@ dependencies = [
"fuzzy",
"gpui",
"picker",
"project",
"serde_json",
"settings",
"theme",

View File

@ -1630,7 +1630,7 @@ mod tests {
use gpui::{
executor::{self, Deterministic},
geometry::vector::vec2f,
ModelHandle, TestAppContext, ViewHandle,
ModelHandle, Task, TestAppContext, ViewHandle,
};
use language::{
range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
@ -1662,7 +1662,7 @@ mod tests {
time::Duration,
};
use theme::ThemeRegistry;
use workspace::{Item, SplitDirection, ToggleFollow, Workspace, WorkspaceParams};
use workspace::{Item, SplitDirection, ToggleFollow, Workspace};
#[cfg(test)]
#[ctor::ctor]
@ -4322,13 +4322,7 @@ mod tests {
// Join the project as client B.
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
let mut params = cx_b.update(WorkspaceParams::test);
params.languages = lang_registry.clone();
params.project = project_b.clone();
params.client = client_b.client.clone();
params.user_store = client_b.user_store.clone();
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(&params, cx));
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "main.rs"), true, cx)
@ -4563,13 +4557,7 @@ mod tests {
// Join the worktree as client B.
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
let mut params = cx_b.update(WorkspaceParams::test);
params.languages = lang_registry.clone();
params.project = project_b.clone();
params.client = client_b.client.clone();
params.user_store = client_b.user_store.clone();
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(&params, cx));
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "one.rs"), true, cx)
@ -6602,13 +6590,21 @@ mod tests {
})
});
Channel::init(&client);
Project::init(&client);
cx.update(|cx| {
workspace::init(&client, cx);
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
let app_state = Arc::new(workspace::AppState {
client: client.clone(),
user_store: user_store.clone(),
languages: Arc::new(LanguageRegistry::new(Task::ready(()))),
themes: ThemeRegistry::new((), cx.font_cache()),
fs: FakeFs::new(cx.background()),
build_window_options: || Default::default(),
build_workspace: |_, _, _| unimplemented!(),
});
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
Channel::init(&client);
Project::init(&client);
cx.update(|cx| workspace::init(app_state.clone(), cx));
client
.authenticate_and_connect(false, &cx.to_async())
.await
@ -6846,23 +6842,7 @@ mod tests {
cx: &mut TestAppContext,
) -> ViewHandle<Workspace> {
let (window_id, _) = cx.add_window(|_| EmptyView);
cx.add_view(window_id, |cx| {
let fs = project.read(cx).fs().clone();
Workspace::new(
&WorkspaceParams {
fs,
project: project.clone(),
user_store: self.user_store.clone(),
languages: self.language_registry.clone(),
themes: ThemeRegistry::new((), cx.font_cache().clone()),
channel_list: cx.add_model(|cx| {
ChannelList::new(self.user_store.clone(), self.client.clone(), cx)
}),
client: self.client.clone(),
},
cx,
)
})
cx.add_view(window_id, |cx| Workspace::new(project.clone(), cx))
}
async fn simulate_host(

View File

@ -12,6 +12,7 @@ editor = { path = "../editor" }
fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
picker = { path = "../picker" }
project = { path = "../project" }
settings = { path = "../settings" }
util = { path = "../util" }
theme = { path = "../theme" }
@ -20,6 +21,7 @@ workspace = { path = "../workspace" }
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
editor = { path = "../editor", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
serde_json = { version = "1.0.64", features = ["preserve_order"] }
workspace = { path = "../workspace", features = ["test-support"] }
ctor = "0.1"

View File

@ -299,7 +299,8 @@ mod tests {
use super::*;
use editor::Editor;
use gpui::TestAppContext;
use workspace::{Workspace, WorkspaceParams};
use project::Project;
use workspace::{AppState, Workspace};
#[test]
fn test_humanize_action_name() {
@ -319,15 +320,16 @@ mod tests {
#[gpui::test]
async fn test_command_palette(cx: &mut TestAppContext) {
let params = cx.update(WorkspaceParams::test);
let app_state = cx.update(AppState::test);
cx.update(|cx| {
editor::init(cx);
workspace::init(&params.client, cx);
workspace::init(app_state.clone(), cx);
init(cx);
});
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
let project = Project::test(app_state.fs.clone(), [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let editor = cx.add_view(window_id, |cx| {
let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx);

View File

@ -60,7 +60,6 @@ pub struct ContactsPanel {
filter_editor: ViewHandle<Editor>,
collapsed_sections: Vec<Section>,
selection: Option<usize>,
app_state: Arc<AppState>,
_maintain_contacts: Subscription,
}
@ -175,7 +174,6 @@ impl ContactsPanel {
let mut this = Self {
list_state: ListState::new(0, Orientation::Top, 1000., {
let this = cx.weak_handle();
let app_state = app_state.clone();
move |ix, cx| {
let this = this.upgrade(cx).unwrap();
let this = this.read(cx);
@ -222,7 +220,6 @@ impl ContactsPanel {
contact.clone(),
current_user_id,
*project_ix,
app_state.clone(),
theme,
is_last_project_for_contact,
is_selected,
@ -240,7 +237,6 @@ impl ContactsPanel {
_maintain_contacts: cx
.observe(&app_state.user_store, |this, _, cx| this.update_entries(cx)),
user_store: app_state.user_store.clone(),
app_state,
};
this.update_entries(cx);
this
@ -339,7 +335,6 @@ impl ContactsPanel {
contact: Arc<Contact>,
current_user_id: Option<u64>,
project_index: usize,
app_state: Arc<AppState>,
theme: &theme::ContactsPanel,
is_last_project: bool,
is_selected: bool,
@ -444,7 +439,6 @@ impl ContactsPanel {
cx.dispatch_global_action(JoinProject {
contact: contact.clone(),
project_index,
app_state: app_state.clone(),
});
}
})
@ -770,7 +764,6 @@ impl ContactsPanel {
.dispatch_global_action(JoinProject {
contact: contact.clone(),
project_index: *project_index,
app_state: self.app_state.clone(),
}),
_ => {}
}
@ -916,17 +909,17 @@ impl PartialEq for ContactEntry {
#[cfg(test)]
mod tests {
use super::*;
use client::{proto, test::FakeServer, ChannelList, Client};
use client::{proto, test::FakeServer, Client};
use gpui::TestAppContext;
use language::LanguageRegistry;
use project::Project;
use theme::ThemeRegistry;
use workspace::WorkspaceParams;
#[gpui::test]
async fn test_contact_panel(cx: &mut TestAppContext) {
let (app_state, server) = init(cx).await;
let workspace_params = cx.update(WorkspaceParams::test);
let workspace = cx.add_view(0, |cx| Workspace::new(&workspace_params, cx));
let project = Project::test(app_state.fs.clone(), [], cx).await;
let workspace = cx.add_view(0, |cx| Workspace::new(project, cx));
let panel = cx.add_view(0, |cx| {
ContactsPanel::new(app_state.clone(), workspace.downgrade(), cx)
});
@ -1110,13 +1103,6 @@ mod tests {
let mut client = Client::new(http_client.clone());
let server = FakeServer::for_client(100, &mut client, &cx).await;
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
let channel_list =
cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx));
let get_channels = server.receive::<proto::GetChannels>().await.unwrap();
server
.respond(get_channels.receipt(), Default::default())
.await;
(
Arc::new(AppState {
@ -1125,7 +1111,6 @@ mod tests {
client,
user_store: user_store.clone(),
fs,
channel_list,
build_window_options: || unimplemented!(),
build_workspace: |_, _, _| unimplemented!(),
}),

View File

@ -707,15 +707,12 @@ mod tests {
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16};
use serde_json::json;
use unindent::Unindent as _;
use workspace::WorkspaceParams;
use workspace::AppState;
#[gpui::test]
async fn test_diagnostics(cx: &mut TestAppContext) {
let params = cx.update(WorkspaceParams::test);
let project = params.project.clone();
let workspace = cx.add_view(0, |cx| Workspace::new(&params, cx));
params
let app_state = cx.update(AppState::test);
app_state
.fs
.as_fake()
.insert_tree(
@ -744,12 +741,8 @@ mod tests {
)
.await;
project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/test", true, cx)
})
.await
.unwrap();
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
let workspace = cx.add_view(0, |cx| Workspace::new(project.clone(), cx));
// Create some diagnostics
project.update(cx, |project, cx| {

View File

@ -8815,7 +8815,7 @@ mod tests {
let fs = FakeFs::new(cx.background().clone());
fs.insert_file("/file.rs", Default::default()).await;
let project = Project::test(fs, ["/file.rs"], cx).await;
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
@ -8937,7 +8937,7 @@ mod tests {
let fs = FakeFs::new(cx.background().clone());
fs.insert_file("/file.rs", text).await;
let project = Project::test(fs, ["/file.rs"], cx).await;
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))

View File

@ -258,9 +258,10 @@ mod tests {
use super::*;
use editor::{Editor, Input};
use serde_json::json;
use std::path::PathBuf;
use workspace::menu::{Confirm, SelectNext};
use workspace::{Workspace, WorkspaceParams};
use workspace::{
menu::{Confirm, SelectNext},
AppState, Workspace,
};
#[ctor::ctor]
fn init_logger() {
@ -271,13 +272,13 @@ mod tests {
#[gpui::test]
async fn test_matching_paths(cx: &mut gpui::TestAppContext) {
cx.update(|cx| {
let app_state = cx.update(|cx| {
super::init(cx);
editor::init(cx);
AppState::test(cx)
});
let params = cx.update(WorkspaceParams::test);
params
app_state
.fs
.as_fake()
.insert_tree(
@ -291,16 +292,8 @@ mod tests {
)
.await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx)
})
.await
.unwrap();
cx.read(|cx| workspace.read(cx).worktree_scans_complete(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));
cx.dispatch_action(window_id, Toggle);
let finder = cx.read(|cx| {
@ -341,9 +334,11 @@ mod tests {
#[gpui::test]
async fn test_matching_cancellation(cx: &mut gpui::TestAppContext) {
let params = cx.update(WorkspaceParams::test);
let fs = params.fs.as_fake();
fs.insert_tree(
let app_state = cx.update(AppState::test);
app_state
.fs
.as_fake()
.insert_tree(
"/dir",
json!({
"hello": "",
@ -357,16 +352,8 @@ mod tests {
)
.await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/dir", true, cx)
})
.await
.unwrap();
cx.read(|cx| workspace.read(cx).worktree_scans_complete(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 (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
@ -406,23 +393,20 @@ mod tests {
#[gpui::test]
async fn test_single_file_worktrees(cx: &mut gpui::TestAppContext) {
let params = cx.update(WorkspaceParams::test);
params
let app_state = cx.update(AppState::test);
app_state
.fs
.as_fake()
.insert_tree("/root", json!({ "the-parent-dir": { "the-file": "" } }))
.await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root/the-parent-dir/the-file", true, cx)
})
.await
.unwrap();
cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
let project = Project::test(
app_state.fs.clone(),
["/root/the-parent-dir/the-file".as_ref()],
cx,
)
.await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
@ -451,10 +435,12 @@ mod tests {
finder.read_with(cx, |f, _| assert_eq!(f.matches.len(), 0));
}
#[gpui::test(retries = 5)]
#[gpui::test]
async fn test_multiple_matches_with_same_relative_path(cx: &mut gpui::TestAppContext) {
let params = cx.update(WorkspaceParams::test);
params
cx.foreground().forbid_parking();
let app_state = cx.update(AppState::test);
app_state
.fs
.as_fake()
.insert_tree(
@ -466,19 +452,13 @@ mod tests {
)
.await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
workspace
.update(cx, |workspace, cx| {
workspace.open_paths(
vec![PathBuf::from("/root/dir1"), PathBuf::from("/root/dir2")],
let project = Project::test(
app_state.fs.clone(),
["/root/dir1".as_ref(), "/root/dir2".as_ref()],
cx,
)
})
.await;
cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
.await;
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let (_, finder) =
cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));

View File

@ -215,12 +215,12 @@ where
self.autoscroll(scroll_max, size.y(), item_height);
let start = cmp::min(
((self.scroll_top() - self.padding_top) / item_height) as usize,
((self.scroll_top() - self.padding_top) / item_height.max(1.)) as usize,
self.item_count,
);
let end = cmp::min(
self.item_count,
start + (size.y() / item_height).ceil() as usize + 1,
start + (size.y() / item_height.max(1.)).ceil() as usize + 1,
);
if (start..end).contains(&sample_item_ix) {

View File

@ -497,7 +497,7 @@ impl Project {
#[cfg(any(test, feature = "test-support"))]
pub async fn test(
fs: Arc<dyn Fs>,
root_paths: impl IntoIterator<Item = impl AsRef<Path>>,
root_paths: impl IntoIterator<Item = &Path>,
cx: &mut gpui::TestAppContext,
) -> ModelHandle<Project> {
let languages = Arc::new(LanguageRegistry::test());
@ -528,6 +528,14 @@ impl Project {
&self.languages
}
pub fn client(&self) -> Arc<Client> {
self.client.clone()
}
pub fn user_store(&self) -> ModelHandle<UserStore> {
self.user_store.clone()
}
#[cfg(any(test, feature = "test-support"))]
pub fn check_invariants(&self, cx: &AppContext) {
if self.is_local() {
@ -5294,7 +5302,7 @@ mod tests {
)
.unwrap();
let project = Project::test(Arc::new(RealFs), [root_link_path], cx).await;
let project = Project::test(Arc::new(RealFs), [root_link_path.as_ref()], cx).await;
project.read_with(cx, |project, cx| {
let tree = project.worktrees(cx).next().unwrap().read(cx);
@ -5378,7 +5386,7 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/the-root"], cx).await;
let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
project.update(cx, |project, _| {
project.languages.add(Arc::new(rust_language));
project.languages.add(Arc::new(json_language));
@ -5714,7 +5722,7 @@ mod tests {
)
.await;
let project = Project::test(fs, ["/dir/a.rs", "/dir/b.rs"], cx).await;
let project = Project::test(fs, ["/dir/a.rs".as_ref(), "/dir/b.rs".as_ref()], cx).await;
let buffer_a = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
@ -5825,7 +5833,7 @@ mod tests {
)
.await;
let project = Project::test(fs, ["/dir"], cx).await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let worktree_id =
project.read_with(cx, |p, cx| p.worktrees(cx).next().unwrap().read(cx).id());
@ -5947,7 +5955,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
let project = Project::test(fs, ["/dir"], cx).await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
@ -6016,7 +6024,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({ "a.rs": text })).await;
let project = Project::test(fs, ["/dir"], cx).await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
@ -6285,7 +6293,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({ "a.rs": text })).await;
let project = Project::test(fs, ["/dir"], cx).await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
.await
@ -6376,7 +6384,7 @@ mod tests {
)
.await;
let project = Project::test(fs, ["/dir"], cx).await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
@ -6530,7 +6538,7 @@ mod tests {
)
.await;
let project = Project::test(fs, ["/dir"], cx).await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
let buffer = project
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
.await
@ -6686,7 +6694,7 @@ mod tests {
)
.await;
let project = Project::test(fs, ["/dir/b.rs"], cx).await;
let project = Project::test(fs, ["/dir/b.rs".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
@ -6780,7 +6788,7 @@ mod tests {
)
.await;
let project = Project::test(fs, ["/dir"], cx).await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
@ -6838,7 +6846,7 @@ mod tests {
)
.await;
let project = Project::test(fs, ["/dir"], cx).await;
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
@ -6944,7 +6952,7 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
.await
@ -6973,7 +6981,7 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/dir/file1"], cx).await;
let project = Project::test(fs.clone(), ["/dir/file1".as_ref()], cx).await;
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
.await
@ -6995,7 +7003,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({})).await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let buffer = project.update(cx, |project, cx| {
project.create_buffer("", None, cx).unwrap()
});
@ -7182,7 +7190,7 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
// Spawn multiple tasks to open paths, repeating some paths.
let (buffer_a_1, buffer_b, buffer_a_2) = project.update(cx, |p, cx| {
@ -7227,7 +7235,7 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let buffer1 = project
.update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx))
@ -7359,7 +7367,7 @@ mod tests {
}),
)
.await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/dir/the-file", cx))
.await
@ -7444,7 +7452,7 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/the-dir"], cx).await;
let project = Project::test(fs.clone(), ["/the-dir".as_ref()], cx).await;
let buffer = project
.update(cx, |p, cx| p.open_local_buffer("/the-dir/a.rs", cx))
.await
@ -7708,7 +7716,7 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
let buffer = project
.update(cx, |project, cx| {
@ -7827,7 +7835,7 @@ mod tests {
}),
)
.await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
assert_eq!(
search(&project, SearchQuery::text("TWO", false, true), cx)
.await

View File

@ -913,11 +913,14 @@ mod tests {
use project::FakeFs;
use serde_json::json;
use std::{collections::HashSet, path::Path};
use workspace::WorkspaceParams;
#[gpui::test]
async fn test_visible_list(cx: &mut gpui::TestAppContext) {
cx.foreground().forbid_parking();
cx.update(|cx| {
let settings = Settings::test(cx);
cx.set_global(settings);
});
let fs = FakeFs::new(cx.background());
fs.insert_tree(
@ -956,9 +959,8 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/root1", "/root2"], cx).await;
let params = cx.update(WorkspaceParams::test);
let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
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 panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
assert_eq!(
visible_entries_as_strings(&panel, 0..50, cx),
@ -1005,6 +1007,10 @@ mod tests {
#[gpui::test(iterations = 30)]
async fn test_editing_files(cx: &mut gpui::TestAppContext) {
cx.foreground().forbid_parking();
cx.update(|cx| {
let settings = Settings::test(cx);
cx.set_global(settings);
});
let fs = FakeFs::new(cx.background());
fs.insert_tree(
@ -1043,9 +1049,8 @@ mod tests {
)
.await;
let project = Project::test(fs.clone(), ["/root1", "/root2"], cx).await;
let params = cx.update(WorkspaceParams::test);
let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
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 panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
select_path(&panel, "root1", cx);

View File

@ -295,7 +295,7 @@ mod tests {
let fs = FakeFs::new(cx.background());
fs.insert_tree("/dir", json!({ "test.rs": "" })).await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let _buffer = project

View File

@ -848,7 +848,7 @@ mod tests {
}),
)
.await;
let project = Project::test(fs.clone(), ["/dir"], cx).await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
let search_view = cx.add_view(Default::default(), |cx| {
ProjectSearchView::new(search.clone(), cx)

View File

@ -7,7 +7,7 @@ use picker::{Picker, PickerDelegate};
use settings::Settings;
use std::sync::Arc;
use theme::{Theme, ThemeRegistry};
use workspace::Workspace;
use workspace::{AppState, Workspace};
pub struct ThemeSelector {
registry: Arc<ThemeRegistry>,
@ -21,9 +21,14 @@ pub struct ThemeSelector {
actions!(theme_selector, [Toggle, Reload]);
pub fn init(cx: &mut MutableAppContext) {
cx.add_action(ThemeSelector::toggle);
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
Picker::<ThemeSelector>::init(cx);
cx.add_action({
let theme_registry = app_state.themes.clone();
move |workspace, _: &Toggle, cx| {
ThemeSelector::toggle(workspace, theme_registry.clone(), cx)
}
});
}
pub enum Event {
@ -63,8 +68,11 @@ impl ThemeSelector {
this
}
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
let themes = workspace.themes();
fn toggle(
workspace: &mut Workspace,
themes: Arc<ThemeRegistry>,
cx: &mut ViewContext<Workspace>,
) {
workspace.toggle_modal(cx, |_, cx| {
let this = cx.add_view(|cx| Self::new(themes, cx));
cx.subscribe(&this, Self::on_event).detach();

View File

@ -7,11 +7,12 @@ use editor::{display_map::ToDisplayPoint, Autoscroll};
use gpui::{json::json, keymap::Keystroke, ViewHandle};
use indoc::indoc;
use language::Selection;
use project::Project;
use util::{
set_eq,
test::{marked_text, marked_text_ranges_by, SetEqError},
};
use workspace::{WorkspaceHandle, WorkspaceParams};
use workspace::{AppState, WorkspaceHandle};
use crate::{state::Operator, *};
@ -30,7 +31,8 @@ impl<'a> VimTestContext<'a> {
settings::KeymapFileContent::load("keymaps/vim.json", cx).unwrap();
});
let params = cx.update(WorkspaceParams::test);
let params = cx.update(AppState::test);
let project = Project::test(params.fs.clone(), [], cx).await;
cx.update(|cx| {
cx.update_global(|settings: &mut Settings, _| {
@ -44,9 +46,8 @@ impl<'a> VimTestContext<'a> {
.insert_tree("/root", json!({ "dir": { "test.txt": "" } }))
.await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx)
})

View File

@ -920,7 +920,7 @@ impl NavHistory {
#[cfg(test)]
mod tests {
use super::*;
use crate::WorkspaceParams;
use crate::AppState;
use gpui::{ModelHandle, TestAppContext, ViewContext};
use project::Project;
use std::sync::atomic::AtomicUsize;
@ -929,8 +929,9 @@ mod tests {
async fn test_close_items(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
let params = cx.update(WorkspaceParams::test);
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
let app_state = cx.update(AppState::test);
let project = Project::test(app_state.fs.clone(), None, cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let item1 = cx.add_view(window_id, |_| {
let mut item = TestItem::new();
item.is_dirty = true;
@ -1019,8 +1020,9 @@ mod tests {
async fn test_prompting_only_on_last_item_for_entry(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
let params = cx.update(WorkspaceParams::test);
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
let app_state = cx.update(AppState::test);
let project = Project::test(app_state.fs.clone(), [], cx).await;
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let item = cx.add_view(window_id, |_| {
let mut item = TestItem::new();
item.is_dirty = true;

View File

@ -9,8 +9,7 @@ mod waiting_room;
use anyhow::{anyhow, Context, Result};
use client::{
proto, Authenticate, ChannelList, Client, Contact, PeerId, Subscription, TypedEnvelope, User,
UserStore,
proto, Authenticate, Client, Contact, PeerId, Subscription, TypedEnvelope, User, UserStore,
};
use clock::ReplicaId;
use collections::{hash_map, HashMap, HashSet};
@ -75,6 +74,8 @@ type FollowableItemBuilders = HashMap<
actions!(
workspace,
[
Open,
OpenNew,
Unfollow,
Save,
ActivatePreviousPane,
@ -83,16 +84,9 @@ actions!(
]
);
#[derive(Clone)]
pub struct Open(pub Arc<AppState>);
#[derive(Clone)]
pub struct OpenNew(pub Arc<AppState>);
#[derive(Clone)]
pub struct OpenPaths {
pub paths: Vec<PathBuf>,
pub app_state: Arc<AppState>,
}
#[derive(Clone)]
@ -102,31 +96,37 @@ pub struct ToggleFollow(pub PeerId);
pub struct JoinProject {
pub contact: Arc<Contact>,
pub project_index: usize,
pub app_state: Arc<AppState>,
}
impl_internal_actions!(
workspace,
[Open, OpenNew, OpenPaths, ToggleFollow, JoinProject]
);
impl_internal_actions!(workspace, [OpenPaths, ToggleFollow, JoinProject]);
pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
pane::init(cx);
cx.add_global_action(open);
cx.add_global_action(move |action: &OpenPaths, cx: &mut MutableAppContext| {
open_paths(&action.paths, &action.app_state, cx).detach();
cx.add_global_action({
let app_state = Arc::downgrade(&app_state);
move |action: &OpenPaths, cx: &mut MutableAppContext| {
if let Some(app_state) = app_state.upgrade() {
open_paths(&action.paths, &app_state, cx).detach();
}
}
});
cx.add_global_action(move |action: &OpenNew, cx: &mut MutableAppContext| {
open_new(&action.0, cx)
cx.add_global_action({
let app_state = Arc::downgrade(&app_state);
move |_: &OpenNew, cx: &mut MutableAppContext| {
if let Some(app_state) = app_state.upgrade() {
open_new(&app_state, cx)
}
}
});
cx.add_global_action(move |action: &JoinProject, cx: &mut MutableAppContext| {
join_project(
action.contact.clone(),
action.project_index,
&action.app_state,
cx,
);
cx.add_global_action({
let app_state = Arc::downgrade(&app_state);
move |action: &JoinProject, cx: &mut MutableAppContext| {
if let Some(app_state) = app_state.upgrade() {
join_project(action.contact.clone(), action.project_index, &app_state, cx);
}
}
});
cx.add_async_action(Workspace::toggle_follow);
@ -151,6 +151,7 @@ pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) {
workspace.activate_next_pane(cx)
});
let client = &app_state.client;
client.add_view_request_handler(Workspace::handle_follow);
client.add_view_message_handler(Workspace::handle_unfollow);
client.add_view_message_handler(Workspace::handle_update_followers);
@ -188,7 +189,6 @@ pub struct AppState {
pub client: Arc<client::Client>,
pub user_store: ModelHandle<client::UserStore>,
pub fs: Arc<dyn fs::Fs>,
pub channel_list: ModelHandle<client::ChannelList>,
pub build_window_options: fn() -> WindowOptions<'static>,
pub build_workspace:
fn(ModelHandle<Project>, &Arc<AppState>, &mut ViewContext<Workspace>) -> Workspace,
@ -636,20 +636,9 @@ impl Into<AnyViewHandle> for &dyn NotificationHandle {
}
}
#[derive(Clone)]
pub struct WorkspaceParams {
pub project: ModelHandle<Project>,
pub client: Arc<Client>,
pub fs: Arc<dyn Fs>,
pub languages: Arc<LanguageRegistry>,
pub themes: Arc<ThemeRegistry>,
pub user_store: ModelHandle<UserStore>,
pub channel_list: ModelHandle<ChannelList>,
}
impl WorkspaceParams {
impl AppState {
#[cfg(any(test, feature = "test-support"))]
pub fn test(cx: &mut MutableAppContext) -> Self {
pub fn test(cx: &mut MutableAppContext) -> Arc<Self> {
let settings = Settings::test(cx);
cx.set_global(settings);
@ -658,42 +647,16 @@ impl WorkspaceParams {
let http_client = client::test::FakeHttpClient::with_404_response();
let client = Client::new(http_client.clone());
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
let project = Project::local(
client.clone(),
user_store.clone(),
languages.clone(),
fs.clone(),
cx,
);
Self {
project,
channel_list: cx
.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx)),
let themes = ThemeRegistry::new((), cx.font_cache().clone());
Arc::new(Self {
client,
themes: ThemeRegistry::new((), cx.font_cache().clone()),
themes,
fs,
languages,
user_store,
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn local(app_state: &Arc<AppState>, cx: &mut MutableAppContext) -> Self {
Self {
project: Project::local(
app_state.client.clone(),
app_state.user_store.clone(),
app_state.languages.clone(),
app_state.fs.clone(),
cx,
),
client: app_state.client.clone(),
fs: app_state.fs.clone(),
themes: app_state.themes.clone(),
languages: app_state.languages.clone(),
user_store: app_state.user_store.clone(),
channel_list: app_state.channel_list.clone(),
}
build_workspace: |project, _, cx| Workspace::new(project, cx),
build_window_options: || Default::default(),
})
}
}
@ -708,7 +671,6 @@ pub struct Workspace {
user_store: ModelHandle<client::UserStore>,
remote_entity_subscription: Option<Subscription>,
fs: Arc<dyn Fs>,
themes: Arc<ThemeRegistry>,
modal: Option<AnyViewHandle>,
center: PaneGroup,
left_sidebar: ViewHandle<Sidebar>,
@ -744,8 +706,8 @@ enum FollowerItem {
}
impl Workspace {
pub fn new(params: &WorkspaceParams, cx: &mut ViewContext<Self>) -> Self {
cx.observe(&params.project, |_, project, cx| {
pub fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
cx.observe(&project, |_, project, cx| {
if project.read(cx).is_read_only() {
cx.blur();
}
@ -753,7 +715,7 @@ impl Workspace {
})
.detach();
cx.subscribe(&params.project, move |this, project, event, cx| {
cx.subscribe(&project, move |this, project, event, cx| {
match event {
project::Event::RemoteIdChanged(remote_id) => {
this.project_remote_id_changed(*remote_id, cx);
@ -785,8 +747,11 @@ impl Workspace {
cx.focus(&pane);
cx.emit(Event::PaneAdded(pane.clone()));
let mut current_user = params.user_store.read(cx).watch_current_user().clone();
let mut connection_status = params.client.status().clone();
let fs = project.read(cx).fs().clone();
let user_store = project.read(cx).user_store();
let client = project.read(cx).client();
let mut current_user = user_store.read(cx).watch_current_user().clone();
let mut connection_status = client.status().clone();
let _observe_current_user = cx.spawn_weak(|this, mut cx| async move {
current_user.recv().await;
connection_status.recv().await;
@ -826,14 +791,13 @@ impl Workspace {
active_pane: pane.clone(),
status_bar,
notifications: Default::default(),
client: params.client.clone(),
client,
remote_entity_subscription: None,
user_store: params.user_store.clone(),
fs: params.fs.clone(),
themes: params.themes.clone(),
user_store,
fs,
left_sidebar,
right_sidebar,
project: params.project.clone(),
project,
leader_state: Default::default(),
follower_states_by_leader: Default::default(),
last_leaders_by_pane: Default::default(),
@ -867,10 +831,6 @@ impl Workspace {
&self.project
}
pub fn themes(&self) -> Arc<ThemeRegistry> {
self.themes.clone()
}
pub fn worktrees<'a>(
&self,
cx: &'a AppContext,
@ -2203,8 +2163,7 @@ impl std::fmt::Debug for OpenPaths {
}
}
fn open(action: &Open, cx: &mut MutableAppContext) {
let app_state = action.0.clone();
fn open(_: &Open, cx: &mut MutableAppContext) {
let mut paths = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: true,
@ -2212,7 +2171,7 @@ fn open(action: &Open, cx: &mut MutableAppContext) {
});
cx.spawn(|mut cx| async move {
if let Some(paths) = paths.recv().await.flatten() {
cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths, app_state }));
cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths }));
}
})
.detach();
@ -2320,7 +2279,7 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
app_state.fs.clone(),
cx,
);
(app_state.build_workspace)(project, &app_state, cx)
(app_state.build_workspace)(project, app_state, cx)
});
cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew(app_state.clone()));
cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew);
}

View File

@ -12,7 +12,7 @@ use cli::{
use client::{
self,
http::{self, HttpClient},
ChannelList, UserStore, ZED_SECRET_CLIENT_TOKEN,
UserStore, ZED_SECRET_CLIENT_TOKEN,
};
use fs::OpenOptions;
use futures::{
@ -133,15 +133,12 @@ fn main() {
let client = client::Client::new(http.clone());
let mut languages = languages::build_language_registry(login_shell_env_loaded);
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
let channel_list =
cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx));
auto_update::init(http, client::ZED_SERVER_URL.clone(), cx);
project::Project::init(&client);
client::Channel::init(&client);
client::init(client.clone(), cx);
command_palette::init(cx);
workspace::init(&client, cx);
editor::init(cx);
go_to_line::init(cx);
file_finder::init(cx);
@ -192,33 +189,33 @@ fn main() {
let app_state = Arc::new(AppState {
languages,
themes,
channel_list,
client: client.clone(),
user_store,
fs,
build_window_options,
build_workspace,
});
workspace::init(app_state.clone(), cx);
journal::init(app_state.clone(), cx);
theme_selector::init(cx);
theme_selector::init(app_state.clone(), cx);
zed::init(&app_state, cx);
cx.set_menus(menus::menus(&app_state.clone()));
cx.set_menus(menus::menus());
if stdout_is_a_pty() {
cx.platform().activate(true);
let paths = collect_path_args();
if paths.is_empty() {
cx.dispatch_global_action(OpenNew(app_state.clone()));
cx.dispatch_global_action(OpenNew);
} else {
cx.dispatch_global_action(OpenPaths { paths, app_state });
cx.dispatch_global_action(OpenPaths { paths });
}
} else {
if let Ok(Some(connection)) = cli_connections_rx.try_next() {
cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
.detach();
} else {
cx.dispatch_global_action(OpenNew(app_state.clone()));
cx.dispatch_global_action(OpenNew);
}
cx.spawn(|cx| async move {
while let Some(connection) = cli_connections_rx.next().await {

View File

@ -1,9 +1,7 @@
use crate::AppState;
use gpui::{Menu, MenuItem};
use std::sync::Arc;
#[cfg(target_os = "macos")]
pub fn menus(state: &Arc<AppState>) -> Vec<Menu<'static>> {
pub fn menus() -> Vec<Menu<'static>> {
vec![
Menu {
name: "Zed",
@ -33,12 +31,12 @@ pub fn menus(state: &Arc<AppState>) -> Vec<Menu<'static>> {
items: vec![
MenuItem::Action {
name: "New",
action: Box::new(workspace::OpenNew(state.clone())),
action: Box::new(workspace::OpenNew),
},
MenuItem::Separator,
MenuItem::Action {
name: "Open…",
action: Box::new(workspace::Open(state.clone())),
action: Box::new(workspace::Open),
},
],
},

View File

@ -1,13 +1,3 @@
use crate::{build_window_options, build_workspace, AppState};
use assets::Assets;
use client::{test::FakeHttpClient, ChannelList, Client, UserStore};
use gpui::MutableAppContext;
use language::LanguageRegistry;
use project::fs::FakeFs;
use settings::Settings;
use std::sync::Arc;
use theme::ThemeRegistry;
#[cfg(test)]
#[ctor::ctor]
fn init_logger() {
@ -15,32 +5,3 @@ fn init_logger() {
env_logger::init();
}
}
pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
let settings = Settings::test(cx);
editor::init(cx);
cx.set_global(settings);
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
let http = FakeHttpClient::with_404_response();
let client = Client::new(http.clone());
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
let languages = LanguageRegistry::test();
languages.add(Arc::new(language::Language::new(
language::LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
)));
Arc::new(AppState {
themes,
languages: Arc::new(languages),
channel_list: cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx)),
client,
user_store,
fs: FakeFs::new(cx.background().clone()),
build_window_options,
build_workspace,
})
}

View File

@ -31,7 +31,7 @@ use std::{
};
use util::ResultExt;
pub use workspace;
use workspace::{AppState, Workspace, WorkspaceParams};
use workspace::{AppState, Workspace};
actions!(
zed,
@ -139,18 +139,7 @@ pub fn build_workspace(
})
.detach();
let workspace_params = WorkspaceParams {
project,
client: app_state.client.clone(),
fs: app_state.fs.clone(),
languages: app_state.languages.clone(),
themes: app_state.themes.clone(),
user_store: app_state.user_store.clone(),
channel_list: app_state.channel_list.clone(),
};
let workspace = Workspace::new(&workspace_params, cx);
let project = workspace.project().clone();
let workspace = Workspace::new(project.clone(), cx);
let theme_names = app_state.themes.list().collect();
let language_names = app_state.languages.language_names();
@ -313,23 +302,26 @@ mod tests {
use assets::Assets;
use editor::{Autoscroll, DisplayPoint, Editor};
use gpui::{AssetSource, MutableAppContext, TestAppContext, ViewHandle};
use project::{Fs, ProjectPath};
use project::ProjectPath;
use serde_json::json;
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use test::test_app_state;
use theme::{Theme, ThemeRegistry, DEFAULT_THEME_NAME};
use util::test::temp_tree;
use workspace::{
open_paths, pane, Item, ItemHandle, OpenNew, Pane, SplitDirection, WorkspaceHandle,
};
#[gpui::test]
async fn test_open_paths_action(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state);
let dir = temp_tree(json!({
let app_state = init(cx);
app_state
.fs
.as_fake()
.insert_tree(
"/root",
json!({
"a": {
"aa": null,
"ab": null,
@ -342,14 +334,13 @@ mod tests {
"ca": null,
"cb": null,
},
}));
}),
)
.await;
cx.update(|cx| {
open_paths(
&[
dir.path().join("a").to_path_buf(),
dir.path().join("b").to_path_buf(),
],
&[PathBuf::from("/root/a"), PathBuf::from("/root/b")],
&app_state,
cx,
)
@ -357,7 +348,7 @@ mod tests {
.await;
assert_eq!(cx.window_ids().len(), 1);
cx.update(|cx| open_paths(&[dir.path().join("a").to_path_buf()], &app_state, cx))
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
.await;
assert_eq!(cx.window_ids().len(), 1);
let workspace_1 = cx.root_view::<Workspace>(cx.window_ids()[0]).unwrap();
@ -369,10 +360,7 @@ mod tests {
cx.update(|cx| {
open_paths(
&[
dir.path().join("b").to_path_buf(),
dir.path().join("c").to_path_buf(),
],
&[PathBuf::from("/root/b"), PathBuf::from("/root/c")],
&app_state,
cx,
)
@ -383,11 +371,8 @@ mod tests {
#[gpui::test]
async fn test_new_empty_workspace(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state);
cx.update(|cx| {
workspace::init(&app_state.client, cx);
});
cx.dispatch_global_action(workspace::OpenNew(app_state.clone()));
let app_state = init(cx);
cx.dispatch_global_action(workspace::OpenNew);
let window_id = *cx.window_ids().first().unwrap();
let workspace = cx.root_view::<Workspace>(window_id).unwrap();
let editor = workspace.update(cx, |workspace, cx| {
@ -414,7 +399,7 @@ mod tests {
#[gpui::test]
async fn test_open_entry(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state);
let app_state = init(cx);
app_state
.fs
.as_fake()
@ -429,18 +414,10 @@ mod tests {
}),
)
.await;
let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx)
})
.await
.unwrap();
cx.read(|cx| workspace.read(cx).worktree_scans_complete(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 entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone();
let file2 = entries[1].clone();
@ -535,7 +512,8 @@ mod tests {
#[gpui::test]
async fn test_open_paths(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state);
let app_state = init(cx);
let fs = app_state.fs.as_fake();
fs.insert_dir("/dir1").await;
fs.insert_dir("/dir2").await;
@ -544,17 +522,8 @@ mod tests {
fs.insert_file("/dir2/b.txt", "".into()).await;
fs.insert_file("/dir3/c.txt", "".into()).await;
let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/dir1", true, cx)
})
.await
.unwrap();
cx.read(|cx| workspace.read(cx).worktree_scans_complete(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));
// Open a file within an existing worktree.
cx.update(|cx| {
@ -655,19 +624,15 @@ mod tests {
#[gpui::test]
async fn test_save_conflicting_item(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state);
let fs = app_state.fs.as_fake();
fs.insert_tree("/root", json!({ "a.txt": "" })).await;
let app_state = init(cx);
app_state
.fs
.as_fake()
.insert_tree("/root", json!({ "a.txt": "" }))
.await;
let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx)
})
.await
.unwrap();
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));
// Open a file within an existing worktree.
cx.update(|cx| {
@ -687,7 +652,11 @@ mod tests {
editor.handle_input(&editor::Input("x".into()), cx)
})
});
fs.insert_file("/root/a.txt", "changed".to_string()).await;
app_state
.fs
.as_fake()
.insert_file("/root/a.txt", "changed".to_string())
.await;
editor
.condition(&cx, |editor, cx| editor.has_conflict(cx))
.await;
@ -704,21 +673,16 @@ mod tests {
#[gpui::test]
async fn test_open_and_save_new_file(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state);
let app_state = init(cx);
app_state.fs.as_fake().insert_dir("/root").await;
let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx)
})
.await
.unwrap();
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(rust_lang()));
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
// Create a new untitled buffer
cx.dispatch_action(window_id, OpenNew(app_state.clone()));
cx.dispatch_action(window_id, OpenNew);
let editor = workspace.read_with(cx, |workspace, cx| {
workspace
.active_item(cx)
@ -773,18 +737,11 @@ mod tests {
// Open the same newly-created file in another pane item. The new editor should reuse
// the same buffer.
cx.dispatch_action(window_id, OpenNew(app_state.clone()));
cx.dispatch_action(window_id, OpenNew);
workspace
.update(cx, |workspace, cx| {
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
workspace.open_path(
ProjectPath {
worktree_id: worktree.read(cx).id(),
path: Path::new("the-new-name.rs").into(),
},
true,
cx,
)
workspace.open_path((worktree.read(cx).id(), "the-new-name.rs"), true, cx)
})
.await
.unwrap();
@ -805,13 +762,15 @@ mod tests {
#[gpui::test]
async fn test_setting_language_when_saving_as_single_file_worktree(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state);
let app_state = init(cx);
app_state.fs.as_fake().insert_dir("/root").await;
let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
let project = Project::test(app_state.fs.clone(), [], cx).await;
project.update(cx, |project, _| project.languages().add(rust_lang()));
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
// Create a new untitled buffer
cx.dispatch_action(window_id, OpenNew(app_state.clone()));
cx.dispatch_action(window_id, OpenNew);
let editor = workspace.read_with(cx, |workspace, cx| {
workspace
.active_item(cx)
@ -842,10 +801,9 @@ mod tests {
#[gpui::test]
async fn test_pane_actions(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
init(cx);
cx.update(|cx| pane::init(cx));
let app_state = cx.update(test_app_state);
let app_state = cx.update(AppState::test);
app_state
.fs
.as_fake()
@ -861,17 +819,9 @@ mod tests {
)
.await;
let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx)
})
.await
.unwrap();
cx.read(|cx| workspace.read(cx).worktree_scans_complete(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 entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone();
@ -926,7 +876,7 @@ mod tests {
#[gpui::test]
async fn test_navigation(cx: &mut TestAppContext) {
let app_state = cx.update(test_app_state);
let app_state = init(cx);
app_state
.fs
.as_fake()
@ -941,17 +891,10 @@ mod tests {
}),
)
.await;
let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
params
.project
.update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx)
})
.await
.unwrap();
cx.read(|cx| workspace.read(cx).worktree_scans_complete(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 entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone();
let file2 = entries[1].clone();
@ -990,7 +933,7 @@ mod tests {
editor.newline(&Default::default(), cx);
editor.move_down(&Default::default(), cx);
editor.move_down(&Default::default(), cx);
editor.save(params.project.clone(), cx)
editor.save(project.clone(), cx)
})
.await
.unwrap();
@ -1104,7 +1047,6 @@ mod tests {
.unwrap();
app_state
.fs
.as_fake()
.remove_file(Path::new("/root/a/file2"), Default::default())
.await
.unwrap();
@ -1219,4 +1161,29 @@ mod tests {
}
assert!(has_default_theme);
}
fn init(cx: &mut TestAppContext) -> Arc<AppState> {
cx.foreground().forbid_parking();
cx.update(|cx| {
let mut app_state = AppState::test(cx);
let state = Arc::get_mut(&mut app_state).unwrap();
state.build_workspace = build_workspace;
state.build_window_options = build_window_options;
workspace::init(app_state.clone(), cx);
editor::init(cx);
pane::init(cx);
app_state
})
}
fn rust_lang() -> Arc<language::Language> {
Arc::new(language::Language::new(
language::LanguageConfig {
name: "Rust".into(),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
))
}
}