mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Test and filter data draft
This commit is contained in:
parent
a5c615ceb4
commit
7d97dfa6be
@ -1,5 +1,5 @@
|
||||
use ignore::gitignore::Gitignore;
|
||||
use std::{ffi::OsStr, path::Path, sync::Arc};
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
pub enum IgnoreStack {
|
||||
None,
|
||||
@ -34,24 +34,4 @@ impl IgnoreStack {
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_abs_path_ignored(&self, abs_path: &Path, is_dir: bool) -> bool {
|
||||
if is_dir && abs_path.file_name() == Some(OsStr::new(".git")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
match self {
|
||||
Self::None => false,
|
||||
Self::All => true,
|
||||
Self::Some {
|
||||
abs_base_path,
|
||||
ignore,
|
||||
parent: prev,
|
||||
} => match ignore.matched(abs_path.strip_prefix(abs_base_path).unwrap(), is_dir) {
|
||||
ignore::Match::None => prev.is_abs_path_ignored(abs_path, is_dir),
|
||||
ignore::Match::Ignore(_) => true,
|
||||
ignore::Match::Whitelist(_) => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,12 @@ pub struct ProjectSettings {
|
||||
pub lsp: HashMap<Arc<str>, LspSettings>,
|
||||
#[serde(default)]
|
||||
pub git: GitSettings,
|
||||
// TODO kb better names and docs
|
||||
// TODO kb how to react on their changes?
|
||||
#[serde(default)]
|
||||
pub scan_exclude_files: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub scan_include_files: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
copy_recursive, ignore::IgnoreStack, DiagnosticSummary, ProjectEntryId, RemoveOptions,
|
||||
copy_recursive, ignore::IgnoreStack, project_settings::ProjectSettings, DiagnosticSummary,
|
||||
ProjectEntryId, RemoveOptions,
|
||||
};
|
||||
use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
@ -55,7 +56,10 @@ use std::{
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet};
|
||||
use util::{paths::HOME, ResultExt};
|
||||
use util::{
|
||||
paths::{PathMatcher, HOME},
|
||||
ResultExt,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
||||
pub struct WorktreeId(usize);
|
||||
@ -216,6 +220,8 @@ pub struct LocalSnapshot {
|
||||
/// All of the git repositories in the worktree, indexed by the project entry
|
||||
/// id of their parent directory.
|
||||
git_repositories: TreeMap<ProjectEntryId, LocalRepositoryEntry>,
|
||||
scan_exclude_files: Vec<PathMatcher>,
|
||||
scan_include_files: Vec<PathMatcher>,
|
||||
}
|
||||
|
||||
struct BackgroundScannerState {
|
||||
@ -303,8 +309,34 @@ impl Worktree {
|
||||
let root_name = abs_path
|
||||
.file_name()
|
||||
.map_or(String::new(), |f| f.to_string_lossy().to_string());
|
||||
|
||||
let project_settings = settings::get::<ProjectSettings>(cx);
|
||||
let scan_exclude_files = project_settings.scan_exclude_files.iter()
|
||||
.filter_map(|pattern| {
|
||||
PathMatcher::new(pattern)
|
||||
.map(Some)
|
||||
.unwrap_or_else(|e| {
|
||||
log::error!(
|
||||
"Skipping pattern {pattern} in `scan_exclude_files` project settings due to parsing error: {e:#}"
|
||||
);
|
||||
None
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let scan_include_files = project_settings.scan_include_files.iter()
|
||||
.filter_map(|pattern| {
|
||||
PathMatcher::new(pattern)
|
||||
.map(Some)
|
||||
.unwrap_or_else(|e| {
|
||||
log::error!(
|
||||
"Skipping pattern {pattern} in `scan_include_files` project settings due to parsing error: {e:#}"
|
||||
);
|
||||
None
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut snapshot = LocalSnapshot {
|
||||
scan_include_files,
|
||||
scan_exclude_files,
|
||||
ignores_by_parent_abs_path: Default::default(),
|
||||
git_repositories: Default::default(),
|
||||
snapshot: Snapshot {
|
||||
@ -2042,7 +2074,7 @@ impl LocalSnapshot {
|
||||
|
||||
let mut ignore_stack = IgnoreStack::none();
|
||||
for (parent_abs_path, ignore) in new_ignores.into_iter().rev() {
|
||||
if ignore_stack.is_abs_path_ignored(parent_abs_path, true) {
|
||||
if self.is_abs_path_ignored(parent_abs_path, &ignore_stack, true) {
|
||||
ignore_stack = IgnoreStack::all();
|
||||
break;
|
||||
} else if let Some(ignore) = ignore {
|
||||
@ -2050,7 +2082,7 @@ impl LocalSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
if ignore_stack.is_abs_path_ignored(abs_path, is_dir) {
|
||||
if self.is_abs_path_ignored(abs_path, &ignore_stack, is_dir) {
|
||||
ignore_stack = IgnoreStack::all();
|
||||
}
|
||||
ignore_stack
|
||||
@ -2145,6 +2177,45 @@ impl LocalSnapshot {
|
||||
paths.sort_by(|a, b| a.0.cmp(b.0));
|
||||
paths
|
||||
}
|
||||
|
||||
fn is_abs_path_ignored(
|
||||
&self,
|
||||
abs_path: &Path,
|
||||
ignore_stack: &IgnoreStack,
|
||||
is_dir: bool,
|
||||
) -> bool {
|
||||
dbg!(&abs_path);
|
||||
if self
|
||||
.scan_include_files
|
||||
.iter()
|
||||
.any(|include_matcher| include_matcher.is_match(abs_path))
|
||||
{
|
||||
dbg!("included!!");
|
||||
return false;
|
||||
} else if self
|
||||
.scan_exclude_files
|
||||
.iter()
|
||||
.any(|exclude_matcher| exclude_matcher.is_match(abs_path))
|
||||
{
|
||||
dbg!("excluded!!");
|
||||
return true;
|
||||
} else if is_dir && abs_path.file_name() == Some(OsStr::new(".git")) {
|
||||
return true;
|
||||
}
|
||||
match ignore_stack {
|
||||
IgnoreStack::None => false,
|
||||
IgnoreStack::All => true,
|
||||
IgnoreStack::Some {
|
||||
abs_base_path,
|
||||
ignore,
|
||||
parent: prev,
|
||||
} => match ignore.matched(abs_path.strip_prefix(abs_base_path).unwrap(), is_dir) {
|
||||
ignore::Match::None => self.is_abs_path_ignored(abs_path, &prev, is_dir),
|
||||
ignore::Match::Ignore(_) => true,
|
||||
ignore::Match::Whitelist(_) => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BackgroundScannerState {
|
||||
@ -2767,7 +2838,7 @@ pub struct Entry {
|
||||
pub mtime: SystemTime,
|
||||
pub is_symlink: bool,
|
||||
|
||||
/// Whether this entry is ignored by Git.
|
||||
/// Whether this entry is ignored by Zed.
|
||||
///
|
||||
/// We only scan ignored entries once the directory is expanded and
|
||||
/// exclude them from searches.
|
||||
@ -3464,7 +3535,7 @@ impl BackgroundScanner {
|
||||
for entry in &mut new_entries {
|
||||
let entry_abs_path = root_abs_path.join(&entry.path);
|
||||
entry.is_ignored =
|
||||
ignore_stack.is_abs_path_ignored(&entry_abs_path, entry.is_dir());
|
||||
self.is_abs_path_ignored(&entry_abs_path, &ignore_stack, entry.is_dir());
|
||||
|
||||
if entry.is_dir() {
|
||||
if let Some(job) = new_jobs.next().expect("missing scan job for entry") {
|
||||
@ -3523,7 +3594,8 @@ impl BackgroundScanner {
|
||||
}
|
||||
|
||||
if child_entry.is_dir() {
|
||||
child_entry.is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, true);
|
||||
child_entry.is_ignored =
|
||||
self.is_abs_path_ignored(&child_abs_path, &ignore_stack, true);
|
||||
|
||||
// Avoid recursing until crash in the case of a recursive symlink
|
||||
if !job.ancestor_inodes.contains(&child_entry.inode) {
|
||||
@ -3547,7 +3619,8 @@ impl BackgroundScanner {
|
||||
new_jobs.push(None);
|
||||
}
|
||||
} else {
|
||||
child_entry.is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, false);
|
||||
child_entry.is_ignored =
|
||||
self.is_abs_path_ignored(&child_abs_path, &ignore_stack, false);
|
||||
if !child_entry.is_ignored {
|
||||
if let Some((repository_dir, repository, staged_statuses)) =
|
||||
&job.containing_repository
|
||||
@ -3825,7 +3898,7 @@ impl BackgroundScanner {
|
||||
for mut entry in snapshot.child_entries(path).cloned() {
|
||||
let was_ignored = entry.is_ignored;
|
||||
let abs_path: Arc<Path> = snapshot.abs_path().join(&entry.path).into();
|
||||
entry.is_ignored = ignore_stack.is_abs_path_ignored(&abs_path, entry.is_dir());
|
||||
entry.is_ignored = self.is_abs_path_ignored(&abs_path, &ignore_stack, entry.is_dir());
|
||||
if entry.is_dir() {
|
||||
let child_ignore_stack = if entry.is_ignored {
|
||||
IgnoreStack::all()
|
||||
@ -4008,6 +4081,18 @@ impl BackgroundScanner {
|
||||
|
||||
smol::Timer::after(Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
fn is_abs_path_ignored(
|
||||
&self,
|
||||
abs_path: &Path,
|
||||
ignore_stack: &IgnoreStack,
|
||||
is_dir: bool,
|
||||
) -> bool {
|
||||
self.state
|
||||
.lock()
|
||||
.snapshot
|
||||
.is_abs_path_ignored(abs_path, ignore_stack, is_dir)
|
||||
}
|
||||
}
|
||||
|
||||
fn char_bag_for_path(root_char_bag: CharBag, path: &Path) -> CharBag {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
project_settings::ProjectSettings,
|
||||
worktree::{Event, Snapshot, WorktreeModelHandle},
|
||||
Entry, EntryKind, PathChange, Worktree,
|
||||
Entry, EntryKind, PathChange, Project, Worktree,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use client::Client;
|
||||
@ -12,6 +13,7 @@ use postage::stream::Stream;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rand::prelude::*;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use std::{
|
||||
env,
|
||||
fmt::Write,
|
||||
@ -877,6 +879,87 @@ async fn test_write_file(cx: &mut TestAppContext) {
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_ignore_inclusions_and_exclusions(cx: &mut TestAppContext) {
|
||||
let dir = temp_tree(json!({
|
||||
".git": {},
|
||||
".gitignore": "**/target\n/node_modules\n",
|
||||
"target": {},
|
||||
"node_modules": {
|
||||
".DS_Store": "",
|
||||
"prettier": {
|
||||
"package.json": "{}",
|
||||
},
|
||||
},
|
||||
"src": {
|
||||
".DS_Store": "",
|
||||
"foo": {
|
||||
"foo.rs": "mod another;\n",
|
||||
"another.rs": "// another",
|
||||
},
|
||||
"bar": {
|
||||
"bar.rs": "// bar",
|
||||
},
|
||||
"lib.rs": "mod foo;\nmod bar;\n",
|
||||
},
|
||||
".DS_Store": "",
|
||||
}));
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
Project::init_settings(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
||||
project_settings.scan_exclude_files =
|
||||
vec!["**/foo/**".to_string(), "**/.DS_Store".to_string()];
|
||||
project_settings.scan_include_files = vec!["**/node_modules".to_string()];
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let tree = Worktree::local(
|
||||
build_client(cx),
|
||||
dir.path(),
|
||||
true,
|
||||
Arc::new(RealFs),
|
||||
Default::default(),
|
||||
&mut cx.to_async(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
|
||||
.await;
|
||||
tree.flush_fs_events(cx).await;
|
||||
|
||||
// tree.update(cx, |tree, cx| {
|
||||
// tree.as_local().unwrap().write_file(
|
||||
// Path::new("tracked-dir/file.txt"),
|
||||
// "hello".into(),
|
||||
// Default::default(),
|
||||
// cx,
|
||||
// )
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
// tree.update(cx, |tree, cx| {
|
||||
// tree.as_local().unwrap().write_file(
|
||||
// Path::new("ignored-dir/file.txt"),
|
||||
// "world".into(),
|
||||
// Default::default(),
|
||||
// cx,
|
||||
// )
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// tree.read_with(cx, |tree, _| {
|
||||
// let tracked = tree.entry_for_path("tracked-dir/file.txt").unwrap();
|
||||
// let ignored = tree.entry_for_path("ignored-dir/file.txt").unwrap();
|
||||
// assert!(!tracked.is_ignored);
|
||||
// assert!(ignored.is_ignored);
|
||||
// });
|
||||
dbg!("!!!!!!!!!!!!");
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 30)]
|
||||
async fn test_create_directory_during_initial_scan(cx: &mut TestAppContext) {
|
||||
let fs = FakeFs::new(cx.background());
|
||||
|
Loading…
Reference in New Issue
Block a user