mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Rename 'project_core' crate to 'worktree', make it just about worktrees (#9189)
This is just a refactor. I noticed that we now have a `project_core` crate, which mainly contains the `Worktree` type and its private helpers, plus the project's settings. In this PR, I've renamed that crate to `worktree` and did some minor simplification to its module structure. I also extracted a new `WorktreeSettings` settings type from the `ProjectSettings`, so that the worktree settings could live in the worktree crate. This way, the crate is now exclusively about worktree logic. Release Notes: - N/A
This commit is contained in:
parent
2b67bb27cf
commit
dfcc143ead
71
Cargo.lock
generated
71
Cargo.lock
generated
@ -7225,11 +7225,11 @@ dependencies = [
|
|||||||
"postage",
|
"postage",
|
||||||
"prettier",
|
"prettier",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"project_core",
|
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
"release_channel",
|
"release_channel",
|
||||||
"rpc",
|
"rpc",
|
||||||
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
@ -7242,40 +7242,7 @@ dependencies = [
|
|||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
"which 6.0.0",
|
"which 6.0.0",
|
||||||
]
|
"worktree",
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "project_core"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"client",
|
|
||||||
"clock",
|
|
||||||
"collections",
|
|
||||||
"fs",
|
|
||||||
"futures 0.3.28",
|
|
||||||
"fuzzy",
|
|
||||||
"git",
|
|
||||||
"git2",
|
|
||||||
"gpui",
|
|
||||||
"ignore",
|
|
||||||
"itertools 0.11.0",
|
|
||||||
"language",
|
|
||||||
"log",
|
|
||||||
"lsp",
|
|
||||||
"parking_lot 0.11.2",
|
|
||||||
"postage",
|
|
||||||
"pretty_assertions",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"rpc",
|
|
||||||
"schemars",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"settings",
|
|
||||||
"smol",
|
|
||||||
"sum_tree",
|
|
||||||
"text",
|
|
||||||
"util",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -12497,6 +12464,40 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "worktree"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"client",
|
||||||
|
"clock",
|
||||||
|
"collections",
|
||||||
|
"fs",
|
||||||
|
"futures 0.3.28",
|
||||||
|
"fuzzy",
|
||||||
|
"git",
|
||||||
|
"git2",
|
||||||
|
"gpui",
|
||||||
|
"ignore",
|
||||||
|
"itertools 0.11.0",
|
||||||
|
"language",
|
||||||
|
"log",
|
||||||
|
"lsp",
|
||||||
|
"parking_lot 0.11.2",
|
||||||
|
"postage",
|
||||||
|
"pretty_assertions",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"rpc",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"settings",
|
||||||
|
"smol",
|
||||||
|
"sum_tree",
|
||||||
|
"text",
|
||||||
|
"util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ws2_32-sys"
|
name = "ws2_32-sys"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -54,7 +54,6 @@ members = [
|
|||||||
"crates/picker",
|
"crates/picker",
|
||||||
"crates/prettier",
|
"crates/prettier",
|
||||||
"crates/project",
|
"crates/project",
|
||||||
"crates/project_core",
|
|
||||||
"crates/project_panel",
|
"crates/project_panel",
|
||||||
"crates/project_symbols",
|
"crates/project_symbols",
|
||||||
"crates/quick_action_bar",
|
"crates/quick_action_bar",
|
||||||
@ -90,6 +89,7 @@ members = [
|
|||||||
"crates/vim",
|
"crates/vim",
|
||||||
"crates/welcome",
|
"crates/welcome",
|
||||||
"crates/workspace",
|
"crates/workspace",
|
||||||
|
"crates/worktree",
|
||||||
"crates/zed",
|
"crates/zed",
|
||||||
"crates/zed_actions",
|
"crates/zed_actions",
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ plugin = { path = "crates/plugin" }
|
|||||||
plugin_macros = { path = "crates/plugin_macros" }
|
plugin_macros = { path = "crates/plugin_macros" }
|
||||||
prettier = { path = "crates/prettier" }
|
prettier = { path = "crates/prettier" }
|
||||||
project = { path = "crates/project" }
|
project = { path = "crates/project" }
|
||||||
project_core = { path = "crates/project_core" }
|
worktree = { path = "crates/worktree" }
|
||||||
project_panel = { path = "crates/project_panel" }
|
project_panel = { path = "crates/project_panel" }
|
||||||
project_symbols = { path = "crates/project_symbols" }
|
project_symbols = { path = "crates/project_symbols" }
|
||||||
quick_action_bar = { path = "crates/quick_action_bar" }
|
quick_action_bar = { path = "crates/quick_action_bar" }
|
||||||
|
@ -12,7 +12,7 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use language::Capability;
|
use language::Capability;
|
||||||
use live_kit_client::MacOSDisplay;
|
use live_kit_client::MacOSDisplay;
|
||||||
use project::project_settings::ProjectSettings;
|
use project::WorktreeSettings;
|
||||||
use rpc::proto::PeerId;
|
use rpc::proto::PeerId;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
@ -1646,8 +1646,8 @@ async fn test_following_into_excluded_file(
|
|||||||
for cx in [&mut cx_a, &mut cx_b] {
|
for cx in [&mut cx_a, &mut cx_b] {
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |settings| {
|
||||||
project_settings.file_scan_exclusions = Some(vec!["**/.git".to_string()]);
|
settings.file_scan_exclusions = Some(vec!["**/.git".to_string()]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3245,7 +3245,7 @@ impl Editor {
|
|||||||
.filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
|
.filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
|
||||||
.filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
|
.filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
|
||||||
let buffer = buffer_handle.read(cx);
|
let buffer = buffer_handle.read(cx);
|
||||||
let buffer_file = project::worktree::File::from_dyn(buffer.file())?;
|
let buffer_file = project::File::from_dyn(buffer.file())?;
|
||||||
let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
|
let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
|
||||||
let worktree_entry = buffer_worktree
|
let worktree_entry = buffer_worktree
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -10,6 +10,7 @@ workspace = true
|
|||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "src/extension_store.rs"
|
path = "src/extension_store.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "extension_json_schemas"
|
name = "extension_json_schemas"
|
||||||
|
@ -260,13 +260,6 @@ impl ExtensionBuilder {
|
|||||||
.args(["fetch", "--depth", "1", "origin", &rev])
|
.args(["fetch", "--depth", "1", "origin", &rev])
|
||||||
.output()
|
.output()
|
||||||
.context("failed to execute `git fetch`")?;
|
.context("failed to execute `git fetch`")?;
|
||||||
if !fetch_output.status.success() {
|
|
||||||
bail!(
|
|
||||||
"failed to fetch revision {} in directory '{}'",
|
|
||||||
rev,
|
|
||||||
directory.display()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let checkout_output = Command::new("git")
|
let checkout_output = Command::new("git")
|
||||||
.arg("--git-dir")
|
.arg("--git-dir")
|
||||||
@ -276,6 +269,13 @@ impl ExtensionBuilder {
|
|||||||
.output()
|
.output()
|
||||||
.context("failed to execute `git checkout`")?;
|
.context("failed to execute `git checkout`")?;
|
||||||
if !checkout_output.status.success() {
|
if !checkout_output.status.success() {
|
||||||
|
if !fetch_output.status.success() {
|
||||||
|
bail!(
|
||||||
|
"failed to fetch revision {} in directory '{}'",
|
||||||
|
rev,
|
||||||
|
directory.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
bail!(
|
bail!(
|
||||||
"failed to checkout revision {} in directory '{}'",
|
"failed to checkout revision {} in directory '{}'",
|
||||||
rev,
|
rev,
|
||||||
|
@ -4,7 +4,7 @@ use super::*;
|
|||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use gpui::{Entity, TestAppContext, VisualTestContext};
|
use gpui::{Entity, TestAppContext, VisualTestContext};
|
||||||
use menu::{Confirm, SelectNext};
|
use menu::{Confirm, SelectNext};
|
||||||
use project::worktree::FS_WATCH_LATENCY;
|
use project::FS_WATCH_LATENCY;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use workspace::{AppState, Workspace};
|
use workspace::{AppState, Workspace};
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ test-support = [
|
|||||||
"settings/test-support",
|
"settings/test-support",
|
||||||
"text/test-support",
|
"text/test-support",
|
||||||
"prettier/test-support",
|
"prettier/test-support",
|
||||||
"project_core/test-support",
|
"worktree/test-support",
|
||||||
"gpui/test-support",
|
"gpui/test-support",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -44,10 +44,11 @@ node_runtime.workspace = true
|
|||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
postage.workspace = true
|
postage.workspace = true
|
||||||
prettier.workspace = true
|
prettier.workspace = true
|
||||||
project_core.workspace = true
|
worktree.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
rpc.workspace = true
|
rpc.workspace = true
|
||||||
|
schemars.workspace = true
|
||||||
task.workspace = true
|
task.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
@ -72,7 +73,7 @@ release_channel.workspace = true
|
|||||||
lsp = { workspace = true, features = ["test-support"] }
|
lsp = { workspace = true, features = ["test-support"] }
|
||||||
prettier = { workspace = true, features = ["test-support"] }
|
prettier = { workspace = true, features = ["test-support"] }
|
||||||
pretty_assertions.workspace = true
|
pretty_assertions.workspace = true
|
||||||
project_core = { workspace = true, features = ["test-support"] }
|
worktree = { workspace = true, features = ["test-support"] }
|
||||||
rpc = { workspace = true, features = ["test-support"] }
|
rpc = { workspace = true, features = ["test-support"] }
|
||||||
settings = { workspace = true, features = ["test-support"] }
|
settings = { workspace = true, features = ["test-support"] }
|
||||||
unindent.workspace = true
|
unindent.workspace = true
|
||||||
|
@ -2,6 +2,7 @@ pub mod debounced_delay;
|
|||||||
pub mod lsp_command;
|
pub mod lsp_command;
|
||||||
pub mod lsp_ext_command;
|
pub mod lsp_ext_command;
|
||||||
mod prettier_support;
|
mod prettier_support;
|
||||||
|
pub mod project_settings;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
mod task_inventory;
|
mod task_inventory;
|
||||||
pub mod terminals;
|
pub mod terminals;
|
||||||
@ -56,9 +57,9 @@ use node_runtime::NodeRuntime;
|
|||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
use prettier_support::{DefaultPrettier, PrettierInstance};
|
use prettier_support::{DefaultPrettier, PrettierInstance};
|
||||||
use project_core::project_settings::{LspSettings, ProjectSettings};
|
use project_settings::{LspSettings, ProjectSettings};
|
||||||
pub use project_core::{DiagnosticSummary, ProjectEntryId};
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use worktree::LocalSnapshot;
|
||||||
|
|
||||||
use rpc::{ErrorCode, ErrorExt as _};
|
use rpc::{ErrorCode, ErrorExt as _};
|
||||||
use search::SearchQuery;
|
use search::SearchQuery;
|
||||||
@ -96,16 +97,20 @@ use util::{
|
|||||||
paths::{LOCAL_SETTINGS_RELATIVE_PATH, LOCAL_TASKS_RELATIVE_PATH},
|
paths::{LOCAL_SETTINGS_RELATIVE_PATH, LOCAL_TASKS_RELATIVE_PATH},
|
||||||
post_inc, ResultExt, TryFutureExt as _,
|
post_inc, ResultExt, TryFutureExt as _,
|
||||||
};
|
};
|
||||||
|
use worktree::{Snapshot, Traversal};
|
||||||
|
|
||||||
pub use fs::*;
|
pub use fs::*;
|
||||||
pub use language::Location;
|
pub use language::Location;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
|
pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
|
||||||
pub use project_core::project_settings;
|
|
||||||
pub use project_core::worktree::{self, *};
|
|
||||||
#[cfg(feature = "test-support")]
|
#[cfg(feature = "test-support")]
|
||||||
pub use task_inventory::test_inventory::*;
|
pub use task_inventory::test_inventory::*;
|
||||||
pub use task_inventory::{Inventory, TaskSourceKind};
|
pub use task_inventory::{Inventory, TaskSourceKind};
|
||||||
|
pub use worktree::{
|
||||||
|
DiagnosticSummary, Entry, EntryKind, File, LocalWorktree, PathChange, ProjectEntryId,
|
||||||
|
RepositoryEntry, UpdatedEntriesSet, UpdatedGitRepositoriesSet, Worktree, WorktreeId,
|
||||||
|
WorktreeSettings, FS_WATCH_LATENCY,
|
||||||
|
};
|
||||||
|
|
||||||
const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
|
const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
|
||||||
const SERVER_REINSTALL_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
|
const SERVER_REINSTALL_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
|
||||||
@ -492,6 +497,7 @@ impl SearchMatchCandidate {
|
|||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
pub fn init_settings(cx: &mut AppContext) {
|
pub fn init_settings(cx: &mut AppContext) {
|
||||||
|
WorktreeSettings::register(cx);
|
||||||
ProjectSettings::register(cx);
|
ProjectSettings::register(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,25 +20,6 @@ pub struct ProjectSettings {
|
|||||||
/// Configuration for Git-related features
|
/// Configuration for Git-related features
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub git: GitSettings,
|
pub git: GitSettings,
|
||||||
|
|
||||||
/// Completely ignore files matching globs from `file_scan_exclusions`
|
|
||||||
///
|
|
||||||
/// Default: [
|
|
||||||
/// "**/.git",
|
|
||||||
/// "**/.svn",
|
|
||||||
/// "**/.hg",
|
|
||||||
/// "**/CVS",
|
|
||||||
/// "**/.DS_Store",
|
|
||||||
/// "**/Thumbs.db",
|
|
||||||
/// "**/.classpath",
|
|
||||||
/// "**/.settings"
|
|
||||||
/// ]
|
|
||||||
#[serde(default)]
|
|
||||||
pub file_scan_exclusions: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// Treat the files matching these globs as `.env` files.
|
|
||||||
/// Default: [ "**/.env*" ]
|
|
||||||
pub private_files: Option<Vec<String>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
@ -14,6 +14,7 @@ use serde_json::json;
|
|||||||
use std::{os, task::Poll};
|
use std::{os, task::Poll};
|
||||||
use unindent::Unindent as _;
|
use unindent::Unindent as _;
|
||||||
use util::{assert_set_eq, paths::PathMatcher, test::temp_tree};
|
use util::{assert_set_eq, paths::PathMatcher, test::temp_tree};
|
||||||
|
use worktree::WorktreeModelHandle as _;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_block_via_channel(cx: &mut gpui::TestAppContext) {
|
async fn test_block_via_channel(cx: &mut gpui::TestAppContext) {
|
||||||
|
@ -9,9 +9,9 @@ use std::{
|
|||||||
use collections::{HashMap, VecDeque};
|
use collections::{HashMap, VecDeque};
|
||||||
use gpui::{AppContext, Context, Model, ModelContext, Subscription};
|
use gpui::{AppContext, Context, Model, ModelContext, Subscription};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use project_core::worktree::WorktreeId;
|
|
||||||
use task::{Task, TaskContext, TaskId, TaskSource};
|
use task::{Task, TaskContext, TaskId, TaskSource};
|
||||||
use util::{post_inc, NumericPrefixWithSuffix};
|
use util::{post_inc, NumericPrefixWithSuffix};
|
||||||
|
use worktree::WorktreeId;
|
||||||
|
|
||||||
/// Inventory tracks available tasks for a given project.
|
/// Inventory tracks available tasks for a given project.
|
||||||
pub struct Inventory {
|
pub struct Inventory {
|
||||||
@ -230,8 +230,8 @@ pub mod test_inventory {
|
|||||||
use std::{path::Path, sync::Arc};
|
use std::{path::Path, sync::Arc};
|
||||||
|
|
||||||
use gpui::{AppContext, Context as _, Model, ModelContext, TestAppContext};
|
use gpui::{AppContext, Context as _, Model, ModelContext, TestAppContext};
|
||||||
use project_core::worktree::WorktreeId;
|
|
||||||
use task::{Task, TaskContext, TaskId, TaskSource};
|
use task::{Task, TaskContext, TaskId, TaskSource};
|
||||||
|
use worktree::WorktreeId;
|
||||||
|
|
||||||
use crate::Inventory;
|
use crate::Inventory;
|
||||||
|
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
use std::path::Path;
|
|
||||||
use std::sync::atomic::AtomicUsize;
|
|
||||||
use std::sync::atomic::Ordering::SeqCst;
|
|
||||||
|
|
||||||
use language::DiagnosticEntry;
|
|
||||||
use lsp::{DiagnosticSeverity, LanguageServerId};
|
|
||||||
use rpc::proto;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
mod ignore;
|
|
||||||
pub mod project_settings;
|
|
||||||
pub mod worktree;
|
|
||||||
#[cfg(test)]
|
|
||||||
mod worktree_tests;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct ProjectEntryId(usize);
|
|
||||||
|
|
||||||
impl ProjectEntryId {
|
|
||||||
pub const MAX: Self = Self(usize::MAX);
|
|
||||||
|
|
||||||
pub fn new(counter: &AtomicUsize) -> Self {
|
|
||||||
Self(counter.fetch_add(1, SeqCst))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_proto(id: u64) -> Self {
|
|
||||||
Self(id as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_proto(&self) -> u64 {
|
|
||||||
self.0 as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_usize(&self) -> usize {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
|
|
||||||
pub struct DiagnosticSummary {
|
|
||||||
pub error_count: usize,
|
|
||||||
pub warning_count: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DiagnosticSummary {
|
|
||||||
fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
|
|
||||||
let mut this = Self {
|
|
||||||
error_count: 0,
|
|
||||||
warning_count: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
for entry in diagnostics {
|
|
||||||
if entry.diagnostic.is_primary {
|
|
||||||
match entry.diagnostic.severity {
|
|
||||||
DiagnosticSeverity::ERROR => this.error_count += 1,
|
|
||||||
DiagnosticSeverity::WARNING => this.warning_count += 1,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.error_count == 0 && self.warning_count == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_proto(
|
|
||||||
&self,
|
|
||||||
language_server_id: LanguageServerId,
|
|
||||||
path: &Path,
|
|
||||||
) -> proto::DiagnosticSummary {
|
|
||||||
proto::DiagnosticSummary {
|
|
||||||
path: path.to_string_lossy().to_string(),
|
|
||||||
language_server_id: language_server_id.0 as u64,
|
|
||||||
error_count: self.error_count as u32,
|
|
||||||
warning_count: self.warning_count as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1717,7 +1717,7 @@ mod tests {
|
|||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use gpui::{TestAppContext, View, VisualTestContext, WindowHandle};
|
use gpui::{TestAppContext, View, VisualTestContext, WindowHandle};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use project::{project_settings::ProjectSettings, FakeFs};
|
use project::{FakeFs, WorktreeSettings};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -1819,8 +1819,8 @@ mod tests {
|
|||||||
init_test(cx);
|
init_test(cx);
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
|
||||||
project_settings.file_scan_exclusions =
|
worktree_settings.file_scan_exclusions =
|
||||||
Some(vec!["**/.git".to_string(), "**/4/**".to_string()]);
|
Some(vec!["**/.git".to_string(), "**/4/**".to_string()]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -3026,8 +3026,8 @@ mod tests {
|
|||||||
init_test_with_editor(cx);
|
init_test_with_editor(cx);
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
|
||||||
project_settings.file_scan_exclusions = Some(Vec::new());
|
worktree_settings.file_scan_exclusions = Some(Vec::new());
|
||||||
});
|
});
|
||||||
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
|
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
|
||||||
project_panel_settings.auto_reveal_entries = Some(false)
|
project_panel_settings.auto_reveal_entries = Some(false)
|
||||||
@ -3264,8 +3264,8 @@ mod tests {
|
|||||||
init_test_with_editor(cx);
|
init_test_with_editor(cx);
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
|
||||||
project_settings.file_scan_exclusions = Some(Vec::new());
|
worktree_settings.file_scan_exclusions = Some(Vec::new());
|
||||||
});
|
});
|
||||||
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
|
store.update_user_settings::<ProjectPanelSettings>(cx, |project_panel_settings| {
|
||||||
project_panel_settings.auto_reveal_entries = Some(false)
|
project_panel_settings.auto_reveal_entries = Some(false)
|
||||||
@ -3582,8 +3582,8 @@ mod tests {
|
|||||||
Project::init_settings(cx);
|
Project::init_settings(cx);
|
||||||
|
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |worktree_settings| {
|
||||||
project_settings.file_scan_exclusions = Some(Vec::new());
|
worktree_settings.file_scan_exclusions = Some(Vec::new());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@ use gpui::{Task, TestAppContext};
|
|||||||
use language::{Language, LanguageConfig, LanguageMatcher, LanguageRegistry, ToOffset};
|
use language::{Language, LanguageConfig, LanguageMatcher, LanguageRegistry, ToOffset};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use project::{project_settings::ProjectSettings, FakeFs, Fs, Project};
|
use project::{FakeFs, Fs, Project};
|
||||||
use rand::{rngs::StdRng, Rng};
|
use rand::{rngs::StdRng, Rng};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
@ -1720,6 +1720,6 @@ fn init_test(cx: &mut TestAppContext) {
|
|||||||
let settings_store = SettingsStore::test(cx);
|
let settings_store = SettingsStore::test(cx);
|
||||||
cx.set_global(settings_store);
|
cx.set_global(settings_store);
|
||||||
SemanticIndexSettings::register(cx);
|
SemanticIndexSettings::register(cx);
|
||||||
ProjectSettings::register(cx);
|
Project::init_settings(cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "project_core"
|
name = "worktree"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/worktree.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
[features]
|
[features]
|
||||||
test-support = [
|
test-support = [
|
||||||
"client/test-support",
|
"client/test-support",
|
@ -1,6 +1,8 @@
|
|||||||
use crate::{
|
mod ignore;
|
||||||
ignore::IgnoreStack, project_settings::ProjectSettings, DiagnosticSummary, ProjectEntryId,
|
mod worktree_settings;
|
||||||
};
|
#[cfg(test)]
|
||||||
|
mod worktree_tests;
|
||||||
|
|
||||||
use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
|
use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use client::{proto, Client};
|
use client::{proto, Client};
|
||||||
@ -26,6 +28,7 @@ use gpui::{
|
|||||||
AppContext, AsyncAppContext, BackgroundExecutor, Context, EventEmitter, Model, ModelContext,
|
AppContext, AsyncAppContext, BackgroundExecutor, Context, EventEmitter, Model, ModelContext,
|
||||||
Task,
|
Task,
|
||||||
};
|
};
|
||||||
|
use ignore::IgnoreStack;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{
|
use language::{
|
||||||
proto::{
|
proto::{
|
||||||
@ -35,13 +38,14 @@ use language::{
|
|||||||
Buffer, Capability, DiagnosticEntry, File as _, LineEnding, PointUtf16, Rope, RopeFingerprint,
|
Buffer, Capability, DiagnosticEntry, File as _, LineEnding, PointUtf16, Rope, RopeFingerprint,
|
||||||
Unclipped,
|
Unclipped,
|
||||||
};
|
};
|
||||||
use lsp::LanguageServerId;
|
use lsp::{DiagnosticSeverity, LanguageServerId};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use postage::{
|
use postage::{
|
||||||
barrier,
|
barrier,
|
||||||
prelude::{Sink as _, Stream as _},
|
prelude::{Sink as _, Stream as _},
|
||||||
watch,
|
watch,
|
||||||
};
|
};
|
||||||
|
use serde::Serialize;
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
use smol::channel::{self, Sender};
|
use smol::channel::{self, Sender};
|
||||||
use std::{
|
use std::{
|
||||||
@ -68,10 +72,12 @@ use util::{
|
|||||||
ResultExt,
|
ResultExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use worktree_settings::WorktreeSettings;
|
||||||
|
|
||||||
#[cfg(feature = "test-support")]
|
#[cfg(feature = "test-support")]
|
||||||
pub const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
|
pub const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
|
||||||
#[cfg(not(feature = "test-support"))]
|
#[cfg(not(feature = "test-support"))]
|
||||||
const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
|
pub const FS_WATCH_LATENCY: Duration = Duration::from_millis(100);
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
||||||
pub struct WorktreeId(usize);
|
pub struct WorktreeId(usize);
|
||||||
@ -340,13 +346,13 @@ impl Worktree {
|
|||||||
cx.observe_global::<SettingsStore>(move |this, cx| {
|
cx.observe_global::<SettingsStore>(move |this, cx| {
|
||||||
if let Self::Local(this) = this {
|
if let Self::Local(this) = this {
|
||||||
let new_file_scan_exclusions = path_matchers(
|
let new_file_scan_exclusions = path_matchers(
|
||||||
ProjectSettings::get_global(cx)
|
WorktreeSettings::get_global(cx)
|
||||||
.file_scan_exclusions
|
.file_scan_exclusions
|
||||||
.as_deref(),
|
.as_deref(),
|
||||||
"file_scan_exclusions",
|
"file_scan_exclusions",
|
||||||
);
|
);
|
||||||
let new_private_files = path_matchers(
|
let new_private_files = path_matchers(
|
||||||
ProjectSettings::get(Some((cx.handle().entity_id().as_u64() as usize, &Path::new(""))), cx).private_files.as_deref(),
|
WorktreeSettings::get(Some((cx.handle().entity_id().as_u64() as usize, &Path::new(""))), cx).private_files.as_deref(),
|
||||||
"private_files",
|
"private_files",
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -396,13 +402,13 @@ impl Worktree {
|
|||||||
|
|
||||||
let mut snapshot = LocalSnapshot {
|
let mut snapshot = LocalSnapshot {
|
||||||
file_scan_exclusions: path_matchers(
|
file_scan_exclusions: path_matchers(
|
||||||
ProjectSettings::get_global(cx)
|
WorktreeSettings::get_global(cx)
|
||||||
.file_scan_exclusions
|
.file_scan_exclusions
|
||||||
.as_deref(),
|
.as_deref(),
|
||||||
"file_scan_exclusions",
|
"file_scan_exclusions",
|
||||||
),
|
),
|
||||||
private_files: path_matchers(
|
private_files: path_matchers(
|
||||||
ProjectSettings::get(Some((cx.handle().entity_id().as_u64() as usize, &Path::new(""))), cx).private_files.as_deref(),
|
WorktreeSettings::get(Some((cx.handle().entity_id().as_u64() as usize, &Path::new(""))), cx).private_files.as_deref(),
|
||||||
"private_files",
|
"private_files",
|
||||||
),
|
),
|
||||||
ignores_by_parent_abs_path: Default::default(),
|
ignores_by_parent_abs_path: Default::default(),
|
||||||
@ -4737,3 +4743,70 @@ fn git_status_to_proto(status: GitFileStatus) -> i32 {
|
|||||||
GitFileStatus::Conflict => proto::GitStatus::Conflict as i32,
|
GitFileStatus::Conflict => proto::GitStatus::Conflict as i32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct ProjectEntryId(usize);
|
||||||
|
|
||||||
|
impl ProjectEntryId {
|
||||||
|
pub const MAX: Self = Self(usize::MAX);
|
||||||
|
|
||||||
|
pub fn new(counter: &AtomicUsize) -> Self {
|
||||||
|
Self(counter.fetch_add(1, SeqCst))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_proto(id: u64) -> Self {
|
||||||
|
Self(id as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_proto(&self) -> u64 {
|
||||||
|
self.0 as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_usize(&self) -> usize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
|
||||||
|
pub struct DiagnosticSummary {
|
||||||
|
pub error_count: usize,
|
||||||
|
pub warning_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiagnosticSummary {
|
||||||
|
fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
|
||||||
|
let mut this = Self {
|
||||||
|
error_count: 0,
|
||||||
|
warning_count: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in diagnostics {
|
||||||
|
if entry.diagnostic.is_primary {
|
||||||
|
match entry.diagnostic.severity {
|
||||||
|
DiagnosticSeverity::ERROR => this.error_count += 1,
|
||||||
|
DiagnosticSeverity::WARNING => this.warning_count += 1,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.error_count == 0 && self.warning_count == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_proto(
|
||||||
|
&self,
|
||||||
|
language_server_id: LanguageServerId,
|
||||||
|
path: &Path,
|
||||||
|
) -> proto::DiagnosticSummary {
|
||||||
|
proto::DiagnosticSummary {
|
||||||
|
path: path.to_string_lossy().to_string(),
|
||||||
|
language_server_id: language_server_id.0 as u64,
|
||||||
|
error_count: self.error_count as u32,
|
||||||
|
warning_count: self.warning_count as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
crates/worktree/src/worktree_settings.rs
Normal file
40
crates/worktree/src/worktree_settings.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use gpui::AppContext;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub struct WorktreeSettings {
|
||||||
|
/// Completely ignore files matching globs from `file_scan_exclusions`
|
||||||
|
///
|
||||||
|
/// Default: [
|
||||||
|
/// "**/.git",
|
||||||
|
/// "**/.svn",
|
||||||
|
/// "**/.hg",
|
||||||
|
/// "**/CVS",
|
||||||
|
/// "**/.DS_Store",
|
||||||
|
/// "**/Thumbs.db",
|
||||||
|
/// "**/.classpath",
|
||||||
|
/// "**/.settings"
|
||||||
|
/// ]
|
||||||
|
#[serde(default)]
|
||||||
|
pub file_scan_exclusions: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Treat the files matching these globs as `.env` files.
|
||||||
|
/// Default: [ "**/.env*" ]
|
||||||
|
pub private_files: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings for WorktreeSettings {
|
||||||
|
const KEY: Option<&'static str> = None;
|
||||||
|
|
||||||
|
type FileContent = Self;
|
||||||
|
|
||||||
|
fn load(
|
||||||
|
default_value: &Self::FileContent,
|
||||||
|
user_values: &[&Self::FileContent],
|
||||||
|
_: &mut AppContext,
|
||||||
|
) -> anyhow::Result<Self> {
|
||||||
|
Self::load_via_json_merge(default_value, user_values)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
project_settings::ProjectSettings,
|
worktree_settings::WorktreeSettings, Entry, EntryKind, Event, PathChange, Snapshot, Worktree,
|
||||||
worktree::{Entry, EntryKind, PathChange, Worktree},
|
WorktreeModelHandle,
|
||||||
worktree::{Event, Snapshot, WorktreeModelHandle},
|
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use client::Client;
|
use client::Client;
|
||||||
@ -804,7 +803,7 @@ async fn test_rescan_with_gitignore(cx: &mut TestAppContext) {
|
|||||||
init_test(cx);
|
init_test(cx);
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||||
project_settings.file_scan_exclusions = Some(Vec::new());
|
project_settings.file_scan_exclusions = Some(Vec::new());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -996,7 +995,7 @@ async fn test_file_scan_exclusions(cx: &mut TestAppContext) {
|
|||||||
}));
|
}));
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||||
project_settings.file_scan_exclusions =
|
project_settings.file_scan_exclusions =
|
||||||
Some(vec!["**/foo/**".to_string(), "**/.DS_Store".to_string()]);
|
Some(vec!["**/foo/**".to_string(), "**/.DS_Store".to_string()]);
|
||||||
});
|
});
|
||||||
@ -1033,7 +1032,7 @@ async fn test_file_scan_exclusions(cx: &mut TestAppContext) {
|
|||||||
|
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||||
project_settings.file_scan_exclusions =
|
project_settings.file_scan_exclusions =
|
||||||
Some(vec!["**/node_modules/**".to_string()]);
|
Some(vec!["**/node_modules/**".to_string()]);
|
||||||
});
|
});
|
||||||
@ -1097,7 +1096,7 @@ async fn test_fs_events_in_exclusions(cx: &mut TestAppContext) {
|
|||||||
}));
|
}));
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||||
project_settings.file_scan_exclusions = Some(vec![
|
project_settings.file_scan_exclusions = Some(vec![
|
||||||
"**/.git".to_string(),
|
"**/.git".to_string(),
|
||||||
"node_modules/".to_string(),
|
"node_modules/".to_string(),
|
||||||
@ -2541,6 +2540,6 @@ fn init_test(cx: &mut gpui::TestAppContext) {
|
|||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let settings_store = SettingsStore::test(cx);
|
let settings_store = SettingsStore::test(cx);
|
||||||
cx.set_global(settings_store);
|
cx.set_global(settings_store);
|
||||||
ProjectSettings::register(cx);
|
WorktreeSettings::register(cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -866,7 +866,7 @@ mod tests {
|
|||||||
VisualTestContext, WindowHandle,
|
VisualTestContext, WindowHandle,
|
||||||
};
|
};
|
||||||
use language::{LanguageMatcher, LanguageRegistry};
|
use language::{LanguageMatcher, LanguageRegistry};
|
||||||
use project::{project_settings::ProjectSettings, Project, ProjectPath};
|
use project::{Project, ProjectPath, WorktreeSettings};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::{handle_settings_file_changes, watch_config_file, SettingsStore};
|
use settings::{handle_settings_file_changes, watch_config_file, SettingsStore};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -1480,7 +1480,7 @@ mod tests {
|
|||||||
let app_state = init_test(cx);
|
let app_state = init_test(cx);
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||||
store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
|
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||||
project_settings.file_scan_exclusions =
|
project_settings.file_scan_exclusions =
|
||||||
Some(vec!["excluded_dir".to_string(), "**/.git".to_string()]);
|
Some(vec!["excluded_dir".to_string(), "**/.git".to_string()]);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user