@@ -39,8 +39,11 @@
//! To ease trusting multiple directory worktrees at once, it's possible to trust a parent directory of a certain directory worktree opened in Zed.
//! Trusting a directory means trusting all its subdirectories as well, including all current and potential directory worktrees.
+use client::ProjectId;
use collections::{HashMap, HashSet};
-use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Global, SharedString, WeakEntity};
+use gpui::{
+ App, AppContext as _, Context, Entity, EventEmitter, Global, SharedString, Task, WeakEntity,
+};
use remote::RemoteConnectionOptions;
use rpc::{AnyProtoClient, proto};
use settings::{Settings as _, WorktreeId};
@@ -53,20 +56,14 @@ use util::debug_panic;
use crate::{project_settings::ProjectSettings, worktree_store::WorktreeStore};
pub fn init(
- db_trusted_paths: TrustedPaths,
- downstream_client: Option<(AnyProtoClient, u64)>,
- upstream_client: Option<(AnyProtoClient, u64)>,
+ db_trusted_paths: DbTrustedPaths,
+ downstream_client: Option<(AnyProtoClient, ProjectId)>,
+ upstream_client: Option<(AnyProtoClient, ProjectId)>,
cx: &mut App,
) {
if TrustedWorktrees::try_get_global(cx).is_none() {
let trusted_worktrees = cx.new(|_| {
- TrustedWorktreesStore::new(
- db_trusted_paths,
- None,
- None,
- downstream_client,
- upstream_client,
- )
+ TrustedWorktreesStore::new(db_trusted_paths, downstream_client, upstream_client)
});
cx.set_global(TrustedWorktrees(trusted_worktrees))
}
@@ -76,38 +73,36 @@ pub fn init(
pub fn track_worktree_trust(
worktree_store: Entity<WorktreeStore>,
remote_host: Option<RemoteHostLocation>,
- downstream_client: Option<(AnyProtoClient, u64)>,
- upstream_client: Option<(AnyProtoClient, u64)>,
+ downstream_client: Option<(AnyProtoClient, ProjectId)>,
+ upstream_client: Option<(AnyProtoClient, ProjectId)>,
cx: &mut App,
) {
match TrustedWorktrees::try_get_global(cx) {
Some(trusted_worktrees) => {
trusted_worktrees.update(cx, |trusted_worktrees, cx| {
- let sync_upstream = trusted_worktrees.upstream_client.as_ref().map(|(_, id)| id)
- != upstream_client.as_ref().map(|(_, id)| id);
- trusted_worktrees.downstream_client = downstream_client;
- trusted_worktrees.upstream_client = upstream_client;
+ if let Some(downstream_client) = downstream_client {
+ trusted_worktrees.downstream_clients.push(downstream_client);
+ }
+ if let Some(upstream_client) = upstream_client.clone() {
+ trusted_worktrees.upstream_clients.push(upstream_client);
+ }
trusted_worktrees.add_worktree_store(worktree_store, remote_host, cx);
- if sync_upstream {
- if let Some((upstream_client, upstream_project_id)) =
- &trusted_worktrees.upstream_client
- {
- let trusted_paths = trusted_worktrees
- .trusted_paths
- .iter()
- .flat_map(|(_, paths)| {
- paths.iter().map(|trusted_path| trusted_path.to_proto())
+ if let Some((upstream_client, upstream_project_id)) = upstream_client {
+ let trusted_paths = trusted_worktrees
+ .trusted_paths
+ .iter()
+ .flat_map(|(_, paths)| {
+ paths.iter().map(|trusted_path| trusted_path.to_proto())
+ })
+ .collect::<Vec<_>>();
+ if !trusted_paths.is_empty() {
+ upstream_client
+ .send(proto::TrustWorktrees {
+ project_id: upstream_project_id.0,
+ trusted_paths,
})
- .collect::<Vec<_>>();
- if !trusted_paths.is_empty() {
- upstream_client
- .send(proto::TrustWorktrees {
- project_id: *upstream_project_id,
- trusted_paths,
- })
- .ok();
- }
+ .ok();
}
}
});
@@ -132,12 +127,15 @@ impl TrustedWorktrees {
///
/// Emits an event each time the worktree was checked and found not trusted,
/// or a certain worktree had been trusted.
+#[derive(Debug)]
pub struct TrustedWorktreesStore {
- downstream_client: Option<(AnyProtoClient, u64)>,
- upstream_client: Option<(AnyProtoClient, u64)>,
+ downstream_clients: Vec<(AnyProtoClient, ProjectId)>,
+ upstream_clients: Vec<(AnyProtoClient, ProjectId)>,
worktree_stores: HashMap<WeakEntity<WorktreeStore>, Option<RemoteHostLocation>>,
+ db_trusted_paths: DbTrustedPaths,
trusted_paths: TrustedPaths,
- restricted: HashSet<WorktreeId>,
+ restricted: HashMap<WeakEntity<WorktreeStore>, HashSet<WorktreeId>>,
+ worktree_trust_serialization: Task<()>,
}
/// An identifier of a host to split the trust questions by.
@@ -165,7 +163,7 @@ impl From<RemoteConnectionOptions> for RemoteHostLocation {
SharedString::new(docker_connection_options.container_id),
),
};
- RemoteHostLocation {
+ Self {
user_name,
host_identifier: host_name,
}
@@ -214,48 +212,50 @@ impl PathTrust {
/// A change of trust on a certain host.
#[derive(Debug)]
pub enum TrustedWorktreesEvent {
- Trusted(Option<RemoteHostLocation>, HashSet<PathTrust>),
- Restricted(Option<RemoteHostLocation>, HashSet<PathTrust>),
+ Trusted(WeakEntity<WorktreeStore>, HashSet<PathTrust>),
+ Restricted(WeakEntity<WorktreeStore>, HashSet<PathTrust>),
}
impl EventEmitter<TrustedWorktreesEvent> for TrustedWorktreesStore {}
-pub type TrustedPaths = HashMap<Option<RemoteHostLocation>, HashSet<PathTrust>>;
+type TrustedPaths = HashMap<WeakEntity<WorktreeStore>, HashSet<PathTrust>>;
+pub type DbTrustedPaths = HashMap<Option<RemoteHostLocation>, HashSet<PathBuf>>;
impl TrustedWorktreesStore {
fn new(
- trusted_paths: TrustedPaths,
- worktree_store: Option<Entity<WorktreeStore>>,
- remote_host: Option<RemoteHostLocation>,
- downstream_client: Option<(AnyProtoClient, u64)>,
- upstream_client: Option<(AnyProtoClient, u64)>,
+ db_trusted_paths: DbTrustedPaths,
+ downstream_client: Option<(AnyProtoClient, ProjectId)>,
+ upstream_client: Option<(AnyProtoClient, ProjectId)>,
) -> Self {
if let Some((upstream_client, upstream_project_id)) = &upstream_client {
- let trusted_paths = trusted_paths
+ let trusted_paths = db_trusted_paths
.iter()
- .flat_map(|(_, paths)| paths.iter().map(|trusted_path| trusted_path.to_proto()))
+ .flat_map(|(_, paths)| {
+ paths
+ .iter()
+ .cloned()
+ .map(PathTrust::AbsPath)
+ .map(|trusted_path| trusted_path.to_proto())
+ })
.collect::<Vec<_>>();
if !trusted_paths.is_empty() {
upstream_client
.send(proto::TrustWorktrees {
- project_id: *upstream_project_id,
+ project_id: upstream_project_id.0,
trusted_paths,
})
.ok();
}
}
- let worktree_stores = match worktree_store {
- Some(worktree_store) => HashMap::from_iter([(worktree_store.downgrade(), remote_host)]),
- None => HashMap::default(),
- };
-
Self {
- trusted_paths,
- downstream_client,
- upstream_client,
- restricted: HashSet::default(),
- worktree_stores,
+ db_trusted_paths,
+ downstream_clients: downstream_client.into_iter().collect(),
+ upstream_clients: upstream_client.into_iter().collect(),
+ trusted_paths: HashMap::default(),
+ worktree_stores: HashMap::default(),
+ restricted: HashMap::default(),
+ worktree_trust_serialization: Task::ready(()),
}
}
@@ -265,13 +265,15 @@ impl TrustedWorktreesStore {
worktree_store: &Entity<WorktreeStore>,
cx: &App,
) -> bool {
- self.worktree_stores
- .contains_key(&worktree_store.downgrade())
- && self.restricted.iter().any(|restricted_worktree| {
- worktree_store
- .read(cx)
- .worktree_for_id(*restricted_worktree, cx)
- .is_some()
+ self.restricted
+ .get(&worktree_store.downgrade())
+ .is_some_and(|restricted_worktrees| {
+ restricted_worktrees.iter().any(|restricted_worktree| {
+ worktree_store
+ .read(cx)
+ .worktree_for_id(*restricted_worktree, cx)
+ .is_some()
+ })
})
}
@@ -280,40 +282,55 @@ impl TrustedWorktreesStore {
/// and the ones that got auto trusted based on trust hierarchy (see module-level docs).
pub fn trust(
&mut self,
+ worktree_store: &Entity<WorktreeStore>,
mut trusted_paths: HashSet<PathTrust>,
- remote_host: Option<RemoteHostLocation>,
cx: &mut Context<Self>,
) {
+ let weak_worktree_store = worktree_store.downgrade();
let mut new_trusted_single_file_worktrees = HashSet::default();
let mut new_trusted_other_worktrees = HashSet::default();
let mut new_trusted_abs_paths = HashSet::default();
for trusted_path in trusted_paths.iter().chain(
self.trusted_paths
- .remove(&remote_host)
+ .remove(&weak_worktree_store)
.iter()
.flat_map(|current_trusted| current_trusted.iter()),
) {
match trusted_path {
PathTrust::Worktree(worktree_id) => {
- self.restricted.remove(worktree_id);
- if let Some((abs_path, is_file, host)) =
- self.find_worktree_data(*worktree_id, cx)
+ if let Some(restricted_worktrees) =
+ self.restricted.get_mut(&weak_worktree_store)
+ {
+ restricted_worktrees.remove(worktree_id);
+ };
+
+ if let Some(worktree) =
+ worktree_store.read(cx).worktree_for_id(*worktree_id, cx)
{
- if host == remote_host {
- if is_file {
- new_trusted_single_file_worktrees.insert(*worktree_id);
- } else {
- new_trusted_other_worktrees.insert((abs_path, *worktree_id));
- }
+ if worktree.read(cx).is_single_file() {
+ new_trusted_single_file_worktrees.insert(*worktree_id);
+ } else {
+ new_trusted_other_worktrees
+ .insert((worktree.read(cx).abs_path(), *worktree_id));
}
}
}
- PathTrust::AbsPath(path) => {
+ PathTrust::AbsPath(abs_path) => {
debug_assert!(
- path.is_absolute(),
- "Cannot trust non-absolute path {path:?}"
+ abs_path.is_absolute(),
+ "Cannot trust non-absolute path {abs_path:?}"
);
- new_trusted_abs_paths.insert(path.clone());
+ if let Some((worktree_id, is_file)) =
+ find_worktree_in_store(worktree_store.read(cx), abs_path, cx)
+ {
+ if is_file {
+ new_trusted_single_file_worktrees.insert(worktree_id);
+ } else {
+ new_trusted_other_worktrees
+ .insert((Arc::from(abs_path.as_path()), worktree_id));
+ }
+ }
+ new_trusted_abs_paths.insert(abs_path.clone());
}
}
}
@@ -326,37 +343,45 @@ impl TrustedWorktreesStore {
if !new_trusted_other_worktrees.is_empty() {
new_trusted_single_file_worktrees.clear();
}
- self.restricted = std::mem::take(&mut self.restricted)
- .into_iter()
- .filter(|restricted_worktree| {
- let Some((restricted_worktree_path, is_file, restricted_host)) =
- self.find_worktree_data(*restricted_worktree, cx)
- else {
- return false;
- };
- if restricted_host != remote_host {
- return true;
- }
- // When trusting an abs path on the host, we transitively trust all single file worktrees on this host too.
- if is_file && !new_trusted_abs_paths.is_empty() {
- trusted_paths.insert(PathTrust::Worktree(*restricted_worktree));
- return false;
- }
+ if let Some(restricted_worktrees) = self.restricted.remove(&weak_worktree_store) {
+ let new_restricted_worktrees = restricted_worktrees
+ .into_iter()
+ .filter(|restricted_worktree| {
+ let Some(worktree) = worktree_store
+ .read(cx)
+ .worktree_for_id(*restricted_worktree, cx)
+ else {
+ return false;
+ };
+ let is_file = worktree.read(cx).is_single_file();
+
+ // When trusting an abs path on the host, we transitively trust all single file worktrees on this host too.
+ if is_file && !new_trusted_abs_paths.is_empty() {
+ trusted_paths.insert(PathTrust::Worktree(*restricted_worktree));
+ return false;
+ }
- let retain = (!is_file || new_trusted_other_worktrees.is_empty())
- && new_trusted_abs_paths.iter().all(|new_trusted_path| {
- !restricted_worktree_path.starts_with(new_trusted_path)
- });
- if !retain {
- trusted_paths.insert(PathTrust::Worktree(*restricted_worktree));
- }
- retain
- })
- .collect();
+ let restricted_worktree_path = worktree.read(cx).abs_path();
+ let retain = (!is_file || new_trusted_other_worktrees.is_empty())
+ && new_trusted_abs_paths.iter().all(|new_trusted_path| {
+ !restricted_worktree_path.starts_with(new_trusted_path)
+ });
+ if !retain {
+ trusted_paths.insert(PathTrust::Worktree(*restricted_worktree));
+ }
+ retain
+ })
+ .collect();
+ self.restricted
+ .insert(weak_worktree_store.clone(), new_restricted_worktrees);
+ }
{
- let trusted_paths = self.trusted_paths.entry(remote_host.clone()).or_default();
+ let trusted_paths = self
+ .trusted_paths
+ .entry(weak_worktree_store.clone())
+ .or_default();
trusted_paths.extend(new_trusted_abs_paths.into_iter().map(PathTrust::AbsPath));
trusted_paths.extend(
new_trusted_other_worktrees
@@ -371,11 +396,11 @@ impl TrustedWorktreesStore {
}
cx.emit(TrustedWorktreesEvent::Trusted(
- remote_host,
+ weak_worktree_store,
trusted_paths.clone(),
));
- if let Some((upstream_client, upstream_project_id)) = &self.upstream_client {
+ for (upstream_client, upstream_project_id) in &self.upstream_clients {
let trusted_paths = trusted_paths
.iter()
.map(|trusted_path| trusted_path.to_proto())
@@ -383,7 +408,7 @@ impl TrustedWorktreesStore {
if !trusted_paths.is_empty() {
upstream_client
.send(proto::TrustWorktrees {
- project_id: *upstream_project_id,
+ project_id: upstream_project_id.0,
trusted_paths,
})
.ok();
@@ -395,63 +420,81 @@ impl TrustedWorktreesStore {
/// This will emit [`TrustedWorktreesEvent::Restricted`] event for all passed entries.
pub fn restrict(
&mut self,
+ worktree_store: WeakEntity<WorktreeStore>,
restricted_paths: HashSet<PathTrust>,
- remote_host: Option<RemoteHostLocation>,
cx: &mut Context<Self>,
) {
+ let mut restricted = HashSet::default();
for restricted_path in restricted_paths {
match restricted_path {
PathTrust::Worktree(worktree_id) => {
- self.restricted.insert(worktree_id);
- cx.emit(TrustedWorktreesEvent::Restricted(
- remote_host.clone(),
- HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
- ));
+ self.restricted
+ .entry(worktree_store.clone())
+ .or_default()
+ .insert(worktree_id);
+ restricted.insert(PathTrust::Worktree(worktree_id));
}
PathTrust::AbsPath(..) => debug_panic!("Unexpected: cannot restrict an abs path"),
}
}
+
+ cx.emit(TrustedWorktreesEvent::Restricted(
+ worktree_store,
+ restricted,
+ ));
}
/// Erases all trust information.
/// Requires Zed's restart to take proper effect.
pub fn clear_trusted_paths(&mut self) {
self.trusted_paths.clear();
+ self.db_trusted_paths.clear();
}
/// Checks whether a certain worktree is trusted (or on a larger trust level).
/// If not, emits [`TrustedWorktreesEvent::Restricted`] event if for the first time and not trusted, or no corresponding worktree store was found.
///
/// No events or data adjustment happens when `trust_all_worktrees` auto trust is enabled.
- pub fn can_trust(&mut self, worktree_id: WorktreeId, cx: &mut Context<Self>) -> bool {
+ pub fn can_trust(
+ &mut self,
+ worktree_store: &Entity<WorktreeStore>,
+ worktree_id: WorktreeId,
+ cx: &mut Context<Self>,
+ ) -> bool {
if ProjectSettings::get_global(cx).session.trust_all_worktrees {
return true;
}
- if self.restricted.contains(&worktree_id) {
- return false;
- }
- let Some((worktree_path, is_file, remote_host)) = self.find_worktree_data(worktree_id, cx)
- else {
+ let weak_worktree_store = worktree_store.downgrade();
+ let Some(worktree) = worktree_store.read(cx).worktree_for_id(worktree_id, cx) else {
return false;
};
+ let worktree_path = worktree.read(cx).abs_path();
+ let is_file = worktree.read(cx).is_single_file();
+ if self
+ .restricted
+ .get(&weak_worktree_store)
+ .is_some_and(|restricted_worktrees| restricted_worktrees.contains(&worktree_id))
+ {
+ return false;
+ }
if self
.trusted_paths
- .get(&remote_host)
+ .get(&weak_worktree_store)
.is_some_and(|trusted_paths| trusted_paths.contains(&PathTrust::Worktree(worktree_id)))
{
return true;
}
// See module documentation for details on trust level.
- if is_file && self.trusted_paths.contains_key(&remote_host) {
+ if is_file && self.trusted_paths.contains_key(&weak_worktree_store) {
return true;
}
let parent_path_trusted =
self.trusted_paths
- .get(&remote_host)
+ .get(&weak_worktree_store)
.is_some_and(|trusted_paths| {
trusted_paths.iter().any(|trusted_path| {
let PathTrust::AbsPath(trusted_path) = trusted_path else {
@@ -464,23 +507,26 @@ impl TrustedWorktreesStore {
return true;
}
- self.restricted.insert(worktree_id);
+ self.restricted
+ .entry(weak_worktree_store.clone())
+ .or_default()
+ .insert(worktree_id);
cx.emit(TrustedWorktreesEvent::Restricted(
- remote_host,
+ weak_worktree_store,
HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
));
- if let Some((downstream_client, downstream_project_id)) = &self.downstream_client {
+ for (downstream_client, downstream_project_id) in &self.downstream_clients {
downstream_client
.send(proto::RestrictWorktrees {
- project_id: *downstream_project_id,
+ project_id: downstream_project_id.0,
worktree_ids: vec![worktree_id.to_proto()],
})
.ok();
}
- if let Some((upstream_client, upstream_project_id)) = &self.upstream_client {
+ for (upstream_client, upstream_project_id) in &self.upstream_clients {
upstream_client
.send(proto::RestrictWorktrees {
- project_id: *upstream_project_id,
+ project_id: upstream_project_id.0,
worktree_ids: vec![worktree_id.to_proto()],
})
.ok();
@@ -491,15 +537,20 @@ impl TrustedWorktreesStore {
/// Lists all explicitly restricted worktrees (via [`TrustedWorktreesStore::can_trust`] method calls) for a particular worktree store on a particular host.
pub fn restricted_worktrees(
&self,
- worktree_store: &WorktreeStore,
+ worktree_store: &Entity<WorktreeStore>,
cx: &App,
) -> HashSet<(WorktreeId, Arc<Path>)> {
let mut single_file_paths = HashSet::default();
+
let other_paths = self
.restricted
- .iter()
+ .get(&worktree_store.downgrade())
+ .into_iter()
+ .flatten()
.filter_map(|&restricted_worktree_id| {
- let worktree = worktree_store.worktree_for_id(restricted_worktree_id, cx)?;
+ let worktree = worktree_store
+ .read(cx)
+ .worktree_for_id(restricted_worktree_id, cx)?;
let worktree = worktree.read(cx);
let abs_path = worktree.abs_path();
if worktree.is_single_file() {
@@ -521,74 +572,58 @@ impl TrustedWorktreesStore {
/// Switches the "trust nothing" mode to "automatically trust everything".
/// This does not influence already persisted data, but stops adding new worktrees there.
pub fn auto_trust_all(&mut self, cx: &mut Context<Self>) {
- for (remote_host, worktrees) in std::mem::take(&mut self.restricted)
- .into_iter()
- .flat_map(|restricted_worktree| {
- let (_, _, host) = self.find_worktree_data(restricted_worktree, cx)?;
- Some((restricted_worktree, host))
- })
- .fold(HashMap::default(), |mut acc, (worktree_id, remote_host)| {
+ for (worktree_store, worktrees) in std::mem::take(&mut self.restricted).into_iter().fold(
+ HashMap::default(),
+ |mut acc, (remote_host, worktrees)| {
acc.entry(remote_host)
.or_insert_with(HashSet::default)
- .insert(PathTrust::Worktree(worktree_id));
+ .extend(worktrees.into_iter().map(PathTrust::Worktree));
acc
- })
- {
- self.trust(worktrees, remote_host, cx);
+ },
+ ) {
+ if let Some(worktree_store) = worktree_store.upgrade() {
+ self.trust(&worktree_store, worktrees, cx);
+ }
}
}
- /// Returns a normalized representation of the trusted paths to store in the DB.
- pub fn trusted_paths_for_serialization(
+ pub fn schedule_serialization<S>(&mut self, cx: &mut Context<Self>, serialize: S)
+ where
+ S: FnOnce(HashMap<Option<RemoteHostLocation>, HashSet<PathBuf>>, &App) -> Task<()>
+ + 'static,
+ {
+ self.worktree_trust_serialization = serialize(self.trusted_paths_for_serialization(cx), cx);
+ }
+
+ fn trusted_paths_for_serialization(
&mut self,
cx: &mut Context<Self>,
) -> HashMap<Option<RemoteHostLocation>, HashSet<PathBuf>> {
- let new_trusted_worktrees = self
- .trusted_paths
- .clone()
- .into_iter()
- .map(|(host, paths)| {
+ self.trusted_paths
+ .iter()
+ .filter_map(|(worktree_store, paths)| {
+ let host = self.worktree_stores.get(&worktree_store)?.clone();
let abs_paths = paths
- .into_iter()
+ .iter()
.flat_map(|path| match path {
- PathTrust::Worktree(worktree_id) => self
- .find_worktree_data(worktree_id, cx)
- .map(|(abs_path, ..)| abs_path.to_path_buf()),
- PathTrust::AbsPath(abs_path) => Some(abs_path),
+ PathTrust::Worktree(worktree_id) => worktree_store
+ .upgrade()
+ .and_then(|worktree_store| {
+ worktree_store.read(cx).worktree_for_id(*worktree_id, cx)
+ })
+ .map(|worktree| worktree.read(cx).abs_path().to_path_buf()),
+ PathTrust::AbsPath(abs_path) => Some(abs_path.clone()),
})
- .collect();
- (host, abs_paths)
+ .collect::<HashSet<_>>();
+ Some((host, abs_paths))
+ })
+ .chain(self.db_trusted_paths.clone())
+ .fold(HashMap::default(), |mut acc, (host, paths)| {
+ acc.entry(host)
+ .or_insert_with(HashSet::default)
+ .extend(paths);
+ acc
})
- .collect();
- new_trusted_worktrees
- }
-
- fn find_worktree_data(
- &mut self,
- worktree_id: WorktreeId,
- cx: &mut Context<Self>,
- ) -> Option<(Arc<Path>, bool, Option<RemoteHostLocation>)> {
- let mut worktree_data = None;
- self.worktree_stores.retain(
- |worktree_store, remote_host| match worktree_store.upgrade() {
- Some(worktree_store) => {
- if worktree_data.is_none() {
- if let Some(worktree) =
- worktree_store.read(cx).worktree_for_id(worktree_id, cx)
- {
- worktree_data = Some((
- worktree.read(cx).abs_path(),
- worktree.read(cx).is_single_file(),
- remote_host.clone(),
- ));
- }
- }
- true
- }
- None => false,
- },
- );
- worktree_data
}
fn add_worktree_store(
@@ -597,18 +632,26 @@ impl TrustedWorktreesStore {
remote_host: Option<RemoteHostLocation>,
cx: &mut Context<Self>,
) {
+ let weak_worktree_store = worktree_store.downgrade();
self.worktree_stores
- .insert(worktree_store.downgrade(), remote_host.clone());
+ .insert(weak_worktree_store.clone(), remote_host.clone());
- if let Some(trusted_paths) = self.trusted_paths.remove(&remote_host) {
+ let mut new_trusted_paths = HashSet::default();
+ if let Some(db_trusted_paths) = self.db_trusted_paths.get(&remote_host) {
+ new_trusted_paths.extend(db_trusted_paths.clone().into_iter().map(PathTrust::AbsPath));
+ }
+ if let Some(trusted_paths) = self.trusted_paths.remove(&weak_worktree_store) {
+ new_trusted_paths.extend(trusted_paths);
+ }
+ if !new_trusted_paths.is_empty() {
self.trusted_paths.insert(
- remote_host.clone(),
- trusted_paths
+ weak_worktree_store,
+ new_trusted_paths
.into_iter()
.map(|path_trust| match path_trust {
PathTrust::AbsPath(abs_path) => {
find_worktree_in_store(worktree_store.read(cx), &abs_path, cx)
- .map(PathTrust::Worktree)
+ .map(|(worktree_id, _)| PathTrust::Worktree(worktree_id))
.unwrap_or_else(|| PathTrust::AbsPath(abs_path))
}
other => other,
@@ -619,14 +662,14 @@ impl TrustedWorktreesStore {
}
}
-pub fn find_worktree_in_store(
+fn find_worktree_in_store(
worktree_store: &WorktreeStore,
abs_path: &Path,
cx: &App,
-) -> Option<WorktreeId> {
+) -> Option<(WorktreeId, bool)> {
let (worktree, path_in_worktree) = worktree_store.find_worktree(&abs_path, cx)?;
if path_in_worktree.is_empty() {
- Some(worktree.read(cx).id())
+ Some((worktree.read(cx).id(), worktree.read(cx).is_single_file()))
} else {
None
}
@@ -703,15 +746,17 @@ mod tests {
})
.detach();
- let can_trust = trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_id, cx));
+ let can_trust = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_id, cx)
+ });
assert!(!can_trust, "worktree should be restricted by default");
{
let events = events.borrow();
assert_eq!(events.len(), 1);
match &events[0] {
- TrustedWorktreesEvent::Restricted(host, paths) => {
- assert!(host.is_none());
+ TrustedWorktreesEvent::Restricted(event_worktree_store, paths) => {
+ assert_eq!(event_worktree_store, &worktree_store.downgrade());
assert!(paths.contains(&PathTrust::Worktree(worktree_id)));
}
_ => panic!("expected Restricted event"),
@@ -723,15 +768,16 @@ mod tests {
});
assert!(has_restricted, "should have restricted worktrees");
- let restricted = worktree_store.read_with(cx, |ws, cx| {
- trusted_worktrees.read(cx).restricted_worktrees(ws, cx)
+ let restricted = trusted_worktrees.read_with(cx, |trusted_worktrees, cx| {
+ trusted_worktrees.restricted_worktrees(&worktree_store, cx)
});
assert!(restricted.iter().any(|(id, _)| *id == worktree_id));
events.borrow_mut().clear();
- let can_trust_again =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_id, cx));
+ let can_trust_again = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_id, cx)
+ });
assert!(!can_trust_again, "worktree should still be restricted");
assert!(
events.borrow().is_empty(),
@@ -740,8 +786,8 @@ mod tests {
trusted_worktrees.update(cx, |store, cx| {
store.trust(
+ &worktree_store,
HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
- None,
cx,
);
});
@@ -750,16 +796,17 @@ mod tests {
let events = events.borrow();
assert_eq!(events.len(), 1);
match &events[0] {
- TrustedWorktreesEvent::Trusted(host, paths) => {
- assert!(host.is_none());
+ TrustedWorktreesEvent::Trusted(event_worktree_store, paths) => {
+ assert_eq!(event_worktree_store, &worktree_store.downgrade());
assert!(paths.contains(&PathTrust::Worktree(worktree_id)));
}
_ => panic!("expected Trusted event"),
}
}
- let can_trust_after =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_id, cx));
+ let can_trust_after = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_id, cx)
+ });
assert!(can_trust_after, "worktree should be trusted after trust()");
let has_restricted_after = trusted_worktrees.read_with(cx, |store, cx| {
@@ -770,8 +817,8 @@ mod tests {
"should have no restricted worktrees after trust"
);
- let restricted_after = worktree_store.read_with(cx, |ws, cx| {
- trusted_worktrees.read(cx).restricted_worktrees(ws, cx)
+ let restricted_after = trusted_worktrees.read_with(cx, |trusted_worktrees, cx| {
+ trusted_worktrees.restricted_worktrees(&worktree_store, cx)
});
assert!(
restricted_after.is_empty(),
@@ -796,7 +843,7 @@ mod tests {
worktree.id()
});
- let trusted_worktrees = init_trust_global(worktree_store, cx);
+ let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
let events: Rc<RefCell<Vec<TrustedWorktreesEvent>>> = Rc::default();
cx.update({
@@ -816,7 +863,9 @@ mod tests {
})
.detach();
- let can_trust = trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_id, cx));
+ let can_trust = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_id, cx)
+ });
assert!(
!can_trust,
"single-file worktree should be restricted by default"
@@ -826,8 +875,8 @@ mod tests {
let events = events.borrow();
assert_eq!(events.len(), 1);
match &events[0] {
- TrustedWorktreesEvent::Restricted(host, paths) => {
- assert!(host.is_none());
+ TrustedWorktreesEvent::Restricted(event_worktree_store, paths) => {
+ assert_eq!(event_worktree_store, &worktree_store.downgrade());
assert!(paths.contains(&PathTrust::Worktree(worktree_id)));
}
_ => panic!("expected Restricted event"),
@@ -838,8 +887,8 @@ mod tests {
trusted_worktrees.update(cx, |store, cx| {
store.trust(
+ &worktree_store,
HashSet::from_iter([PathTrust::Worktree(worktree_id)]),
- None,
cx,
);
});
@@ -848,16 +897,17 @@ mod tests {
let events = events.borrow();
assert_eq!(events.len(), 1);
match &events[0] {
- TrustedWorktreesEvent::Trusted(host, paths) => {
- assert!(host.is_none());
+ TrustedWorktreesEvent::Trusted(event_worktree_store, paths) => {
+ assert_eq!(event_worktree_store, &worktree_store.downgrade());
assert!(paths.contains(&PathTrust::Worktree(worktree_id)));
}
_ => panic!("expected Trusted event"),
}
}
- let can_trust_after =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_id, cx));
+ let can_trust_after = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_id, cx)
+ });
assert!(
can_trust_after,
"single-file worktree should be trusted after trust()"
@@ -902,11 +952,12 @@ mod tests {
});
assert_eq!(worktree_ids.len(), 3);
- let trusted_worktrees = init_trust_global(worktree_store, cx);
+ let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
for &worktree_id in &worktree_ids {
- let can_trust =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_id, cx));
+ let can_trust = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_id, cx)
+ });
assert!(
!can_trust,
"worktree {worktree_id:?} should be restricted initially"
@@ -915,18 +966,21 @@ mod tests {
trusted_worktrees.update(cx, |store, cx| {
store.trust(
+ &worktree_store,
HashSet::from_iter([PathTrust::Worktree(worktree_ids[1])]),
- None,
cx,
);
});
- let can_trust_0 =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[0], cx));
- let can_trust_1 =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[1], cx));
- let can_trust_2 =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[2], cx));
+ let can_trust_0 = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[0], cx)
+ });
+ let can_trust_1 = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[1], cx)
+ });
+ let can_trust_2 = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[2], cx)
+ });
assert!(!can_trust_0, "worktree 0 should still be restricted");
assert!(can_trust_1, "worktree 1 should be trusted");
@@ -969,42 +1023,48 @@ mod tests {
});
assert_eq!(worktree_ids.len(), 2);
- let trusted_worktrees = init_trust_global(worktree_store, cx);
+ let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
- let can_trust_a =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[0], cx));
- let can_trust_b =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[1], cx));
+ let can_trust_a = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[0], cx)
+ });
+ let can_trust_b = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[1], cx)
+ });
assert!(!can_trust_a, "project_a should be restricted initially");
assert!(!can_trust_b, "project_b should be restricted initially");
trusted_worktrees.update(cx, |store, cx| {
store.trust(
+ &worktree_store,
HashSet::from_iter([PathTrust::Worktree(worktree_ids[0])]),
- None,
cx,
);
});
- let can_trust_a =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[0], cx));
- let can_trust_b =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[1], cx));
+ let can_trust_a = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[0], cx)
+ });
+ let can_trust_b = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[1], cx)
+ });
assert!(can_trust_a, "project_a should be trusted after trust()");
assert!(!can_trust_b, "project_b should still be restricted");
trusted_worktrees.update(cx, |store, cx| {
store.trust(
+ &worktree_store,
HashSet::from_iter([PathTrust::Worktree(worktree_ids[1])]),
- None,
cx,
);
});
- let can_trust_a =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[0], cx));
- let can_trust_b =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(worktree_ids[1], cx));
+ let can_trust_a = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[0], cx)
+ });
+ let can_trust_b = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, worktree_ids[1], cx)
+ });
assert!(can_trust_a, "project_a should remain trusted");
assert!(can_trust_b, "project_b should now be trusted");
}
@@ -1043,17 +1103,19 @@ mod tests {
(dir_worktree.read(cx).id(), file_worktree.read(cx).id())
});
- let trusted_worktrees = init_trust_global(worktree_store, cx);
+ let trusted_worktrees = init_trust_global(worktree_store.clone(), cx);
- let can_trust_file =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(file_worktree_id, cx));
+ let can_trust_file = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, file_worktree_id, cx)
+ });
assert!(
!can_trust_file,
"single-file worktree should be restricted initially"
);
- let can_trust_directory =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(dir_worktree_id, cx));
+ let can_trust_directory = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, dir_worktree_id, cx)
+ });
assert!(
!can_trust_directory,
"directory worktree should be restricted initially"
@@ -1061,16 +1123,18 @@ mod tests {
trusted_worktrees.update(cx, |store, cx| {
store.trust(
+ &worktree_store,
HashSet::from_iter([PathTrust::Worktree(dir_worktree_id)]),
- None,
cx,
);
});
- let can_trust_dir =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(dir_worktree_id, cx));
- let can_trust_file_after =
- trusted_worktrees.update(cx, |store, cx| store.can_trust(file_worktree_id, cx));
+ let can_trust_dir = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, dir_worktree_id, cx)
+ });
+ let can_trust_file_after = trusted_worktrees.update(cx, |store, cx| {
+ store.can_trust(&worktree_store, file_worktree_id, cx)
+ });
assert!(can_trust_dir, "directory worktree should be trusted");
assert!(
can_trust_file_after,