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:
Max Brunsfeld 2024-03-11 11:35:27 -07:00 committed by GitHub
parent 2b67bb27cf
commit dfcc143ead
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 215 additions and 190 deletions

71
Cargo.lock generated
View File

@ -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"

View File

@ -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" }

View File

@ -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()]);
}); });
}); });
}); });

View File

@ -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)

View File

@ -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"

View File

@ -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,

View File

@ -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};

View File

@ -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

View File

@ -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);
} }

View File

@ -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)]

View File

@ -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) {

View File

@ -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;

View File

@ -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,
}
}
}

View File

@ -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());
}); });
}); });
}); });

View File

@ -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);
}); });
} }

View File

@ -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",

View File

@ -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,
}
}
}

View 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)
}
}

View File

@ -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);
}); });
} }

View File

@ -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()]);
}); });