worktree_settings.rs

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