worktree_settings.rs

 1use std::{path::Path, sync::Arc};
 2
 3use gpui::AppContext;
 4use schemars::JsonSchema;
 5use serde::{Deserialize, Serialize};
 6use settings::{Settings, SettingsSources};
 7use util::paths::PathMatcher;
 8
 9#[derive(Clone, PartialEq, Eq)]
10pub struct WorktreeSettings {
11    pub file_scan_exclusions: Arc<[PathMatcher]>,
12    pub private_files: Arc<[PathMatcher]>,
13}
14
15impl WorktreeSettings {
16    pub fn is_path_private(&self, path: &Path) -> bool {
17        path.ancestors().any(|ancestor| {
18            self.private_files
19                .iter()
20                .any(|matcher| matcher.is_match(&ancestor))
21        })
22    }
23
24    pub fn is_path_excluded(&self, path: &Path) -> bool {
25        path.ancestors().any(|ancestor| {
26            self.file_scan_exclusions
27                .iter()
28                .any(|matcher| matcher.is_match(&ancestor))
29        })
30    }
31}
32
33#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
34pub struct WorktreeSettingsContent {
35    /// Completely ignore files matching globs from `file_scan_exclusions`
36    ///
37    /// Default: [
38    ///   "**/.git",
39    ///   "**/.svn",
40    ///   "**/.hg",
41    ///   "**/CVS",
42    ///   "**/.DS_Store",
43    ///   "**/Thumbs.db",
44    ///   "**/.classpath",
45    ///   "**/.settings"
46    /// ]
47    #[serde(default)]
48    pub file_scan_exclusions: Option<Vec<String>>,
49
50    /// Treat the files matching these globs as `.env` files.
51    /// Default: [ "**/.env*" ]
52    pub private_files: Option<Vec<String>>,
53}
54
55impl Settings for WorktreeSettings {
56    const KEY: Option<&'static str> = None;
57
58    type FileContent = WorktreeSettingsContent;
59
60    fn load(
61        sources: SettingsSources<Self::FileContent>,
62        _: &mut AppContext,
63    ) -> anyhow::Result<Self> {
64        let result: WorktreeSettingsContent = sources.json_merge()?;
65        let mut file_scan_exclusions = result.file_scan_exclusions.unwrap_or_default();
66        let mut private_files = result.private_files.unwrap_or_default();
67        file_scan_exclusions.sort();
68        private_files.sort();
69        Ok(Self {
70            file_scan_exclusions: path_matchers(&file_scan_exclusions, "file_scan_exclusions"),
71            private_files: path_matchers(&private_files, "private_files"),
72        })
73    }
74}
75
76fn path_matchers(values: &[String], context: &'static str) -> Arc<[PathMatcher]> {
77    values
78        .iter()
79        .filter_map(|pattern| {
80            PathMatcher::new(pattern)
81                .map(Some)
82                .unwrap_or_else(|e| {
83                    log::error!(
84                        "Skipping pattern {pattern} in `{}` project settings due to parsing error: {e:#}", context
85                    );
86                    None
87                })
88        })
89        .collect::<Vec<_>>()
90        .into()
91}