Port to gpui2
This commit is contained in:
parent
e5616bce98
commit
16b5d4b35c
17 changed files with 2585 additions and 355 deletions
|
@ -4,10 +4,12 @@
|
||||||
// use call::ActiveCall;
|
// use call::ActiveCall;
|
||||||
// use collab_ui::notifications::project_shared_notification::ProjectSharedNotification;
|
// use collab_ui::notifications::project_shared_notification::ProjectSharedNotification;
|
||||||
// use editor::{Editor, ExcerptRange, MultiBuffer};
|
// use editor::{Editor, ExcerptRange, MultiBuffer};
|
||||||
// use gpui::{BackgroundExecutor, TestAppContext, View};
|
// use gpui::{point, BackgroundExecutor, TestAppContext, View, VisualTestContext, WindowContext};
|
||||||
// use live_kit_client::MacOSDisplay;
|
// use live_kit_client::MacOSDisplay;
|
||||||
|
// use project::project_settings::ProjectSettings;
|
||||||
// use rpc::proto::PeerId;
|
// use rpc::proto::PeerId;
|
||||||
// use serde_json::json;
|
// use serde_json::json;
|
||||||
|
// use settings::SettingsStore;
|
||||||
// use std::borrow::Cow;
|
// use std::borrow::Cow;
|
||||||
// use workspace::{
|
// use workspace::{
|
||||||
// dock::{test::TestPanel, DockPosition},
|
// dock::{test::TestPanel, DockPosition},
|
||||||
|
@ -24,7 +26,7 @@
|
||||||
// cx_c: &mut TestAppContext,
|
// cx_c: &mut TestAppContext,
|
||||||
// cx_d: &mut TestAppContext,
|
// cx_d: &mut TestAppContext,
|
||||||
// ) {
|
// ) {
|
||||||
// let mut server = TestServer::start(&executor).await;
|
// let mut server = TestServer::start(executor.clone()).await;
|
||||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
// let client_c = server.create_client(cx_c, "user_c").await;
|
// let client_c = server.create_client(cx_c, "user_c").await;
|
||||||
|
@ -71,12 +73,22 @@
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// let window_a = client_a.build_workspace(&project_a, cx_a);
|
// let window_a = client_a.build_workspace(&project_a, cx_a);
|
||||||
// let workspace_a = window_a.root(cx_a);
|
// let workspace_a = window_a.root(cx_a).unwrap();
|
||||||
// let window_b = client_b.build_workspace(&project_b, cx_b);
|
// let window_b = client_b.build_workspace(&project_b, cx_b);
|
||||||
// let workspace_b = window_b.root(cx_b);
|
// let workspace_b = window_b.root(cx_b).unwrap();
|
||||||
|
|
||||||
|
// todo!("could be wrong")
|
||||||
|
// let mut cx_a = VisualTestContext::from_window(*window_a, cx_a);
|
||||||
|
// let cx_a = &mut cx_a;
|
||||||
|
// let mut cx_b = VisualTestContext::from_window(*window_b, cx_b);
|
||||||
|
// let cx_b = &mut cx_b;
|
||||||
|
// let mut cx_c = VisualTestContext::from_window(*window_c, cx_c);
|
||||||
|
// let cx_c = &mut cx_c;
|
||||||
|
// let mut cx_d = VisualTestContext::from_window(*window_d, cx_d);
|
||||||
|
// let cx_d = &mut cx_d;
|
||||||
|
|
||||||
// // Client A opens some editors.
|
// // Client A opens some editors.
|
||||||
// let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
// let pane_a = workspace_a.update(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||||
// let editor_a1 = workspace_a
|
// let editor_a1 = workspace_a
|
||||||
// .update(cx_a, |workspace, cx| {
|
// .update(cx_a, |workspace, cx| {
|
||||||
// workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
// workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
||||||
|
@ -132,8 +144,8 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// cx_c.foreground().run_until_parked();
|
// cx_c.executor().run_until_parked();
|
||||||
// let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
|
// let editor_b2 = workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// workspace
|
// workspace
|
||||||
// .active_item(cx)
|
// .active_item(cx)
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
|
@ -145,19 +157,19 @@
|
||||||
// Some((worktree_id, "2.txt").into())
|
// Some((worktree_id, "2.txt").into())
|
||||||
// );
|
// );
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// editor_b2.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
|
// editor_b2.update(cx_b, |editor, cx| editor.selections.ranges(cx)),
|
||||||
// vec![2..1]
|
// vec![2..1]
|
||||||
// );
|
// );
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// editor_b1.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
|
// editor_b1.update(cx_b, |editor, cx| editor.selections.ranges(cx)),
|
||||||
// vec![3..2]
|
// vec![3..2]
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// cx_c.foreground().run_until_parked();
|
// cx_c.executor().run_until_parked();
|
||||||
// let active_call_c = cx_c.read(ActiveCall::global);
|
// let active_call_c = cx_c.read(ActiveCall::global);
|
||||||
// let project_c = client_c.build_remote_project(project_id, cx_c).await;
|
// let project_c = client_c.build_remote_project(project_id, cx_c).await;
|
||||||
// let window_c = client_c.build_workspace(&project_c, cx_c);
|
// let window_c = client_c.build_workspace(&project_c, cx_c);
|
||||||
// let workspace_c = window_c.root(cx_c);
|
// let workspace_c = window_c.root(cx_c).unwrap();
|
||||||
// active_call_c
|
// active_call_c
|
||||||
// .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
|
// .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
|
||||||
// .await
|
// .await
|
||||||
|
@ -172,10 +184,13 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// cx_d.foreground().run_until_parked();
|
// cx_d.executor().run_until_parked();
|
||||||
// let active_call_d = cx_d.read(ActiveCall::global);
|
// let active_call_d = cx_d.read(ActiveCall::global);
|
||||||
// let project_d = client_d.build_remote_project(project_id, cx_d).await;
|
// let project_d = client_d.build_remote_project(project_id, cx_d).await;
|
||||||
// let workspace_d = client_d.build_workspace(&project_d, cx_d).root(cx_d);
|
// let workspace_d = client_d
|
||||||
|
// .build_workspace(&project_d, cx_d)
|
||||||
|
// .root(cx_d)
|
||||||
|
// .unwrap();
|
||||||
// active_call_d
|
// active_call_d
|
||||||
// .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
|
// .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
|
||||||
// .await
|
// .await
|
||||||
|
@ -183,7 +198,7 @@
|
||||||
// drop(project_d);
|
// drop(project_d);
|
||||||
|
|
||||||
// // All clients see that clients B and C are following client A.
|
// // All clients see that clients B and C are following client A.
|
||||||
// cx_c.foreground().run_until_parked();
|
// cx_c.executor().run_until_parked();
|
||||||
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// followers_by_leader(project_id, cx),
|
// followers_by_leader(project_id, cx),
|
||||||
|
@ -198,7 +213,7 @@
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // All clients see that clients B is following client A.
|
// // All clients see that clients B is following client A.
|
||||||
// cx_c.foreground().run_until_parked();
|
// cx_c.executor().run_until_parked();
|
||||||
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// followers_by_leader(project_id, cx),
|
// followers_by_leader(project_id, cx),
|
||||||
|
@ -216,7 +231,7 @@
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// // All clients see that clients B and C are following client A.
|
// // All clients see that clients B and C are following client A.
|
||||||
// cx_c.foreground().run_until_parked();
|
// cx_c.executor().run_until_parked();
|
||||||
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// followers_by_leader(project_id, cx),
|
// followers_by_leader(project_id, cx),
|
||||||
|
@ -240,7 +255,7 @@
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// // All clients see that D is following C
|
// // All clients see that D is following C
|
||||||
// cx_d.foreground().run_until_parked();
|
// cx_d.executor().run_until_parked();
|
||||||
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// followers_by_leader(project_id, cx),
|
// followers_by_leader(project_id, cx),
|
||||||
|
@ -257,7 +272,7 @@
|
||||||
// cx_c.drop_last(workspace_c);
|
// cx_c.drop_last(workspace_c);
|
||||||
|
|
||||||
// // Clients A and B see that client B is following A, and client C is not present in the followers.
|
// // Clients A and B see that client B is following A, and client C is not present in the followers.
|
||||||
// cx_c.foreground().run_until_parked();
|
// cx_c.executor().run_until_parked();
|
||||||
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
// for (name, cx) in [("A", &cx_a), ("B", &cx_b), ("C", &cx_c), ("D", &cx_d)] {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// followers_by_leader(project_id, cx),
|
// followers_by_leader(project_id, cx),
|
||||||
|
@ -271,12 +286,15 @@
|
||||||
// workspace.activate_item(&editor_a1, cx)
|
// workspace.activate_item(&editor_a1, cx)
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// workspace_b.read_with(cx_b, |workspace, cx| {
|
// workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
|
// assert_eq!(
|
||||||
|
// workspace.active_item(cx).unwrap().item_id(),
|
||||||
|
// editor_b1.item_id()
|
||||||
|
// );
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // When client A opens a multibuffer, client B does so as well.
|
// // When client A opens a multibuffer, client B does so as well.
|
||||||
// let multibuffer_a = cx_a.add_model(|cx| {
|
// let multibuffer_a = cx_a.build_model(|cx| {
|
||||||
// let buffer_a1 = project_a.update(cx, |project, cx| {
|
// let buffer_a1 = project_a.update(cx, |project, cx| {
|
||||||
// project
|
// project
|
||||||
// .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
|
// .get_open_buffer(&(worktree_id, "1.txt").into(), cx)
|
||||||
|
@ -308,12 +326,12 @@
|
||||||
// });
|
// });
|
||||||
// let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
|
// let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
|
||||||
// let editor =
|
// let editor =
|
||||||
// cx.add_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
|
// cx.build_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
|
||||||
// workspace.add_item(Box::new(editor.clone()), cx);
|
// workspace.add_item(Box::new(editor.clone()), cx);
|
||||||
// editor
|
// editor
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// let multibuffer_editor_b = workspace_b.read_with(cx_b, |workspace, cx| {
|
// let multibuffer_editor_b = workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// workspace
|
// workspace
|
||||||
// .active_item(cx)
|
// .active_item(cx)
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
|
@ -321,8 +339,8 @@
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// });
|
// });
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// multibuffer_editor_a.read_with(cx_a, |editor, cx| editor.text(cx)),
|
// multibuffer_editor_a.update(cx_a, |editor, cx| editor.text(cx)),
|
||||||
// multibuffer_editor_b.read_with(cx_b, |editor, cx| editor.text(cx)),
|
// multibuffer_editor_b.update(cx_b, |editor, cx| editor.text(cx)),
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // When client A navigates back and forth, client B does so as well.
|
// // When client A navigates back and forth, client B does so as well.
|
||||||
|
@ -333,8 +351,11 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// workspace_b.read_with(cx_b, |workspace, cx| {
|
// workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
|
// assert_eq!(
|
||||||
|
// workspace.active_item(cx).unwrap().item_id(),
|
||||||
|
// editor_b1.item_id()
|
||||||
|
// );
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// workspace_a
|
// workspace_a
|
||||||
|
@ -344,8 +365,11 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// workspace_b.read_with(cx_b, |workspace, cx| {
|
// workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
|
// assert_eq!(
|
||||||
|
// workspace.active_item(cx).unwrap().item_id(),
|
||||||
|
// editor_b2.item_id()
|
||||||
|
// );
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// workspace_a
|
// workspace_a
|
||||||
|
@ -355,8 +379,11 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// workspace_b.read_with(cx_b, |workspace, cx| {
|
// workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
|
// assert_eq!(
|
||||||
|
// workspace.active_item(cx).unwrap().item_id(),
|
||||||
|
// editor_b1.item_id()
|
||||||
|
// );
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // Changes to client A's editor are reflected on client B.
|
// // Changes to client A's editor are reflected on client B.
|
||||||
|
@ -364,20 +391,20 @@
|
||||||
// editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
|
// editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2]));
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// editor_b1.read_with(cx_b, |editor, cx| {
|
// editor_b1.update(cx_b, |editor, cx| {
|
||||||
// assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
|
// assert_eq!(editor.selections.ranges(cx), &[1..1, 2..2]);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
|
// editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// editor_b1.read_with(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
|
// editor_b1.update(cx_b, |editor, cx| assert_eq!(editor.text(cx), "TWO"));
|
||||||
|
|
||||||
// editor_a1.update(cx_a, |editor, cx| {
|
// editor_a1.update(cx_a, |editor, cx| {
|
||||||
// editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
|
// editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
|
||||||
// editor.set_scroll_position(vec2f(0., 100.), cx);
|
// editor.set_scroll_position(point(0., 100.), cx);
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// editor_b1.read_with(cx_b, |editor, cx| {
|
// editor_b1.update(cx_b, |editor, cx| {
|
||||||
// assert_eq!(editor.selections.ranges(cx), &[3..3]);
|
// assert_eq!(editor.selections.ranges(cx), &[3..3]);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
@ -390,11 +417,11 @@
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, cx| workspace
|
// workspace_b.update(cx_b, |workspace, cx| workspace
|
||||||
// .active_item(cx)
|
// .active_item(cx)
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .id()),
|
// .item_id()),
|
||||||
// editor_b1.id()
|
// editor_b1.item_id()
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // Client A starts following client B.
|
// // Client A starts following client B.
|
||||||
|
@ -405,15 +432,15 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
|
// workspace_a.update(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
|
||||||
// Some(peer_id_b)
|
// Some(peer_id_b)
|
||||||
// );
|
// );
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_a.read_with(cx_a, |workspace, cx| workspace
|
// workspace_a.update(cx_a, |workspace, cx| workspace
|
||||||
// .active_item(cx)
|
// .active_item(cx)
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .id()),
|
// .item_id()),
|
||||||
// editor_a1.id()
|
// editor_a1.item_id()
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
|
// // Client B activates an external window, which causes a new screen-sharing item to be added to the pane.
|
||||||
|
@ -432,7 +459,7 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// let shared_screen = workspace_a.read_with(cx_a, |workspace, cx| {
|
// let shared_screen = workspace_a.update(cx_a, |workspace, cx| {
|
||||||
// workspace
|
// workspace
|
||||||
// .active_item(cx)
|
// .active_item(cx)
|
||||||
// .expect("no active item")
|
// .expect("no active item")
|
||||||
|
@ -446,8 +473,11 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// workspace_a.read_with(cx_a, |workspace, cx| {
|
// workspace_a.update(cx_a, |workspace, cx| {
|
||||||
// assert_eq!(workspace.active_item(cx).unwrap().id(), editor_a1.id())
|
// assert_eq!(
|
||||||
|
// workspace.active_item(cx).unwrap().item_id(),
|
||||||
|
// editor_a1.item_id()
|
||||||
|
// )
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
|
// // Client B activates a multibuffer that was created by following client A. Client A returns to that multibuffer.
|
||||||
|
@ -455,26 +485,26 @@
|
||||||
// workspace.activate_item(&multibuffer_editor_b, cx)
|
// workspace.activate_item(&multibuffer_editor_b, cx)
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// workspace_a.read_with(cx_a, |workspace, cx| {
|
// workspace_a.update(cx_a, |workspace, cx| {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace.active_item(cx).unwrap().id(),
|
// workspace.active_item(cx).unwrap().item_id(),
|
||||||
// multibuffer_editor_a.id()
|
// multibuffer_editor_a.item_id()
|
||||||
// )
|
// )
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // Client B activates a panel, and the previously-opened screen-sharing item gets activated.
|
// // Client B activates a panel, and the previously-opened screen-sharing item gets activated.
|
||||||
// let panel = window_b.add_view(cx_b, |_| TestPanel::new(DockPosition::Left));
|
// let panel = window_b.build_view(cx_b, |_| TestPanel::new(DockPosition::Left));
|
||||||
// workspace_b.update(cx_b, |workspace, cx| {
|
// workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// workspace.add_panel(panel, cx);
|
// workspace.add_panel(panel, cx);
|
||||||
// workspace.toggle_panel_focus::<TestPanel>(cx);
|
// workspace.toggle_panel_focus::<TestPanel>(cx);
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_a.read_with(cx_a, |workspace, cx| workspace
|
// workspace_a.update(cx_a, |workspace, cx| workspace
|
||||||
// .active_item(cx)
|
// .active_item(cx)
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .id()),
|
// .item_id()),
|
||||||
// shared_screen.id()
|
// shared_screen.item_id()
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // Toggling the focus back to the pane causes client A to return to the multibuffer.
|
// // Toggling the focus back to the pane causes client A to return to the multibuffer.
|
||||||
|
@ -482,16 +512,16 @@
|
||||||
// workspace.toggle_panel_focus::<TestPanel>(cx);
|
// workspace.toggle_panel_focus::<TestPanel>(cx);
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// workspace_a.read_with(cx_a, |workspace, cx| {
|
// workspace_a.update(cx_a, |workspace, cx| {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace.active_item(cx).unwrap().id(),
|
// workspace.active_item(cx).unwrap().item_id(),
|
||||||
// multibuffer_editor_a.id()
|
// multibuffer_editor_a.item_id()
|
||||||
// )
|
// )
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // Client B activates an item that doesn't implement following,
|
// // Client B activates an item that doesn't implement following,
|
||||||
// // so the previously-opened screen-sharing item gets activated.
|
// // so the previously-opened screen-sharing item gets activated.
|
||||||
// let unfollowable_item = window_b.add_view(cx_b, |_| TestItem::new());
|
// let unfollowable_item = window_b.build_view(cx_b, |_| TestItem::new());
|
||||||
// workspace_b.update(cx_b, |workspace, cx| {
|
// workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// workspace.active_pane().update(cx, |pane, cx| {
|
// workspace.active_pane().update(cx, |pane, cx| {
|
||||||
// pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
|
// pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
|
||||||
|
@ -499,18 +529,18 @@
|
||||||
// });
|
// });
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_a.read_with(cx_a, |workspace, cx| workspace
|
// workspace_a.update(cx_a, |workspace, cx| workspace
|
||||||
// .active_item(cx)
|
// .active_item(cx)
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .id()),
|
// .item_id()),
|
||||||
// shared_screen.id()
|
// shared_screen.item_id()
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // Following interrupts when client B disconnects.
|
// // Following interrupts when client B disconnects.
|
||||||
// client_b.disconnect(&cx_b.to_async());
|
// client_b.disconnect(&cx_b.to_async());
|
||||||
// executor.advance_clock(RECONNECT_TIMEOUT);
|
// executor.advance_clock(RECONNECT_TIMEOUT);
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
|
// workspace_a.update(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
|
||||||
// None
|
// None
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
@ -521,7 +551,7 @@
|
||||||
// cx_a: &mut TestAppContext,
|
// cx_a: &mut TestAppContext,
|
||||||
// cx_b: &mut TestAppContext,
|
// cx_b: &mut TestAppContext,
|
||||||
// ) {
|
// ) {
|
||||||
// let mut server = TestServer::start(&executor).await;
|
// let mut server = TestServer::start(executor.clone()).await;
|
||||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
// server
|
// server
|
||||||
|
@ -560,13 +590,19 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
// let workspace_a = client_a
|
||||||
// let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
// .build_workspace(&project_a, cx_a)
|
||||||
|
// .root(cx_a)
|
||||||
|
// .unwrap();
|
||||||
|
// let pane_a = workspace_a.update(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||||
|
|
||||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
// let workspace_b = client_b
|
||||||
// let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
// .build_workspace(&project_b, cx_b)
|
||||||
|
// .root(cx_b)
|
||||||
|
// .unwrap();
|
||||||
|
// let pane_b = workspace_b.update(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||||
|
|
||||||
// let client_b_id = project_a.read_with(cx_a, |project, _| {
|
// let client_b_id = project_a.update(cx_a, |project, _| {
|
||||||
// project.collaborators().values().next().unwrap().peer_id
|
// project.collaborators().values().next().unwrap().peer_id
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
@ -584,7 +620,7 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// let pane_paths = |pane: &ViewHandle<workspace::Pane>, cx: &mut TestAppContext| {
|
// let pane_paths = |pane: &View<workspace::Pane>, cx: &mut TestAppContext| {
|
||||||
// pane.update(cx, |pane, cx| {
|
// pane.update(cx, |pane, cx| {
|
||||||
// pane.items()
|
// pane.items()
|
||||||
// .map(|item| {
|
// .map(|item| {
|
||||||
|
@ -642,7 +678,7 @@
|
||||||
// cx_a: &mut TestAppContext,
|
// cx_a: &mut TestAppContext,
|
||||||
// cx_b: &mut TestAppContext,
|
// cx_b: &mut TestAppContext,
|
||||||
// ) {
|
// ) {
|
||||||
// let mut server = TestServer::start(&executor).await;
|
// let mut server = TestServer::start(executor.clone()).await;
|
||||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
// server
|
// server
|
||||||
|
@ -685,7 +721,10 @@
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// // Client A opens a file.
|
// // Client A opens a file.
|
||||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
// let workspace_a = client_a
|
||||||
|
// .build_workspace(&project_a, cx_a)
|
||||||
|
// .root(cx_a)
|
||||||
|
// .unwrap();
|
||||||
// workspace_a
|
// workspace_a
|
||||||
// .update(cx_a, |workspace, cx| {
|
// .update(cx_a, |workspace, cx| {
|
||||||
// workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
// workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
||||||
|
@ -696,7 +735,10 @@
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// // Client B opens a different file.
|
// // Client B opens a different file.
|
||||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
// let workspace_b = client_b
|
||||||
|
// .build_workspace(&project_b, cx_b)
|
||||||
|
// .root(cx_b)
|
||||||
|
// .unwrap();
|
||||||
// workspace_b
|
// workspace_b
|
||||||
// .update(cx_b, |workspace, cx| {
|
// .update(cx_b, |workspace, cx| {
|
||||||
// workspace.open_path((worktree_id, "2.txt"), None, true, cx)
|
// workspace.open_path((worktree_id, "2.txt"), None, true, cx)
|
||||||
|
@ -1167,7 +1209,7 @@
|
||||||
// cx_b: &mut TestAppContext,
|
// cx_b: &mut TestAppContext,
|
||||||
// ) {
|
// ) {
|
||||||
// // 2 clients connect to a server.
|
// // 2 clients connect to a server.
|
||||||
// let mut server = TestServer::start(&executor).await;
|
// let mut server = TestServer::start(executor.clone()).await;
|
||||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
// server
|
// server
|
||||||
|
@ -1207,8 +1249,17 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
|
// todo!("could be wrong")
|
||||||
|
// let mut cx_a = VisualTestContext::from_window(*window_a, cx_a);
|
||||||
|
// let cx_a = &mut cx_a;
|
||||||
|
// let mut cx_b = VisualTestContext::from_window(*window_b, cx_b);
|
||||||
|
// let cx_b = &mut cx_b;
|
||||||
|
|
||||||
// // Client A opens some editors.
|
// // Client A opens some editors.
|
||||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
// let workspace_a = client_a
|
||||||
|
// .build_workspace(&project_a, cx_a)
|
||||||
|
// .root(cx_a)
|
||||||
|
// .unwrap();
|
||||||
// let _editor_a1 = workspace_a
|
// let _editor_a1 = workspace_a
|
||||||
// .update(cx_a, |workspace, cx| {
|
// .update(cx_a, |workspace, cx| {
|
||||||
// workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
// workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
||||||
|
@ -1219,9 +1270,12 @@
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// // Client B starts following client A.
|
// // Client B starts following client A.
|
||||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
// let workspace_b = client_b
|
||||||
// let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
// .build_workspace(&project_b, cx_b)
|
||||||
// let leader_id = project_b.read_with(cx_b, |project, _| {
|
// .root(cx_b)
|
||||||
|
// .unwrap();
|
||||||
|
// let pane_b = workspace_b.update(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||||
|
// let leader_id = project_b.update(cx_b, |project, _| {
|
||||||
// project.collaborators().values().next().unwrap().peer_id
|
// project.collaborators().values().next().unwrap().peer_id
|
||||||
// });
|
// });
|
||||||
// workspace_b
|
// workspace_b
|
||||||
|
@ -1231,10 +1285,10 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// Some(leader_id)
|
// Some(leader_id)
|
||||||
// );
|
// );
|
||||||
// let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
|
// let editor_b2 = workspace_b.update(cx_b, |workspace, cx| {
|
||||||
// workspace
|
// workspace
|
||||||
// .active_item(cx)
|
// .active_item(cx)
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
|
@ -1245,7 +1299,7 @@
|
||||||
// // When client B moves, it automatically stops following client A.
|
// // When client B moves, it automatically stops following client A.
|
||||||
// editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
|
// editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// None
|
// None
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
@ -1256,14 +1310,14 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// Some(leader_id)
|
// Some(leader_id)
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // When client B edits, it automatically stops following client A.
|
// // When client B edits, it automatically stops following client A.
|
||||||
// editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
|
// editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// None
|
// None
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
@ -1274,16 +1328,16 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// Some(leader_id)
|
// Some(leader_id)
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// // When client B scrolls, it automatically stops following client A.
|
// // When client B scrolls, it automatically stops following client A.
|
||||||
// editor_b2.update(cx_b, |editor, cx| {
|
// editor_b2.update(cx_b, |editor, cx| {
|
||||||
// editor.set_scroll_position(vec2f(0., 3.), cx)
|
// editor.set_scroll_position(point(0., 3.), cx)
|
||||||
// });
|
// });
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// None
|
// None
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
@ -1294,7 +1348,7 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// Some(leader_id)
|
// Some(leader_id)
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
@ -1303,13 +1357,13 @@
|
||||||
// workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx)
|
// workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx)
|
||||||
// });
|
// });
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// Some(leader_id)
|
// Some(leader_id)
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
|
// workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// Some(leader_id)
|
// Some(leader_id)
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
@ -1321,7 +1375,7 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
// workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
|
||||||
// None
|
// None
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
@ -1332,7 +1386,7 @@
|
||||||
// cx_a: &mut TestAppContext,
|
// cx_a: &mut TestAppContext,
|
||||||
// cx_b: &mut TestAppContext,
|
// cx_b: &mut TestAppContext,
|
||||||
// ) {
|
// ) {
|
||||||
// let mut server = TestServer::start(&executor).await;
|
// let mut server = TestServer::start(executor.clone()).await;
|
||||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
// server
|
// server
|
||||||
|
@ -1345,20 +1399,26 @@
|
||||||
|
|
||||||
// client_a.fs().insert_tree("/a", json!({})).await;
|
// client_a.fs().insert_tree("/a", json!({})).await;
|
||||||
// let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
|
// let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
|
||||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
// let workspace_a = client_a
|
||||||
|
// .build_workspace(&project_a, cx_a)
|
||||||
|
// .root(cx_a)
|
||||||
|
// .unwrap();
|
||||||
// let project_id = active_call_a
|
// let project_id = active_call_a
|
||||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
// let workspace_b = client_b
|
||||||
|
// .build_workspace(&project_b, cx_b)
|
||||||
|
// .root(cx_b)
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
// executor.run_until_parked();
|
// executor.run_until_parked();
|
||||||
// let client_a_id = project_b.read_with(cx_b, |project, _| {
|
// let client_a_id = project_b.update(cx_b, |project, _| {
|
||||||
// project.collaborators().values().next().unwrap().peer_id
|
// project.collaborators().values().next().unwrap().peer_id
|
||||||
// });
|
// });
|
||||||
// let client_b_id = project_a.read_with(cx_a, |project, _| {
|
// let client_b_id = project_a.update(cx_a, |project, _| {
|
||||||
// project.collaborators().values().next().unwrap().peer_id
|
// project.collaborators().values().next().unwrap().peer_id
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
@ -1370,13 +1430,13 @@
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// futures::try_join!(a_follow_b, b_follow_a).unwrap();
|
// futures::try_join!(a_follow_b, b_follow_a).unwrap();
|
||||||
// workspace_a.read_with(cx_a, |workspace, _| {
|
// workspace_a.update(cx_a, |workspace, _| {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace.leader_for_pane(workspace.active_pane()),
|
// workspace.leader_for_pane(workspace.active_pane()),
|
||||||
// Some(client_b_id)
|
// Some(client_b_id)
|
||||||
// );
|
// );
|
||||||
// });
|
// });
|
||||||
// workspace_b.read_with(cx_b, |workspace, _| {
|
// workspace_b.update(cx_b, |workspace, _| {
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// workspace.leader_for_pane(workspace.active_pane()),
|
// workspace.leader_for_pane(workspace.active_pane()),
|
||||||
// Some(client_a_id)
|
// Some(client_a_id)
|
||||||
|
@ -1398,7 +1458,7 @@
|
||||||
// // b opens a different file in project 2, a follows b
|
// // b opens a different file in project 2, a follows b
|
||||||
// // b opens a different file in project 1, a cannot follow b
|
// // b opens a different file in project 1, a cannot follow b
|
||||||
// // b shares the project, a joins the project and follows b
|
// // b shares the project, a joins the project and follows b
|
||||||
// let mut server = TestServer::start(&executor).await;
|
// let mut server = TestServer::start(executor.clone()).await;
|
||||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
// cx_a.update(editor::init);
|
// cx_a.update(editor::init);
|
||||||
|
@ -1435,8 +1495,14 @@
|
||||||
// let (project_a, worktree_id_a) = client_a.build_local_project("/a", cx_a).await;
|
// let (project_a, worktree_id_a) = client_a.build_local_project("/a", cx_a).await;
|
||||||
// let (project_b, worktree_id_b) = client_b.build_local_project("/b", cx_b).await;
|
// let (project_b, worktree_id_b) = client_b.build_local_project("/b", cx_b).await;
|
||||||
|
|
||||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
// let workspace_a = client_a
|
||||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
// .build_workspace(&project_a, cx_a)
|
||||||
|
// .root(cx_a)
|
||||||
|
// .unwrap();
|
||||||
|
// let workspace_b = client_b
|
||||||
|
// .build_workspace(&project_b, cx_b)
|
||||||
|
// .root(cx_b)
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
// cx_a.update(|cx| collab_ui::init(&client_a.app_state, cx));
|
// cx_a.update(|cx| collab_ui::init(&client_a.app_state, cx));
|
||||||
// cx_b.update(|cx| collab_ui::init(&client_b.app_state, cx));
|
// cx_b.update(|cx| collab_ui::init(&client_b.app_state, cx));
|
||||||
|
@ -1455,6 +1521,12 @@
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
|
// todo!("could be wrong")
|
||||||
|
// let mut cx_a = VisualTestContext::from_window(*window_a, cx_a);
|
||||||
|
// let cx_a = &mut cx_a;
|
||||||
|
// let mut cx_b = VisualTestContext::from_window(*window_b, cx_b);
|
||||||
|
// let cx_b = &mut cx_b;
|
||||||
|
|
||||||
// workspace_a
|
// workspace_a
|
||||||
// .update(cx_a, |workspace, cx| {
|
// .update(cx_a, |workspace, cx| {
|
||||||
// workspace.open_path((worktree_id_a, "w.rs"), None, true, cx)
|
// workspace.open_path((worktree_id_a, "w.rs"), None, true, cx)
|
||||||
|
@ -1476,11 +1548,12 @@
|
||||||
// let workspace_b_project_a = cx_b
|
// let workspace_b_project_a = cx_b
|
||||||
// .windows()
|
// .windows()
|
||||||
// .iter()
|
// .iter()
|
||||||
// .max_by_key(|window| window.id())
|
// .max_by_key(|window| window.item_id())
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .downcast::<Workspace>()
|
// .downcast::<Workspace>()
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .root(cx_b);
|
// .root(cx_b)
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
// // assert that b is following a in project a in w.rs
|
// // assert that b is following a in project a in w.rs
|
||||||
// workspace_b_project_a.update(cx_b, |workspace, cx| {
|
// workspace_b_project_a.update(cx_b, |workspace, cx| {
|
||||||
|
@ -1534,7 +1607,7 @@
|
||||||
// workspace.leader_for_pane(workspace.active_pane())
|
// workspace.leader_for_pane(workspace.active_pane())
|
||||||
// );
|
// );
|
||||||
// let item = workspace.active_pane().read(cx).active_item().unwrap();
|
// let item = workspace.active_pane().read(cx).active_item().unwrap();
|
||||||
// assert_eq!(item.tab_description(0, cx).unwrap(), Cow::Borrowed("x.rs"));
|
// assert_eq!(item.tab_description(0, cx).unwrap(), "x.rs".into());
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // b moves to y.rs in b's project, a is still following but can't yet see
|
// // b moves to y.rs in b's project, a is still following but can't yet see
|
||||||
|
@ -1578,11 +1651,12 @@
|
||||||
// let workspace_a_project_b = cx_a
|
// let workspace_a_project_b = cx_a
|
||||||
// .windows()
|
// .windows()
|
||||||
// .iter()
|
// .iter()
|
||||||
// .max_by_key(|window| window.id())
|
// .max_by_key(|window| window.item_id())
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .downcast::<Workspace>()
|
// .downcast::<Workspace>()
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .root(cx_a);
|
// .root(cx_a)
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
// workspace_a_project_b.update(cx_a, |workspace, cx| {
|
// workspace_a_project_b.update(cx_a, |workspace, cx| {
|
||||||
// assert_eq!(workspace.project().read(cx).remote_id(), Some(project_b_id));
|
// assert_eq!(workspace.project().read(cx).remote_id(), Some(project_b_id));
|
||||||
|
@ -1596,12 +1670,151 @@
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// #[gpui::test]
|
||||||
|
// async fn test_following_into_excluded_file(
|
||||||
|
// executor: BackgroundExecutor,
|
||||||
|
// mut cx_a: &mut TestAppContext,
|
||||||
|
// mut cx_b: &mut TestAppContext,
|
||||||
|
// ) {
|
||||||
|
// let mut server = TestServer::start(executor.clone()).await;
|
||||||
|
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
|
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
|
// for cx in [&mut cx_a, &mut cx_b] {
|
||||||
|
// cx.update(|cx| {
|
||||||
|
// cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
|
// store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
||||||
|
// project_settings.file_scan_exclusions = Some(vec!["**/.git".to_string()]);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// server
|
||||||
|
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||||
|
// .await;
|
||||||
|
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||||
|
// let active_call_b = cx_b.read(ActiveCall::global);
|
||||||
|
|
||||||
|
// cx_a.update(editor::init);
|
||||||
|
// cx_b.update(editor::init);
|
||||||
|
|
||||||
|
// client_a
|
||||||
|
// .fs()
|
||||||
|
// .insert_tree(
|
||||||
|
// "/a",
|
||||||
|
// json!({
|
||||||
|
// ".git": {
|
||||||
|
// "COMMIT_EDITMSG": "write your commit message here",
|
||||||
|
// },
|
||||||
|
// "1.txt": "one\none\none",
|
||||||
|
// "2.txt": "two\ntwo\ntwo",
|
||||||
|
// "3.txt": "three\nthree\nthree",
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
// .await;
|
||||||
|
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||||
|
// active_call_a
|
||||||
|
// .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// let project_id = active_call_a
|
||||||
|
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||||
|
// active_call_b
|
||||||
|
// .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// let window_a = client_a.build_workspace(&project_a, cx_a);
|
||||||
|
// let workspace_a = window_a.root(cx_a).unwrap();
|
||||||
|
// let peer_id_a = client_a.peer_id().unwrap();
|
||||||
|
// let window_b = client_b.build_workspace(&project_b, cx_b);
|
||||||
|
// let workspace_b = window_b.root(cx_b).unwrap();
|
||||||
|
|
||||||
|
// todo!("could be wrong")
|
||||||
|
// let mut cx_a = VisualTestContext::from_window(*window_a, cx_a);
|
||||||
|
// let cx_a = &mut cx_a;
|
||||||
|
// let mut cx_b = VisualTestContext::from_window(*window_b, cx_b);
|
||||||
|
// let cx_b = &mut cx_b;
|
||||||
|
|
||||||
|
// // Client A opens editors for a regular file and an excluded file.
|
||||||
|
// let editor_for_regular = workspace_a
|
||||||
|
// .update(cx_a, |workspace, cx| {
|
||||||
|
// workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
||||||
|
// })
|
||||||
|
// .await
|
||||||
|
// .unwrap()
|
||||||
|
// .downcast::<Editor>()
|
||||||
|
// .unwrap();
|
||||||
|
// let editor_for_excluded_a = workspace_a
|
||||||
|
// .update(cx_a, |workspace, cx| {
|
||||||
|
// workspace.open_path((worktree_id, ".git/COMMIT_EDITMSG"), None, true, cx)
|
||||||
|
// })
|
||||||
|
// .await
|
||||||
|
// .unwrap()
|
||||||
|
// .downcast::<Editor>()
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// // Client A updates their selections in those editors
|
||||||
|
// editor_for_regular.update(cx_a, |editor, cx| {
|
||||||
|
// editor.handle_input("a", cx);
|
||||||
|
// editor.handle_input("b", cx);
|
||||||
|
// editor.handle_input("c", cx);
|
||||||
|
// editor.select_left(&Default::default(), cx);
|
||||||
|
// assert_eq!(editor.selections.ranges(cx), vec![3..2]);
|
||||||
|
// });
|
||||||
|
// editor_for_excluded_a.update(cx_a, |editor, cx| {
|
||||||
|
// editor.select_all(&Default::default(), cx);
|
||||||
|
// editor.handle_input("new commit message", cx);
|
||||||
|
// editor.select_left(&Default::default(), cx);
|
||||||
|
// assert_eq!(editor.selections.ranges(cx), vec![18..17]);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // When client B starts following client A, currently visible file is replicated
|
||||||
|
// workspace_b
|
||||||
|
// .update(cx_b, |workspace, cx| {
|
||||||
|
// workspace.follow(peer_id_a, cx).unwrap()
|
||||||
|
// })
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// let editor_for_excluded_b = workspace_b.update(cx_b, |workspace, cx| {
|
||||||
|
// workspace
|
||||||
|
// .active_item(cx)
|
||||||
|
// .unwrap()
|
||||||
|
// .downcast::<Editor>()
|
||||||
|
// .unwrap()
|
||||||
|
// });
|
||||||
|
// assert_eq!(
|
||||||
|
// cx_b.read(|cx| editor_for_excluded_b.project_path(cx)),
|
||||||
|
// Some((worktree_id, ".git/COMMIT_EDITMSG").into())
|
||||||
|
// );
|
||||||
|
// assert_eq!(
|
||||||
|
// editor_for_excluded_b.update(cx_b, |editor, cx| editor.selections.ranges(cx)),
|
||||||
|
// vec![18..17]
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Changes from B to the excluded file are replicated in A's editor
|
||||||
|
// editor_for_excluded_b.update(cx_b, |editor, cx| {
|
||||||
|
// editor.handle_input("\nCo-Authored-By: B <b@b.b>", cx);
|
||||||
|
// });
|
||||||
|
// executor.run_until_parked();
|
||||||
|
// editor_for_excluded_a.update(cx_a, |editor, cx| {
|
||||||
|
// assert_eq!(
|
||||||
|
// editor.text(cx),
|
||||||
|
// "new commit messag\nCo-Authored-By: B <b@b.b>"
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
// fn visible_push_notifications(
|
// fn visible_push_notifications(
|
||||||
// cx: &mut TestAppContext,
|
// cx: &mut TestAppContext,
|
||||||
// ) -> Vec<gpui::ViewHandle<ProjectSharedNotification>> {
|
// ) -> Vec<gpui::View<ProjectSharedNotification>> {
|
||||||
// let mut ret = Vec::new();
|
// let mut ret = Vec::new();
|
||||||
// for window in cx.windows() {
|
// for window in cx.windows() {
|
||||||
// window.read_with(cx, |window| {
|
// window.update(cx, |window| {
|
||||||
// if let Some(handle) = window
|
// if let Some(handle) = window
|
||||||
// .root_view()
|
// .root_view()
|
||||||
// .clone()
|
// .clone()
|
||||||
|
@ -1645,8 +1858,8 @@
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// fn pane_summaries(workspace: &ViewHandle<Workspace>, cx: &mut TestAppContext) -> Vec<PaneSummary> {
|
// fn pane_summaries(workspace: &View<Workspace>, cx: &mut WindowContext<'_>) -> Vec<PaneSummary> {
|
||||||
// workspace.read_with(cx, |workspace, cx| {
|
// workspace.update(cx, |workspace, cx| {
|
||||||
// let active_pane = workspace.active_pane();
|
// let active_pane = workspace.active_pane();
|
||||||
// workspace
|
// workspace
|
||||||
// .panes()
|
// .panes()
|
||||||
|
|
|
@ -2781,11 +2781,10 @@ async fn test_fs_operations(
|
||||||
|
|
||||||
let entry = project_b
|
let entry = project_b
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project
|
project.create_entry((worktree_id, "c.txt"), false, cx)
|
||||||
.create_entry((worktree_id, "c.txt"), false, cx)
|
|
||||||
.unwrap()
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
worktree_a.read_with(cx_a, |worktree, _| {
|
worktree_a.read_with(cx_a, |worktree, _| {
|
||||||
|
@ -2812,8 +2811,8 @@ async fn test_fs_operations(
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project.rename_entry(entry.id, Path::new("d.txt"), cx)
|
project.rename_entry(entry.id, Path::new("d.txt"), cx)
|
||||||
})
|
})
|
||||||
.unwrap()
|
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
worktree_a.read_with(cx_a, |worktree, _| {
|
worktree_a.read_with(cx_a, |worktree, _| {
|
||||||
|
@ -2838,11 +2837,10 @@ async fn test_fs_operations(
|
||||||
|
|
||||||
let dir_entry = project_b
|
let dir_entry = project_b
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project
|
project.create_entry((worktree_id, "DIR"), true, cx)
|
||||||
.create_entry((worktree_id, "DIR"), true, cx)
|
|
||||||
.unwrap()
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
worktree_a.read_with(cx_a, |worktree, _| {
|
worktree_a.read_with(cx_a, |worktree, _| {
|
||||||
|
@ -2867,27 +2865,24 @@ async fn test_fs_operations(
|
||||||
|
|
||||||
project_b
|
project_b
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project
|
project.create_entry((worktree_id, "DIR/e.txt"), false, cx)
|
||||||
.create_entry((worktree_id, "DIR/e.txt"), false, cx)
|
|
||||||
.unwrap()
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
project_b
|
project_b
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project
|
project.create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
|
||||||
.create_entry((worktree_id, "DIR/SUBDIR"), true, cx)
|
|
||||||
.unwrap()
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
project_b
|
project_b
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project
|
project.create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
|
||||||
.create_entry((worktree_id, "DIR/SUBDIR/f.txt"), false, cx)
|
|
||||||
.unwrap()
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
worktree_a.read_with(cx_a, |worktree, _| {
|
worktree_a.read_with(cx_a, |worktree, _| {
|
||||||
|
@ -2928,11 +2923,10 @@ async fn test_fs_operations(
|
||||||
|
|
||||||
project_b
|
project_b
|
||||||
.update(cx_b, |project, cx| {
|
.update(cx_b, |project, cx| {
|
||||||
project
|
project.copy_entry(entry.id, Path::new("f.txt"), cx)
|
||||||
.copy_entry(entry.id, Path::new("f.txt"), cx)
|
|
||||||
.unwrap()
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
worktree_a.read_with(cx_a, |worktree, _| {
|
worktree_a.read_with(cx_a, |worktree, _| {
|
||||||
|
|
|
@ -665,7 +665,6 @@ impl RandomizedTest for ProjectCollaborationTest {
|
||||||
ensure_project_shared(&project, client, cx).await;
|
ensure_project_shared(&project, client, cx).await;
|
||||||
project
|
project
|
||||||
.update(cx, |p, cx| p.create_entry(project_path, is_dir, cx))
|
.update(cx, |p, cx| p.create_entry(project_path, is_dir, cx))
|
||||||
.unwrap()
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5812,8 +5812,9 @@ impl Project {
|
||||||
}
|
}
|
||||||
} else if !fs_metadata.is_symlink {
|
} else if !fs_metadata.is_symlink {
|
||||||
if !query.file_matches(Some(&ignored_abs_path))
|
if !query.file_matches(Some(&ignored_abs_path))
|
||||||
|| snapshot
|
|| snapshot.is_path_excluded(
|
||||||
.is_path_excluded(ignored_abs_path.clone())
|
ignored_entry.path.to_path_buf(),
|
||||||
|
)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1327,7 +1327,7 @@ impl LocalWorktree {
|
||||||
old_path: Option<Arc<Path>>,
|
old_path: Option<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Task<Result<Option<Entry>>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
if self.is_path_excluded(self.absolutize(&path)) {
|
if self.is_path_excluded(path.to_path_buf()) {
|
||||||
return Task::ready(Ok(None));
|
return Task::ready(Ok(None));
|
||||||
}
|
}
|
||||||
let paths = if let Some(old_path) = old_path.as_ref() {
|
let paths = if let Some(old_path) = old_path.as_ref() {
|
||||||
|
@ -2521,7 +2521,7 @@ impl BackgroundScannerState {
|
||||||
ids_to_preserve.insert(work_directory_id);
|
ids_to_preserve.insert(work_directory_id);
|
||||||
} else {
|
} else {
|
||||||
let git_dir_abs_path = snapshot.abs_path().join(&entry.git_dir_path);
|
let git_dir_abs_path = snapshot.abs_path().join(&entry.git_dir_path);
|
||||||
let git_dir_excluded = snapshot.is_path_excluded(git_dir_abs_path.clone());
|
let git_dir_excluded = snapshot.is_path_excluded(entry.git_dir_path.to_path_buf());
|
||||||
if git_dir_excluded
|
if git_dir_excluded
|
||||||
&& !matches!(smol::block_on(fs.metadata(&git_dir_abs_path)), Ok(None))
|
&& !matches!(smol::block_on(fs.metadata(&git_dir_abs_path)), Ok(None))
|
||||||
{
|
{
|
||||||
|
@ -3400,7 +3400,7 @@ impl BackgroundScanner {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if snapshot.is_path_excluded(abs_path.clone()) {
|
if snapshot.is_path_excluded(relative_path.to_path_buf()) {
|
||||||
if !is_git_related {
|
if !is_git_related {
|
||||||
log::debug!("ignoring FS event for excluded path {relative_path:?}");
|
log::debug!("ignoring FS event for excluded path {relative_path:?}");
|
||||||
}
|
}
|
||||||
|
@ -3584,7 +3584,7 @@ impl BackgroundScanner {
|
||||||
let state = self.state.lock();
|
let state = self.state.lock();
|
||||||
let snapshot = &state.snapshot;
|
let snapshot = &state.snapshot;
|
||||||
root_abs_path = snapshot.abs_path().clone();
|
root_abs_path = snapshot.abs_path().clone();
|
||||||
if snapshot.is_path_excluded(job.abs_path.to_path_buf()) {
|
if snapshot.is_path_excluded(job.path.to_path_buf()) {
|
||||||
log::error!("skipping excluded directory {:?}", job.path);
|
log::error!("skipping excluded directory {:?}", job.path);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -3656,11 +3656,8 @@ impl BackgroundScanner {
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
if state
|
let relative_path = job.path.join(child_name);
|
||||||
.snapshot
|
if state.snapshot.is_path_excluded(relative_path.clone()) {
|
||||||
.is_path_excluded(child_abs_path.to_path_buf())
|
|
||||||
{
|
|
||||||
let relative_path = job.path.join(child_name);
|
|
||||||
log::debug!("skipping excluded child entry {relative_path:?}");
|
log::debug!("skipping excluded child entry {relative_path:?}");
|
||||||
state.remove_path(&relative_path);
|
state.remove_path(&relative_path);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1151,20 +1151,22 @@ impl Project {
|
||||||
project_path: impl Into<ProjectPath>,
|
project_path: impl Into<ProjectPath>,
|
||||||
is_directory: bool,
|
is_directory: bool,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Option<Task<Result<Entry>>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
let project_path = project_path.into();
|
let project_path = project_path.into();
|
||||||
let worktree = self.worktree_for_id(project_path.worktree_id, cx)?;
|
let Some(worktree) = self.worktree_for_id(project_path.worktree_id, cx) else {
|
||||||
|
return Task::ready(Ok(None));
|
||||||
|
};
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
Some(worktree.update(cx, |worktree, cx| {
|
worktree.update(cx, |worktree, cx| {
|
||||||
worktree
|
worktree
|
||||||
.as_local_mut()
|
.as_local_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.create_entry(project_path.path, is_directory, cx)
|
.create_entry(project_path.path, is_directory, cx)
|
||||||
}))
|
})
|
||||||
} else {
|
} else {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
let project_id = self.remote_id().unwrap();
|
let project_id = self.remote_id().unwrap();
|
||||||
Some(cx.spawn(move |_, mut cx| async move {
|
cx.spawn(move |_, mut cx| async move {
|
||||||
let response = client
|
let response = client
|
||||||
.request(proto::CreateProjectEntry {
|
.request(proto::CreateProjectEntry {
|
||||||
worktree_id: project_path.worktree_id.to_proto(),
|
worktree_id: project_path.worktree_id.to_proto(),
|
||||||
|
@ -1173,19 +1175,20 @@ impl Project {
|
||||||
is_directory,
|
is_directory,
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
let entry = response
|
match response.entry {
|
||||||
.entry
|
Some(entry) => worktree
|
||||||
.ok_or_else(|| anyhow!("missing entry in response"))?;
|
.update(&mut cx, |worktree, cx| {
|
||||||
worktree
|
worktree.as_remote_mut().unwrap().insert_entry(
|
||||||
.update(&mut cx, |worktree, cx| {
|
entry,
|
||||||
worktree.as_remote_mut().unwrap().insert_entry(
|
response.worktree_scan_id as usize,
|
||||||
entry,
|
cx,
|
||||||
response.worktree_scan_id as usize,
|
)
|
||||||
cx,
|
})?
|
||||||
)
|
.await
|
||||||
})?
|
.map(Some),
|
||||||
.await
|
None => Ok(None),
|
||||||
}))
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1194,8 +1197,10 @@ impl Project {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
new_path: impl Into<Arc<Path>>,
|
new_path: impl Into<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Option<Task<Result<Entry>>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
let worktree = self.worktree_for_entry(entry_id, cx)?;
|
let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
|
||||||
|
return Task::ready(Ok(None));
|
||||||
|
};
|
||||||
let new_path = new_path.into();
|
let new_path = new_path.into();
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
worktree.update(cx, |worktree, cx| {
|
worktree.update(cx, |worktree, cx| {
|
||||||
|
@ -1208,7 +1213,7 @@ impl Project {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
let project_id = self.remote_id().unwrap();
|
let project_id = self.remote_id().unwrap();
|
||||||
|
|
||||||
Some(cx.spawn(move |_, mut cx| async move {
|
cx.spawn(move |_, mut cx| async move {
|
||||||
let response = client
|
let response = client
|
||||||
.request(proto::CopyProjectEntry {
|
.request(proto::CopyProjectEntry {
|
||||||
project_id,
|
project_id,
|
||||||
|
@ -1216,19 +1221,20 @@ impl Project {
|
||||||
new_path: new_path.to_string_lossy().into(),
|
new_path: new_path.to_string_lossy().into(),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
let entry = response
|
match response.entry {
|
||||||
.entry
|
Some(entry) => worktree
|
||||||
.ok_or_else(|| anyhow!("missing entry in response"))?;
|
.update(&mut cx, |worktree, cx| {
|
||||||
worktree
|
worktree.as_remote_mut().unwrap().insert_entry(
|
||||||
.update(&mut cx, |worktree, cx| {
|
entry,
|
||||||
worktree.as_remote_mut().unwrap().insert_entry(
|
response.worktree_scan_id as usize,
|
||||||
entry,
|
cx,
|
||||||
response.worktree_scan_id as usize,
|
)
|
||||||
cx,
|
})?
|
||||||
)
|
.await
|
||||||
})?
|
.map(Some),
|
||||||
.await
|
None => Ok(None),
|
||||||
}))
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,8 +1243,10 @@ impl Project {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
new_path: impl Into<Arc<Path>>,
|
new_path: impl Into<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Option<Task<Result<Entry>>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
let worktree = self.worktree_for_entry(entry_id, cx)?;
|
let Some(worktree) = self.worktree_for_entry(entry_id, cx) else {
|
||||||
|
return Task::ready(Ok(None));
|
||||||
|
};
|
||||||
let new_path = new_path.into();
|
let new_path = new_path.into();
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
worktree.update(cx, |worktree, cx| {
|
worktree.update(cx, |worktree, cx| {
|
||||||
|
@ -1251,7 +1259,7 @@ impl Project {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
let project_id = self.remote_id().unwrap();
|
let project_id = self.remote_id().unwrap();
|
||||||
|
|
||||||
Some(cx.spawn(move |_, mut cx| async move {
|
cx.spawn(move |_, mut cx| async move {
|
||||||
let response = client
|
let response = client
|
||||||
.request(proto::RenameProjectEntry {
|
.request(proto::RenameProjectEntry {
|
||||||
project_id,
|
project_id,
|
||||||
|
@ -1259,19 +1267,20 @@ impl Project {
|
||||||
new_path: new_path.to_string_lossy().into(),
|
new_path: new_path.to_string_lossy().into(),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
let entry = response
|
match response.entry {
|
||||||
.entry
|
Some(entry) => worktree
|
||||||
.ok_or_else(|| anyhow!("missing entry in response"))?;
|
.update(&mut cx, |worktree, cx| {
|
||||||
worktree
|
worktree.as_remote_mut().unwrap().insert_entry(
|
||||||
.update(&mut cx, |worktree, cx| {
|
entry,
|
||||||
worktree.as_remote_mut().unwrap().insert_entry(
|
response.worktree_scan_id as usize,
|
||||||
entry,
|
cx,
|
||||||
response.worktree_scan_id as usize,
|
)
|
||||||
cx,
|
})?
|
||||||
)
|
.await
|
||||||
})?
|
.map(Some),
|
||||||
.await
|
None => Ok(None),
|
||||||
}))
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1688,18 +1697,15 @@ impl Project {
|
||||||
|
|
||||||
pub fn open_path(
|
pub fn open_path(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: impl Into<ProjectPath>,
|
path: ProjectPath,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<(ProjectEntryId, AnyModel)>> {
|
) -> Task<Result<(Option<ProjectEntryId>, AnyModel)>> {
|
||||||
let project_path = path.into();
|
let task = self.open_buffer(path.clone(), cx);
|
||||||
let task = self.open_buffer(project_path.clone(), cx);
|
cx.spawn(move |_, cx| async move {
|
||||||
cx.spawn(move |_, mut cx| async move {
|
|
||||||
let buffer = task.await?;
|
let buffer = task.await?;
|
||||||
let project_entry_id = buffer
|
let project_entry_id = buffer.read_with(&cx, |buffer, cx| {
|
||||||
.update(&mut cx, |buffer, cx| {
|
File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
|
||||||
File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
|
})?;
|
||||||
})?
|
|
||||||
.with_context(|| format!("no project entry for {project_path:?}"))?;
|
|
||||||
|
|
||||||
let buffer: &AnyModel = &buffer;
|
let buffer: &AnyModel = &buffer;
|
||||||
Ok((project_entry_id, buffer.clone()))
|
Ok((project_entry_id, buffer.clone()))
|
||||||
|
@ -2018,8 +2024,10 @@ impl Project {
|
||||||
remote_id,
|
remote_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.local_buffer_ids_by_entry_id
|
if let Some(entry_id) = file.entry_id {
|
||||||
.insert(file.entry_id, remote_id);
|
self.local_buffer_ids_by_entry_id
|
||||||
|
.insert(entry_id, remote_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2474,24 +2482,25 @@ impl Project {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.local_buffer_ids_by_entry_id.get(&file.entry_id) {
|
let remote_id = buffer.read(cx).remote_id();
|
||||||
Some(_) => {
|
if let Some(entry_id) = file.entry_id {
|
||||||
return None;
|
match self.local_buffer_ids_by_entry_id.get(&entry_id) {
|
||||||
|
Some(_) => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.local_buffer_ids_by_entry_id
|
||||||
|
.insert(entry_id, remote_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
};
|
||||||
let remote_id = buffer.read(cx).remote_id();
|
self.local_buffer_ids_by_path.insert(
|
||||||
self.local_buffer_ids_by_entry_id
|
ProjectPath {
|
||||||
.insert(file.entry_id, remote_id);
|
worktree_id: file.worktree_id(cx),
|
||||||
|
path: file.path.clone(),
|
||||||
self.local_buffer_ids_by_path.insert(
|
},
|
||||||
ProjectPath {
|
remote_id,
|
||||||
worktree_id: file.worktree_id(cx),
|
);
|
||||||
path: file.path.clone(),
|
|
||||||
},
|
|
||||||
remote_id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -5845,11 +5854,6 @@ impl Project {
|
||||||
while let Some(ignored_abs_path) =
|
while let Some(ignored_abs_path) =
|
||||||
ignored_paths_to_process.pop_front()
|
ignored_paths_to_process.pop_front()
|
||||||
{
|
{
|
||||||
if !query.file_matches(Some(&ignored_abs_path))
|
|
||||||
|| snapshot.is_path_excluded(&ignored_abs_path)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some(fs_metadata) = fs
|
if let Some(fs_metadata) = fs
|
||||||
.metadata(&ignored_abs_path)
|
.metadata(&ignored_abs_path)
|
||||||
.await
|
.await
|
||||||
|
@ -5877,6 +5881,13 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if !fs_metadata.is_symlink {
|
} else if !fs_metadata.is_symlink {
|
||||||
|
if !query.file_matches(Some(&ignored_abs_path))
|
||||||
|
|| snapshot.is_path_excluded(
|
||||||
|
ignored_entry.path.to_path_buf(),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let matches = if let Some(file) = fs
|
let matches = if let Some(file) = fs
|
||||||
.open_sync(&ignored_abs_path)
|
.open_sync(&ignored_abs_path)
|
||||||
.await
|
.await
|
||||||
|
@ -6278,10 +6289,13 @@ impl Project {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_file = if let Some(entry) = snapshot.entry_for_id(old_file.entry_id) {
|
let new_file = if let Some(entry) = old_file
|
||||||
|
.entry_id
|
||||||
|
.and_then(|entry_id| snapshot.entry_for_id(entry_id))
|
||||||
|
{
|
||||||
File {
|
File {
|
||||||
is_local: true,
|
is_local: true,
|
||||||
entry_id: entry.id,
|
entry_id: Some(entry.id),
|
||||||
mtime: entry.mtime,
|
mtime: entry.mtime,
|
||||||
path: entry.path.clone(),
|
path: entry.path.clone(),
|
||||||
worktree: worktree_handle.clone(),
|
worktree: worktree_handle.clone(),
|
||||||
|
@ -6290,7 +6304,7 @@ impl Project {
|
||||||
} else if let Some(entry) = snapshot.entry_for_path(old_file.path().as_ref()) {
|
} else if let Some(entry) = snapshot.entry_for_path(old_file.path().as_ref()) {
|
||||||
File {
|
File {
|
||||||
is_local: true,
|
is_local: true,
|
||||||
entry_id: entry.id,
|
entry_id: Some(entry.id),
|
||||||
mtime: entry.mtime,
|
mtime: entry.mtime,
|
||||||
path: entry.path.clone(),
|
path: entry.path.clone(),
|
||||||
worktree: worktree_handle.clone(),
|
worktree: worktree_handle.clone(),
|
||||||
|
@ -6320,10 +6334,12 @@ impl Project {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_file.entry_id != *entry_id {
|
if new_file.entry_id != Some(*entry_id) {
|
||||||
self.local_buffer_ids_by_entry_id.remove(entry_id);
|
self.local_buffer_ids_by_entry_id.remove(entry_id);
|
||||||
self.local_buffer_ids_by_entry_id
|
if let Some(entry_id) = new_file.entry_id {
|
||||||
.insert(new_file.entry_id, buffer_id);
|
self.local_buffer_ids_by_entry_id
|
||||||
|
.insert(entry_id, buffer_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_file != *old_file {
|
if new_file != *old_file {
|
||||||
|
@ -6890,7 +6906,7 @@ impl Project {
|
||||||
})?
|
})?
|
||||||
.await?;
|
.await?;
|
||||||
Ok(proto::ProjectEntryResponse {
|
Ok(proto::ProjectEntryResponse {
|
||||||
entry: Some((&entry).into()),
|
entry: entry.as_ref().map(|e| e.into()),
|
||||||
worktree_scan_id: worktree_scan_id as u64,
|
worktree_scan_id: worktree_scan_id as u64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6914,11 +6930,10 @@ impl Project {
|
||||||
.as_local_mut()
|
.as_local_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.rename_entry(entry_id, new_path, cx)
|
.rename_entry(entry_id, new_path, cx)
|
||||||
.ok_or_else(|| anyhow!("invalid entry"))
|
})?
|
||||||
})??
|
|
||||||
.await?;
|
.await?;
|
||||||
Ok(proto::ProjectEntryResponse {
|
Ok(proto::ProjectEntryResponse {
|
||||||
entry: Some((&entry).into()),
|
entry: entry.as_ref().map(|e| e.into()),
|
||||||
worktree_scan_id: worktree_scan_id as u64,
|
worktree_scan_id: worktree_scan_id as u64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6942,11 +6957,10 @@ impl Project {
|
||||||
.as_local_mut()
|
.as_local_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.copy_entry(entry_id, new_path, cx)
|
.copy_entry(entry_id, new_path, cx)
|
||||||
.ok_or_else(|| anyhow!("invalid entry"))
|
})?
|
||||||
})??
|
|
||||||
.await?;
|
.await?;
|
||||||
Ok(proto::ProjectEntryResponse {
|
Ok(proto::ProjectEntryResponse {
|
||||||
entry: Some((&entry).into()),
|
entry: entry.as_ref().map(|e| e.into()),
|
||||||
worktree_scan_id: worktree_scan_id as u64,
|
worktree_scan_id: worktree_scan_id as u64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4182,6 +4182,94 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) {
|
||||||
|
init_test(cx);
|
||||||
|
|
||||||
|
let fs = FakeFs::new(cx.background_executor.clone());
|
||||||
|
fs.insert_tree(
|
||||||
|
"/dir",
|
||||||
|
json!({
|
||||||
|
".git": {},
|
||||||
|
".gitignore": "**/target\n/node_modules\n",
|
||||||
|
"target": {
|
||||||
|
"index.txt": "index_key:index_value"
|
||||||
|
},
|
||||||
|
"node_modules": {
|
||||||
|
"eslint": {
|
||||||
|
"index.ts": "const eslint_key = 'eslint value'",
|
||||||
|
"package.json": r#"{ "some_key": "some value" }"#,
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"index.ts": "const prettier_key = 'prettier value'",
|
||||||
|
"package.json": r#"{ "other_key": "other value" }"#,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"package.json": r#"{ "main_key": "main value" }"#,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||||
|
|
||||||
|
let query = "key";
|
||||||
|
assert_eq!(
|
||||||
|
search(
|
||||||
|
&project,
|
||||||
|
SearchQuery::text(query, false, false, false, Vec::new(), Vec::new()).unwrap(),
|
||||||
|
cx
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
HashMap::from_iter([("package.json".to_string(), vec![8..11])]),
|
||||||
|
"Only one non-ignored file should have the query"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
search(
|
||||||
|
&project,
|
||||||
|
SearchQuery::text(query, false, false, true, Vec::new(), Vec::new()).unwrap(),
|
||||||
|
cx
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
HashMap::from_iter([
|
||||||
|
("package.json".to_string(), vec![8..11]),
|
||||||
|
("target/index.txt".to_string(), vec![6..9]),
|
||||||
|
(
|
||||||
|
"node_modules/prettier/package.json".to_string(),
|
||||||
|
vec![9..12]
|
||||||
|
),
|
||||||
|
("node_modules/prettier/index.ts".to_string(), vec![15..18]),
|
||||||
|
("node_modules/eslint/index.ts".to_string(), vec![13..16]),
|
||||||
|
("node_modules/eslint/package.json".to_string(), vec![8..11]),
|
||||||
|
]),
|
||||||
|
"Unrestricted search with ignored directories should find every file with the query"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
search(
|
||||||
|
&project,
|
||||||
|
SearchQuery::text(
|
||||||
|
query,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
vec![PathMatcher::new("node_modules/prettier/**").unwrap()],
|
||||||
|
vec![PathMatcher::new("*.ts").unwrap()],
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
cx
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
HashMap::from_iter([(
|
||||||
|
"node_modules/prettier/package.json".to_string(),
|
||||||
|
vec![9..12]
|
||||||
|
)]),
|
||||||
|
"With search including ignored prettier directory and excluding TS files, only one file should be found"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_glob_literal_prefix() {
|
fn test_glob_literal_prefix() {
|
||||||
assert_eq!(glob_literal_prefix("**/*.js"), "");
|
assert_eq!(glob_literal_prefix("**/*.js"), "");
|
||||||
|
|
|
@ -371,15 +371,25 @@ impl SearchQuery {
|
||||||
pub fn file_matches(&self, file_path: Option<&Path>) -> bool {
|
pub fn file_matches(&self, file_path: Option<&Path>) -> bool {
|
||||||
match file_path {
|
match file_path {
|
||||||
Some(file_path) => {
|
Some(file_path) => {
|
||||||
!self
|
let mut path = file_path.to_path_buf();
|
||||||
.files_to_exclude()
|
loop {
|
||||||
.iter()
|
if self
|
||||||
.any(|exclude_glob| exclude_glob.is_match(file_path))
|
.files_to_exclude()
|
||||||
&& (self.files_to_include().is_empty()
|
.iter()
|
||||||
|
.any(|exclude_glob| exclude_glob.is_match(&path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
} else if self.files_to_include().is_empty()
|
||||||
|| self
|
|| self
|
||||||
.files_to_include()
|
.files_to_include()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|include_glob| include_glob.is_match(file_path)))
|
.any(|include_glob| include_glob.is_match(&path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
} else if !path.pop() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => self.files_to_include().is_empty(),
|
None => self.files_to_include().is_empty(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -958,8 +958,6 @@ impl LocalWorktree {
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let text = fs.load(&abs_path).await?;
|
let text = fs.load(&abs_path).await?;
|
||||||
let entry = entry.await?;
|
|
||||||
|
|
||||||
let mut index_task = None;
|
let mut index_task = None;
|
||||||
let snapshot = this.update(&mut cx, |this, _| this.as_local().unwrap().snapshot())?;
|
let snapshot = this.update(&mut cx, |this, _| this.as_local().unwrap().snapshot())?;
|
||||||
if let Some(repo) = snapshot.repository_for_path(&path) {
|
if let Some(repo) = snapshot.repository_for_path(&path) {
|
||||||
|
@ -982,18 +980,43 @@ impl LocalWorktree {
|
||||||
let worktree = this
|
let worktree = this
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.ok_or_else(|| anyhow!("worktree was dropped"))?;
|
.ok_or_else(|| anyhow!("worktree was dropped"))?;
|
||||||
Ok((
|
match entry.await? {
|
||||||
File {
|
Some(entry) => Ok((
|
||||||
entry_id: entry.id,
|
File {
|
||||||
worktree,
|
entry_id: Some(entry.id),
|
||||||
path: entry.path,
|
worktree,
|
||||||
mtime: entry.mtime,
|
path: entry.path,
|
||||||
is_local: true,
|
mtime: entry.mtime,
|
||||||
is_deleted: false,
|
is_local: true,
|
||||||
},
|
is_deleted: false,
|
||||||
text,
|
},
|
||||||
diff_base,
|
text,
|
||||||
))
|
diff_base,
|
||||||
|
)),
|
||||||
|
None => {
|
||||||
|
let metadata = fs
|
||||||
|
.metadata(&abs_path)
|
||||||
|
.await
|
||||||
|
.with_context(|| {
|
||||||
|
format!("Loading metadata for excluded file {abs_path:?}")
|
||||||
|
})?
|
||||||
|
.with_context(|| {
|
||||||
|
format!("Excluded file {abs_path:?} got removed during loading")
|
||||||
|
})?;
|
||||||
|
Ok((
|
||||||
|
File {
|
||||||
|
entry_id: None,
|
||||||
|
worktree,
|
||||||
|
path,
|
||||||
|
mtime: metadata.mtime,
|
||||||
|
is_local: true,
|
||||||
|
is_deleted: false,
|
||||||
|
},
|
||||||
|
text,
|
||||||
|
diff_base,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1013,18 +1036,38 @@ impl LocalWorktree {
|
||||||
let text = buffer.as_rope().clone();
|
let text = buffer.as_rope().clone();
|
||||||
let fingerprint = text.fingerprint();
|
let fingerprint = text.fingerprint();
|
||||||
let version = buffer.version();
|
let version = buffer.version();
|
||||||
let save = self.write_file(path, text, buffer.line_ending(), cx);
|
let save = self.write_file(path.as_ref(), text, buffer.line_ending(), cx);
|
||||||
|
let fs = Arc::clone(&self.fs);
|
||||||
|
let abs_path = self.absolutize(&path);
|
||||||
|
|
||||||
cx.spawn(move |this, mut cx| async move {
|
cx.spawn(move |this, mut cx| async move {
|
||||||
let entry = save.await?;
|
let entry = save.await?;
|
||||||
let this = this.upgrade().context("worktree dropped")?;
|
let this = this.upgrade().context("worktree dropped")?;
|
||||||
|
|
||||||
|
let (entry_id, mtime, path) = match entry {
|
||||||
|
Some(entry) => (Some(entry.id), entry.mtime, entry.path),
|
||||||
|
None => {
|
||||||
|
let metadata = fs
|
||||||
|
.metadata(&abs_path)
|
||||||
|
.await
|
||||||
|
.with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Fetching metadata after saving the excluded buffer {abs_path:?}"
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.with_context(|| {
|
||||||
|
format!("Excluded buffer {path:?} got removed during saving")
|
||||||
|
})?;
|
||||||
|
(None, metadata.mtime, path)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if has_changed_file {
|
if has_changed_file {
|
||||||
let new_file = Arc::new(File {
|
let new_file = Arc::new(File {
|
||||||
entry_id: entry.id,
|
entry_id,
|
||||||
worktree: this,
|
worktree: this,
|
||||||
path: entry.path,
|
path,
|
||||||
mtime: entry.mtime,
|
mtime,
|
||||||
is_local: true,
|
is_local: true,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
});
|
});
|
||||||
|
@ -1050,13 +1093,13 @@ impl LocalWorktree {
|
||||||
project_id,
|
project_id,
|
||||||
buffer_id,
|
buffer_id,
|
||||||
version: serialize_version(&version),
|
version: serialize_version(&version),
|
||||||
mtime: Some(entry.mtime.into()),
|
mtime: Some(mtime.into()),
|
||||||
fingerprint: serialize_fingerprint(fingerprint),
|
fingerprint: serialize_fingerprint(fingerprint),
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_handle.update(&mut cx, |buffer, cx| {
|
buffer_handle.update(&mut cx, |buffer, cx| {
|
||||||
buffer.did_save(version.clone(), fingerprint, entry.mtime, cx);
|
buffer.did_save(version.clone(), fingerprint, mtime, cx);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1081,7 +1124,7 @@ impl LocalWorktree {
|
||||||
path: impl Into<Arc<Path>>,
|
path: impl Into<Arc<Path>>,
|
||||||
is_dir: bool,
|
is_dir: bool,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Task<Result<Entry>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let lowest_ancestor = self.lowest_ancestor(&path);
|
let lowest_ancestor = self.lowest_ancestor(&path);
|
||||||
let abs_path = self.absolutize(&path);
|
let abs_path = self.absolutize(&path);
|
||||||
|
@ -1098,7 +1141,7 @@ impl LocalWorktree {
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
write.await?;
|
write.await?;
|
||||||
let (result, refreshes) = this.update(&mut cx, |this, cx| {
|
let (result, refreshes) = this.update(&mut cx, |this, cx| {
|
||||||
let mut refreshes = Vec::<Task<anyhow::Result<Entry>>>::new();
|
let mut refreshes = Vec::new();
|
||||||
let refresh_paths = path.strip_prefix(&lowest_ancestor).unwrap();
|
let refresh_paths = path.strip_prefix(&lowest_ancestor).unwrap();
|
||||||
for refresh_path in refresh_paths.ancestors() {
|
for refresh_path in refresh_paths.ancestors() {
|
||||||
if refresh_path == Path::new("") {
|
if refresh_path == Path::new("") {
|
||||||
|
@ -1125,14 +1168,14 @@ impl LocalWorktree {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_file(
|
pub(crate) fn write_file(
|
||||||
&self,
|
&self,
|
||||||
path: impl Into<Arc<Path>>,
|
path: impl Into<Arc<Path>>,
|
||||||
text: Rope,
|
text: Rope,
|
||||||
line_ending: LineEnding,
|
line_ending: LineEnding,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Task<Result<Entry>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
let path = path.into();
|
let path: Arc<Path> = path.into();
|
||||||
let abs_path = self.absolutize(&path);
|
let abs_path = self.absolutize(&path);
|
||||||
let fs = self.fs.clone();
|
let fs = self.fs.clone();
|
||||||
let write = cx
|
let write = cx
|
||||||
|
@ -1191,8 +1234,11 @@ impl LocalWorktree {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
new_path: impl Into<Arc<Path>>,
|
new_path: impl Into<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Option<Task<Result<Entry>>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
let old_path = self.entry_for_id(entry_id)?.path.clone();
|
let old_path = match self.entry_for_id(entry_id) {
|
||||||
|
Some(entry) => entry.path.clone(),
|
||||||
|
None => return Task::ready(Ok(None)),
|
||||||
|
};
|
||||||
let new_path = new_path.into();
|
let new_path = new_path.into();
|
||||||
let abs_old_path = self.absolutize(&old_path);
|
let abs_old_path = self.absolutize(&old_path);
|
||||||
let abs_new_path = self.absolutize(&new_path);
|
let abs_new_path = self.absolutize(&new_path);
|
||||||
|
@ -1202,7 +1248,7 @@ impl LocalWorktree {
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
rename.await?;
|
rename.await?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.as_local_mut()
|
this.as_local_mut()
|
||||||
|
@ -1210,7 +1256,7 @@ impl LocalWorktree {
|
||||||
.refresh_entry(new_path.clone(), Some(old_path), cx)
|
.refresh_entry(new_path.clone(), Some(old_path), cx)
|
||||||
})?
|
})?
|
||||||
.await
|
.await
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_entry(
|
pub fn copy_entry(
|
||||||
|
@ -1218,8 +1264,11 @@ impl LocalWorktree {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
new_path: impl Into<Arc<Path>>,
|
new_path: impl Into<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Option<Task<Result<Entry>>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
let old_path = self.entry_for_id(entry_id)?.path.clone();
|
let old_path = match self.entry_for_id(entry_id) {
|
||||||
|
Some(entry) => entry.path.clone(),
|
||||||
|
None => return Task::ready(Ok(None)),
|
||||||
|
};
|
||||||
let new_path = new_path.into();
|
let new_path = new_path.into();
|
||||||
let abs_old_path = self.absolutize(&old_path);
|
let abs_old_path = self.absolutize(&old_path);
|
||||||
let abs_new_path = self.absolutize(&new_path);
|
let abs_new_path = self.absolutize(&new_path);
|
||||||
|
@ -1234,7 +1283,7 @@ impl LocalWorktree {
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
copy.await?;
|
copy.await?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.as_local_mut()
|
this.as_local_mut()
|
||||||
|
@ -1242,7 +1291,7 @@ impl LocalWorktree {
|
||||||
.refresh_entry(new_path.clone(), None, cx)
|
.refresh_entry(new_path.clone(), None, cx)
|
||||||
})?
|
})?
|
||||||
.await
|
.await
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_entry(
|
pub fn expand_entry(
|
||||||
|
@ -1278,7 +1327,10 @@ impl LocalWorktree {
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
old_path: Option<Arc<Path>>,
|
old_path: Option<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Task<Result<Entry>> {
|
) -> Task<Result<Option<Entry>>> {
|
||||||
|
if self.is_path_excluded(path.to_path_buf()) {
|
||||||
|
return Task::ready(Ok(None));
|
||||||
|
}
|
||||||
let paths = if let Some(old_path) = old_path.as_ref() {
|
let paths = if let Some(old_path) = old_path.as_ref() {
|
||||||
vec![old_path.clone(), path.clone()]
|
vec![old_path.clone(), path.clone()]
|
||||||
} else {
|
} else {
|
||||||
|
@ -1287,11 +1339,12 @@ impl LocalWorktree {
|
||||||
let mut refresh = self.refresh_entries_for_paths(paths);
|
let mut refresh = self.refresh_entries_for_paths(paths);
|
||||||
cx.spawn(move |this, mut cx| async move {
|
cx.spawn(move |this, mut cx| async move {
|
||||||
refresh.recv().await;
|
refresh.recv().await;
|
||||||
this.update(&mut cx, |this, _| {
|
let new_entry = this.update(&mut cx, |this, _| {
|
||||||
this.entry_for_path(path)
|
this.entry_for_path(path)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| anyhow!("failed to read path after update"))
|
.ok_or_else(|| anyhow!("failed to read path after update"))
|
||||||
})?
|
})??;
|
||||||
|
Ok(Some(new_entry))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2222,10 +2275,19 @@ impl LocalSnapshot {
|
||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_path_excluded(&self, abs_path: &Path) -> bool {
|
pub fn is_path_excluded(&self, mut path: PathBuf) -> bool {
|
||||||
self.file_scan_exclusions
|
loop {
|
||||||
.iter()
|
if self
|
||||||
.any(|exclude_matcher| exclude_matcher.is_match(abs_path))
|
.file_scan_exclusions
|
||||||
|
.iter()
|
||||||
|
.any(|exclude_matcher| exclude_matcher.is_match(&path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if !path.pop() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2455,8 +2517,7 @@ impl BackgroundScannerState {
|
||||||
ids_to_preserve.insert(work_directory_id);
|
ids_to_preserve.insert(work_directory_id);
|
||||||
} else {
|
} else {
|
||||||
let git_dir_abs_path = snapshot.abs_path().join(&entry.git_dir_path);
|
let git_dir_abs_path = snapshot.abs_path().join(&entry.git_dir_path);
|
||||||
let git_dir_excluded = snapshot.is_path_excluded(&entry.git_dir_path)
|
let git_dir_excluded = snapshot.is_path_excluded(entry.git_dir_path.to_path_buf());
|
||||||
|| snapshot.is_path_excluded(&git_dir_abs_path);
|
|
||||||
if git_dir_excluded
|
if git_dir_excluded
|
||||||
&& !matches!(smol::block_on(fs.metadata(&git_dir_abs_path)), Ok(None))
|
&& !matches!(smol::block_on(fs.metadata(&git_dir_abs_path)), Ok(None))
|
||||||
{
|
{
|
||||||
|
@ -2663,7 +2724,7 @@ pub struct File {
|
||||||
pub worktree: Model<Worktree>,
|
pub worktree: Model<Worktree>,
|
||||||
pub path: Arc<Path>,
|
pub path: Arc<Path>,
|
||||||
pub mtime: SystemTime,
|
pub mtime: SystemTime,
|
||||||
pub(crate) entry_id: ProjectEntryId,
|
pub(crate) entry_id: Option<ProjectEntryId>,
|
||||||
pub(crate) is_local: bool,
|
pub(crate) is_local: bool,
|
||||||
pub(crate) is_deleted: bool,
|
pub(crate) is_deleted: bool,
|
||||||
}
|
}
|
||||||
|
@ -2732,7 +2793,7 @@ impl language::File for File {
|
||||||
fn to_proto(&self) -> rpc::proto::File {
|
fn to_proto(&self) -> rpc::proto::File {
|
||||||
rpc::proto::File {
|
rpc::proto::File {
|
||||||
worktree_id: self.worktree.entity_id().as_u64(),
|
worktree_id: self.worktree.entity_id().as_u64(),
|
||||||
entry_id: self.entry_id.to_proto(),
|
entry_id: self.entry_id.map(|id| id.to_proto()),
|
||||||
path: self.path.to_string_lossy().into(),
|
path: self.path.to_string_lossy().into(),
|
||||||
mtime: Some(self.mtime.into()),
|
mtime: Some(self.mtime.into()),
|
||||||
is_deleted: self.is_deleted,
|
is_deleted: self.is_deleted,
|
||||||
|
@ -2790,7 +2851,7 @@ impl File {
|
||||||
worktree,
|
worktree,
|
||||||
path: entry.path.clone(),
|
path: entry.path.clone(),
|
||||||
mtime: entry.mtime,
|
mtime: entry.mtime,
|
||||||
entry_id: entry.id,
|
entry_id: Some(entry.id),
|
||||||
is_local: true,
|
is_local: true,
|
||||||
is_deleted: false,
|
is_deleted: false,
|
||||||
})
|
})
|
||||||
|
@ -2815,7 +2876,7 @@ impl File {
|
||||||
worktree,
|
worktree,
|
||||||
path: Path::new(&proto.path).into(),
|
path: Path::new(&proto.path).into(),
|
||||||
mtime: proto.mtime.ok_or_else(|| anyhow!("no timestamp"))?.into(),
|
mtime: proto.mtime.ok_or_else(|| anyhow!("no timestamp"))?.into(),
|
||||||
entry_id: ProjectEntryId::from_proto(proto.entry_id),
|
entry_id: proto.entry_id.map(ProjectEntryId::from_proto),
|
||||||
is_local: false,
|
is_local: false,
|
||||||
is_deleted: proto.is_deleted,
|
is_deleted: proto.is_deleted,
|
||||||
})
|
})
|
||||||
|
@ -2833,7 +2894,7 @@ impl File {
|
||||||
if self.is_deleted {
|
if self.is_deleted {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(self.entry_id)
|
self.entry_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3329,16 +3390,7 @@ impl BackgroundScanner {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FS events may come for files which parent directory is excluded, need to check ignore those.
|
if snapshot.is_path_excluded(relative_path.to_path_buf()) {
|
||||||
let mut path_to_test = abs_path.clone();
|
|
||||||
let mut excluded_file_event = snapshot.is_path_excluded(abs_path)
|
|
||||||
|| snapshot.is_path_excluded(&relative_path);
|
|
||||||
while !excluded_file_event && path_to_test.pop() {
|
|
||||||
if snapshot.is_path_excluded(&path_to_test) {
|
|
||||||
excluded_file_event = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if excluded_file_event {
|
|
||||||
if !is_git_related {
|
if !is_git_related {
|
||||||
log::debug!("ignoring FS event for excluded path {relative_path:?}");
|
log::debug!("ignoring FS event for excluded path {relative_path:?}");
|
||||||
}
|
}
|
||||||
|
@ -3522,7 +3574,7 @@ impl BackgroundScanner {
|
||||||
let state = self.state.lock();
|
let state = self.state.lock();
|
||||||
let snapshot = &state.snapshot;
|
let snapshot = &state.snapshot;
|
||||||
root_abs_path = snapshot.abs_path().clone();
|
root_abs_path = snapshot.abs_path().clone();
|
||||||
if snapshot.is_path_excluded(&job.abs_path) {
|
if snapshot.is_path_excluded(job.path.to_path_buf()) {
|
||||||
log::error!("skipping excluded directory {:?}", job.path);
|
log::error!("skipping excluded directory {:?}", job.path);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -3593,9 +3645,9 @@ impl BackgroundScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
let relative_path = job.path.join(child_name);
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
if state.snapshot.is_path_excluded(&child_abs_path) {
|
if state.snapshot.is_path_excluded(relative_path.clone()) {
|
||||||
let relative_path = job.path.join(child_name);
|
|
||||||
log::debug!("skipping excluded child entry {relative_path:?}");
|
log::debug!("skipping excluded child entry {relative_path:?}");
|
||||||
state.remove_path(&relative_path);
|
state.remove_path(&relative_path);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1055,11 +1055,12 @@ async fn test_fs_events_in_exclusions(cx: &mut TestAppContext) {
|
||||||
&[
|
&[
|
||||||
".git/HEAD",
|
".git/HEAD",
|
||||||
".git/foo",
|
".git/foo",
|
||||||
|
"node_modules",
|
||||||
"node_modules/.DS_Store",
|
"node_modules/.DS_Store",
|
||||||
"node_modules/prettier",
|
"node_modules/prettier",
|
||||||
"node_modules/prettier/package.json",
|
"node_modules/prettier/package.json",
|
||||||
],
|
],
|
||||||
&["target", "node_modules"],
|
&["target"],
|
||||||
&[
|
&[
|
||||||
".DS_Store",
|
".DS_Store",
|
||||||
"src/.DS_Store",
|
"src/.DS_Store",
|
||||||
|
@ -1109,6 +1110,7 @@ async fn test_fs_events_in_exclusions(cx: &mut TestAppContext) {
|
||||||
".git/HEAD",
|
".git/HEAD",
|
||||||
".git/foo",
|
".git/foo",
|
||||||
".git/new_file",
|
".git/new_file",
|
||||||
|
"node_modules",
|
||||||
"node_modules/.DS_Store",
|
"node_modules/.DS_Store",
|
||||||
"node_modules/prettier",
|
"node_modules/prettier",
|
||||||
"node_modules/prettier/package.json",
|
"node_modules/prettier/package.json",
|
||||||
|
@ -1117,7 +1119,7 @@ async fn test_fs_events_in_exclusions(cx: &mut TestAppContext) {
|
||||||
"build_output/new_file",
|
"build_output/new_file",
|
||||||
"test_output/new_file",
|
"test_output/new_file",
|
||||||
],
|
],
|
||||||
&["target", "node_modules", "test_output"],
|
&["target", "test_output"],
|
||||||
&[
|
&[
|
||||||
".DS_Store",
|
".DS_Store",
|
||||||
"src/.DS_Store",
|
"src/.DS_Store",
|
||||||
|
@ -1177,6 +1179,7 @@ async fn test_create_directory_during_initial_scan(cx: &mut TestAppContext) {
|
||||||
.create_entry("a/e".as_ref(), true, cx)
|
.create_entry("a/e".as_ref(), true, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(entry.is_dir());
|
assert!(entry.is_dir());
|
||||||
|
|
||||||
|
@ -1226,6 +1229,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
|
||||||
.create_entry("a/b/c/d.txt".as_ref(), false, cx)
|
.create_entry("a/b/c/d.txt".as_ref(), false, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(entry.is_file());
|
assert!(entry.is_file());
|
||||||
|
|
||||||
|
@ -1261,6 +1265,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
|
||||||
.create_entry("a/b/c/d.txt".as_ref(), false, cx)
|
.create_entry("a/b/c/d.txt".as_ref(), false, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(entry.is_file());
|
assert!(entry.is_file());
|
||||||
|
|
||||||
|
@ -1279,6 +1284,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
|
||||||
.create_entry("a/b/c/e.txt".as_ref(), false, cx)
|
.create_entry("a/b/c/e.txt".as_ref(), false, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(entry.is_file());
|
assert!(entry.is_file());
|
||||||
|
|
||||||
|
@ -1295,6 +1301,7 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) {
|
||||||
.create_entry("d/e/f/g.txt".as_ref(), false, cx)
|
.create_entry("d/e/f/g.txt".as_ref(), false, cx)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(entry.is_file());
|
assert!(entry.is_file());
|
||||||
|
|
||||||
|
@ -1620,14 +1627,14 @@ fn randomly_mutate_worktree(
|
||||||
entry.id.0,
|
entry.id.0,
|
||||||
new_path
|
new_path
|
||||||
);
|
);
|
||||||
let task = worktree.rename_entry(entry.id, new_path, cx).unwrap();
|
let task = worktree.rename_entry(entry.id, new_path, cx);
|
||||||
cx.background_executor().spawn(async move {
|
cx.background_executor().spawn(async move {
|
||||||
task.await?;
|
task.await?.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let task = if entry.is_dir() {
|
if entry.is_dir() {
|
||||||
let child_path = entry.path.join(random_filename(rng));
|
let child_path = entry.path.join(random_filename(rng));
|
||||||
let is_dir = rng.gen_bool(0.3);
|
let is_dir = rng.gen_bool(0.3);
|
||||||
log::info!(
|
log::info!(
|
||||||
|
@ -1635,15 +1642,20 @@ fn randomly_mutate_worktree(
|
||||||
if is_dir { "dir" } else { "file" },
|
if is_dir { "dir" } else { "file" },
|
||||||
child_path,
|
child_path,
|
||||||
);
|
);
|
||||||
worktree.create_entry(child_path, is_dir, cx)
|
let task = worktree.create_entry(child_path, is_dir, cx);
|
||||||
|
cx.background_executor().spawn(async move {
|
||||||
|
task.await?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
log::info!("overwriting file {:?} ({})", entry.path, entry.id.0);
|
log::info!("overwriting file {:?} ({})", entry.path, entry.id.0);
|
||||||
worktree.write_file(entry.path.clone(), "".into(), Default::default(), cx)
|
let task =
|
||||||
};
|
worktree.write_file(entry.path.clone(), "".into(), Default::default(), cx);
|
||||||
cx.background_executor().spawn(async move {
|
cx.background_executor().spawn(async move {
|
||||||
task.await?;
|
task.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -610,7 +610,7 @@ impl ProjectPanel {
|
||||||
edited_entry_id = NEW_ENTRY_ID;
|
edited_entry_id = NEW_ENTRY_ID;
|
||||||
edit_task = self.project.update(cx, |project, cx| {
|
edit_task = self.project.update(cx, |project, cx| {
|
||||||
project.create_entry((worktree_id, &new_path), is_dir, cx)
|
project.create_entry((worktree_id, &new_path), is_dir, cx)
|
||||||
})?;
|
});
|
||||||
} else {
|
} else {
|
||||||
let new_path = if let Some(parent) = entry.path.clone().parent() {
|
let new_path = if let Some(parent) = entry.path.clone().parent() {
|
||||||
parent.join(&filename)
|
parent.join(&filename)
|
||||||
|
@ -624,7 +624,7 @@ impl ProjectPanel {
|
||||||
edited_entry_id = entry.id;
|
edited_entry_id = entry.id;
|
||||||
edit_task = self.project.update(cx, |project, cx| {
|
edit_task = self.project.update(cx, |project, cx| {
|
||||||
project.rename_entry(entry.id, new_path.as_path(), cx)
|
project.rename_entry(entry.id, new_path.as_path(), cx)
|
||||||
})?;
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
edit_state.processing_filename = Some(filename);
|
edit_state.processing_filename = Some(filename);
|
||||||
|
@ -637,21 +637,22 @@ impl ProjectPanel {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let new_entry = new_entry?;
|
if let Some(new_entry) = new_entry? {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
if let Some(selection) = &mut this.selection {
|
if let Some(selection) = &mut this.selection {
|
||||||
if selection.entry_id == edited_entry_id {
|
if selection.entry_id == edited_entry_id {
|
||||||
selection.worktree_id = worktree_id;
|
selection.worktree_id = worktree_id;
|
||||||
selection.entry_id = new_entry.id;
|
selection.entry_id = new_entry.id;
|
||||||
this.expand_to_selection(cx);
|
this.expand_to_selection(cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
this.update_visible_entries(None, cx);
|
||||||
this.update_visible_entries(None, cx);
|
if is_new_entry && !is_dir {
|
||||||
if is_new_entry && !is_dir {
|
this.open_entry(new_entry.id, true, cx);
|
||||||
this.open_entry(new_entry.id, true, cx);
|
}
|
||||||
}
|
cx.notify();
|
||||||
cx.notify();
|
})?;
|
||||||
})?;
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -931,15 +932,17 @@ impl ProjectPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
if clipboard_entry.is_cut() {
|
if clipboard_entry.is_cut() {
|
||||||
if let Some(task) = self.project.update(cx, |project, cx| {
|
self.project
|
||||||
project.rename_entry(clipboard_entry.entry_id(), new_path, cx)
|
.update(cx, |project, cx| {
|
||||||
}) {
|
project.rename_entry(clipboard_entry.entry_id(), new_path, cx)
|
||||||
task.detach_and_log_err(cx);
|
})
|
||||||
}
|
.detach_and_log_err(cx)
|
||||||
} else if let Some(task) = self.project.update(cx, |project, cx| {
|
} else {
|
||||||
project.copy_entry(clipboard_entry.entry_id(), new_path, cx)
|
self.project
|
||||||
}) {
|
.update(cx, |project, cx| {
|
||||||
task.detach_and_log_err(cx);
|
project.copy_entry(clipboard_entry.entry_id(), new_path, cx)
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
|
@ -1025,7 +1028,7 @@ impl ProjectPanel {
|
||||||
// let mut new_path = destination_path.to_path_buf();
|
// let mut new_path = destination_path.to_path_buf();
|
||||||
// new_path.push(entry_path.path.file_name()?);
|
// new_path.push(entry_path.path.file_name()?);
|
||||||
// if new_path != entry_path.path.as_ref() {
|
// if new_path != entry_path.path.as_ref() {
|
||||||
// let task = project.rename_entry(entry_to_move, new_path, cx)?;
|
// let task = project.rename_entry(entry_to_move, new_path, cx);
|
||||||
// cx.foreground_executor().spawn(task).detach_and_log_err(cx);
|
// cx.foreground_executor().spawn(task).detach_and_log_err(cx);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
|
@ -430,7 +430,7 @@ message ExpandProjectEntryResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProjectEntryResponse {
|
message ProjectEntryResponse {
|
||||||
Entry entry = 1;
|
optional Entry entry = 1;
|
||||||
uint64 worktree_scan_id = 2;
|
uint64 worktree_scan_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1357,7 +1357,7 @@ message User {
|
||||||
|
|
||||||
message File {
|
message File {
|
||||||
uint64 worktree_id = 1;
|
uint64 worktree_id = 1;
|
||||||
uint64 entry_id = 2;
|
optional uint64 entry_id = 2;
|
||||||
string path = 3;
|
string path = 3;
|
||||||
Timestamp mtime = 4;
|
Timestamp mtime = 4;
|
||||||
bool is_deleted = 5;
|
bool is_deleted = 5;
|
||||||
|
|
|
@ -9,4 +9,4 @@ pub use notification::*;
|
||||||
pub use peer::*;
|
pub use peer::*;
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
pub const PROTOCOL_VERSION: u32 = 66;
|
pub const PROTOCOL_VERSION: u32 = 67;
|
||||||
|
|
|
@ -1170,6 +1170,7 @@ mod tests {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(wt, entry)
|
(wt, entry)
|
||||||
|
|
|
@ -537,18 +537,21 @@ impl Pane {
|
||||||
|
|
||||||
pub(crate) fn open_item(
|
pub(crate) fn open_item(
|
||||||
&mut self,
|
&mut self,
|
||||||
project_entry_id: ProjectEntryId,
|
project_entry_id: Option<ProjectEntryId>,
|
||||||
focus_item: bool,
|
focus_item: bool,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
build_item: impl FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
|
build_item: impl FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
|
||||||
) -> Box<dyn ItemHandle> {
|
) -> Box<dyn ItemHandle> {
|
||||||
let mut existing_item = None;
|
let mut existing_item = None;
|
||||||
for (index, item) in self.items.iter().enumerate() {
|
if let Some(project_entry_id) = project_entry_id {
|
||||||
if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [project_entry_id]
|
for (index, item) in self.items.iter().enumerate() {
|
||||||
{
|
if item.is_singleton(cx)
|
||||||
let item = item.boxed_clone();
|
&& item.project_entry_ids(cx).as_slice() == [project_entry_id]
|
||||||
existing_item = Some((index, item));
|
{
|
||||||
break;
|
let item = item.boxed_clone();
|
||||||
|
existing_item = Some((index, item));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod persistence;
|
||||||
pub mod searchable;
|
pub mod searchable;
|
||||||
// todo!()
|
// todo!()
|
||||||
mod modal_layer;
|
mod modal_layer;
|
||||||
mod shared_screen;
|
pub mod shared_screen;
|
||||||
mod status_bar;
|
mod status_bar;
|
||||||
mod toolbar;
|
mod toolbar;
|
||||||
mod workspace_settings;
|
mod workspace_settings;
|
||||||
|
@ -1853,13 +1853,13 @@ impl Workspace {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn load_path(
|
fn load_path(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: ProjectPath,
|
path: ProjectPath,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<
|
) -> Task<
|
||||||
Result<(
|
Result<(
|
||||||
ProjectEntryId,
|
Option<ProjectEntryId>,
|
||||||
impl 'static + Send + FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
|
impl 'static + Send + FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
|
||||||
)>,
|
)>,
|
||||||
> {
|
> {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue