@@ -2464,21 +2464,30 @@ impl BackgroundScannerState {
// Remove any git repositories whose .git entry no longer exists.
let snapshot = &mut self.snapshot;
- // TODO kb stop cleaning those up here?
- let mut repositories = mem::take(&mut snapshot.git_repositories);
- let mut repository_entries = mem::take(&mut snapshot.repository_entries);
- repositories.retain(|_, entry| {
- // TODO kb use fs
- snapshot.abs_path().join(&entry.git_dir_path).exists()
- // snapshot
- // .entry_for_id(*work_directory_id)
- // .map_or(false, |entry| {
- // snapshot.entry_for_path(entry.path.join(*DOT_GIT)).is_some()
- // })
- });
- repository_entries.retain(|_, entry| repositories.get(&entry.work_directory.0).is_some());
- snapshot.git_repositories = repositories;
- snapshot.repository_entries = repository_entries;
+ let mut ids_to_preserve = HashSet::default();
+ for (&work_directory_id, entry) in snapshot.git_repositories.iter() {
+ let exists_in_snapshot = snapshot
+ .entry_for_id(work_directory_id)
+ .map_or(false, |entry| {
+ snapshot.entry_for_path(entry.path.join(*DOT_GIT)).is_some()
+ });
+ if exists_in_snapshot {
+ ids_to_preserve.insert(work_directory_id);
+ } else {
+ let git_dir_abs_path = snapshot.abs_path().join(&entry.git_dir_path);
+ if snapshot.is_abs_path_excluded(&git_dir_abs_path)
+ && !matches!(smol::block_on(fs.metadata(&git_dir_abs_path)), Ok(None))
+ {
+ ids_to_preserve.insert(work_directory_id);
+ }
+ }
+ }
+ snapshot
+ .git_repositories
+ .retain(|work_directory_id, _| ids_to_preserve.contains(work_directory_id));
+ snapshot
+ .repository_entries
+ .retain(|_, entry| ids_to_preserve.contains(&entry.work_directory.0));
}
fn build_git_repository(
@@ -3320,20 +3329,22 @@ impl BackgroundScanner {
return false;
};
- let parent_dir_is_loaded = relative_path.parent().map_or(true, |parent| {
- snapshot
- .entry_for_path(parent)
- .map_or(false, |entry| entry.kind == EntryKind::Dir)
- });
- if !parent_dir_is_loaded && !is_git_related(&abs_path) {
- log::debug!("ignoring event {relative_path:?} within unloaded directory");
- return false;
- }
- if snapshot.is_abs_path_excluded(abs_path) && !is_git_related(&abs_path) {
- log::debug!(
+ if !is_git_related(&abs_path) {
+ let parent_dir_is_loaded = relative_path.parent().map_or(true, |parent| {
+ snapshot
+ .entry_for_path(parent)
+ .map_or(false, |entry| entry.kind == EntryKind::Dir)
+ });
+ if !parent_dir_is_loaded {
+ log::debug!("ignoring event {relative_path:?} within unloaded directory");
+ return false;
+ }
+ if snapshot.is_abs_path_excluded(abs_path) {
+ log::debug!(
"ignoring FS event for path {relative_path:?} within excluded directory"
);
- return false;
+ return false;
+ }
}
relative_paths.push(relative_path);
@@ -3573,11 +3584,6 @@ impl BackgroundScanner {
// If we find a .git, we'll need to load the repository.
else if child_name == *DOT_GIT {
dotgit_path = Some(child_path.clone());
- {
- let mut state = self.state.lock();
- state.build_git_repository(child_path.clone(), self.fs.as_ref());
- drop(state);
- }
}
{
@@ -3595,7 +3601,7 @@ impl BackgroundScanner {
Ok(Some(metadata)) => metadata,
Ok(None) => continue,
Err(err) => {
- log::error!("error processing {:?}: {:?}", child_abs_path, err);
+ log::error!("error processing {child_abs_path:?}: {err:?}");
continue;
}
};
@@ -4124,7 +4130,7 @@ impl BackgroundScanner {
}
}
-fn is_git_related(abs_path: &&PathBuf) -> bool {
+fn is_git_related(abs_path: &Path) -> bool {
abs_path
.components()
.any(|c| c.as_os_str() == *DOT_GIT || c.as_os_str() == *GITIGNORE)
@@ -1732,7 +1732,7 @@ mod tests {
use super::*;
use gpui::{AnyWindowHandle, TestAppContext, ViewHandle, WindowHandle};
use pretty_assertions::assert_eq;
- use project::FakeFs;
+ use project::{project_settings::ProjectSettings, FakeFs};
use serde_json::json;
use settings::SettingsStore;
use std::{
@@ -1832,6 +1832,123 @@ mod tests {
);
}
+ #[gpui::test]
+ async fn test_exclusions_in_visible_list(cx: &mut gpui::TestAppContext) {
+ init_test(cx);
+ 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(), "**/4/**".to_string()]);
+ });
+ });
+ });
+
+ let fs = FakeFs::new(cx.background());
+ fs.insert_tree(
+ "/root1",
+ json!({
+ ".dockerignore": "",
+ ".git": {
+ "HEAD": "",
+ },
+ "a": {
+ "0": { "q": "", "r": "", "s": "" },
+ "1": { "t": "", "u": "" },
+ "2": { "v": "", "w": "", "x": "", "y": "" },
+ },
+ "b": {
+ "3": { "Q": "" },
+ "4": { "R": "", "S": "", "T": "", "U": "" },
+ },
+ "C": {
+ "5": {},
+ "6": { "V": "", "W": "" },
+ "7": { "X": "" },
+ "8": { "Y": {}, "Z": "" }
+ }
+ }),
+ )
+ .await;
+ fs.insert_tree(
+ "/root2",
+ json!({
+ "d": {
+ "4": ""
+ },
+ "e": {}
+ }),
+ )
+ .await;
+
+ let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
+ let workspace = cx
+ .add_window(|cx| Workspace::test_new(project.clone(), cx))
+ .root(cx);
+ let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..50, cx),
+ &[
+ "v root1",
+ " > a",
+ " > b",
+ " > C",
+ " .dockerignore",
+ "v root2",
+ " > d",
+ " > e",
+ ]
+ );
+
+ toggle_expand_dir(&panel, "root1/b", cx);
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..50, cx),
+ &[
+ "v root1",
+ " > a",
+ " v b <== selected",
+ " > 3",
+ " > C",
+ " .dockerignore",
+ "v root2",
+ " > d",
+ " > e",
+ ]
+ );
+
+ toggle_expand_dir(&panel, "root2/d", cx);
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..50, cx),
+ &[
+ "v root1",
+ " > a",
+ " v b",
+ " > 3",
+ " > C",
+ " .dockerignore",
+ "v root2",
+ " v d <== selected",
+ " > e",
+ ]
+ );
+
+ toggle_expand_dir(&panel, "root2/e", cx);
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..50, cx),
+ &[
+ "v root1",
+ " > a",
+ " v b",
+ " > 3",
+ " > C",
+ " .dockerignore",
+ "v root2",
+ " v d",
+ " v e <== selected",
+ ]
+ );
+ }
+
#[gpui::test(iterations = 30)]
async fn test_editing_files(cx: &mut gpui::TestAppContext) {
init_test(cx);
@@ -2930,6 +3047,13 @@ mod tests {
client::init_settings(cx);
Project::init_settings(cx);
});
+ cx.update(|cx| {
+ cx.update_global::<SettingsStore, _, _>(|store, cx| {
+ store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
+ project_settings.file_scan_exclusions = Some(Vec::new());
+ });
+ });
+ });
}
fn init_test_with_editor(cx: &mut TestAppContext) {