worktree_settings.rs

 1use std::path::Path;
 2
 3use anyhow::Context as _;
 4use gpui::App;
 5use settings::{Settings, SettingsContent};
 6use util::{ResultExt, paths::PathMatcher};
 7
 8#[derive(Clone, PartialEq, Eq)]
 9pub struct WorktreeSettings {
10    pub project_name: Option<String>,
11    pub file_scan_inclusions: PathMatcher,
12    pub file_scan_exclusions: PathMatcher,
13    pub private_files: PathMatcher,
14}
15
16impl WorktreeSettings {
17    pub fn is_path_private(&self, path: &Path) -> bool {
18        path.ancestors()
19            .any(|ancestor| self.private_files.is_match(ancestor))
20    }
21
22    pub fn is_path_excluded(&self, path: &Path) -> bool {
23        path.ancestors()
24            .any(|ancestor| self.file_scan_exclusions.is_match(&ancestor))
25    }
26
27    pub fn is_path_always_included(&self, path: &Path) -> bool {
28        path.ancestors()
29            .any(|ancestor| self.file_scan_inclusions.is_match(&ancestor))
30    }
31}
32
33impl Settings for WorktreeSettings {
34    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
35        let worktree = content.project.worktree.clone();
36        let file_scan_exclusions = worktree.file_scan_exclusions.unwrap();
37        let file_scan_inclusions = worktree.file_scan_inclusions.unwrap();
38        let private_files = worktree.private_files.unwrap().0;
39        let parsed_file_scan_inclusions: Vec<String> = file_scan_inclusions
40            .iter()
41            .flat_map(|glob| {
42                Path::new(glob)
43                    .ancestors()
44                    .map(|a| a.to_string_lossy().into())
45            })
46            .filter(|p: &String| !p.is_empty())
47            .collect();
48
49        Self {
50            project_name: None,
51            file_scan_exclusions: path_matchers(file_scan_exclusions, "file_scan_exclusions")
52                .log_err()
53                .unwrap_or_default(),
54            file_scan_inclusions: path_matchers(
55                parsed_file_scan_inclusions,
56                "file_scan_inclusions",
57            )
58            .unwrap(),
59            private_files: path_matchers(private_files, "private_files")
60                .log_err()
61                .unwrap_or_default(),
62        }
63    }
64
65    fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
66        if let Some(inclusions) = vscode
67            .read_value("files.watcherInclude")
68            .and_then(|v| v.as_array())
69            .and_then(|v| v.iter().map(|n| n.as_str().map(str::to_owned)).collect())
70        {
71            if let Some(old) = current.project.worktree.file_scan_inclusions.as_mut() {
72                old.extend(inclusions)
73            } else {
74                current.project.worktree.file_scan_inclusions = Some(inclusions)
75            }
76        }
77        if let Some(exclusions) = vscode
78            .read_value("files.watcherExclude")
79            .and_then(|v| v.as_array())
80            .and_then(|v| v.iter().map(|n| n.as_str().map(str::to_owned)).collect())
81        {
82            if let Some(old) = current.project.worktree.file_scan_exclusions.as_mut() {
83                old.extend(exclusions)
84            } else {
85                current.project.worktree.file_scan_exclusions = Some(exclusions)
86            }
87        }
88    }
89}
90
91fn path_matchers(mut values: Vec<String>, context: &'static str) -> anyhow::Result<PathMatcher> {
92    values.sort();
93    PathMatcher::new(values).with_context(|| format!("Failed to parse globs from {}", context))
94}