Settings refactor (#38367)
Co-Authored-By: Ben K <ben@zed.dev> Co-Authored-By: Anthony <anthony@zed.dev> Co-Authored-By: Mikayla <mikayla@zed.dev> Release Notes: - settings: Major internal changes to settings. The primary user-facing effect is that some settings which did not make sense in project settings files are no-longer read from there. (For example the inline blame settings) --------- Co-authored-by: Ben Kunkle <ben@zed.dev> Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com> Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
co-authored by
Ben Kunkle
Mikayla Maki
Anthony
parent
0a9023bce0
commit
fcdab160f9
@@ -2,10 +2,8 @@ use std::path::Path;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use gpui::App;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
|
||||
use util::paths::PathMatcher;
|
||||
use settings::{Settings, SettingsContent};
|
||||
use util::{ResultExt, paths::PathMatcher};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct WorktreeSettings {
|
||||
@@ -32,57 +30,13 @@ impl WorktreeSettings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
|
||||
#[settings_key(None)]
|
||||
pub struct WorktreeSettingsContent {
|
||||
/// The displayed name of this project. If not set, the root directory name
|
||||
/// will be displayed.
|
||||
///
|
||||
/// Default: none
|
||||
#[serde(default)]
|
||||
pub project_name: Option<String>,
|
||||
|
||||
/// Completely ignore files matching globs from `file_scan_exclusions`. Overrides
|
||||
/// `file_scan_inclusions`.
|
||||
///
|
||||
/// Default: [
|
||||
/// "**/.git",
|
||||
/// "**/.svn",
|
||||
/// "**/.hg",
|
||||
/// "**/.jj",
|
||||
/// "**/CVS",
|
||||
/// "**/.DS_Store",
|
||||
/// "**/Thumbs.db",
|
||||
/// "**/.classpath",
|
||||
/// "**/.settings"
|
||||
/// ]
|
||||
#[serde(default)]
|
||||
pub file_scan_exclusions: Option<Vec<String>>,
|
||||
|
||||
/// Always include files that match these globs when scanning for files, even if they're
|
||||
/// ignored by git. This setting is overridden by `file_scan_exclusions`.
|
||||
/// Default: [
|
||||
/// ".env*",
|
||||
/// "docker-compose.*.yml",
|
||||
/// ]
|
||||
#[serde(default)]
|
||||
pub file_scan_inclusions: Option<Vec<String>>,
|
||||
|
||||
/// Treat the files matching these globs as `.env` files.
|
||||
/// Default: [ "**/.env*" ]
|
||||
pub private_files: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Settings for WorktreeSettings {
|
||||
type FileContent = WorktreeSettingsContent;
|
||||
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> anyhow::Result<Self> {
|
||||
let result: WorktreeSettingsContent = sources.json_merge()?;
|
||||
let mut file_scan_exclusions = result.file_scan_exclusions.unwrap_or_default();
|
||||
let mut private_files = result.private_files.unwrap_or_default();
|
||||
let mut parsed_file_scan_inclusions: Vec<String> = result
|
||||
.file_scan_inclusions
|
||||
.unwrap_or_default()
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let worktree = content.project.worktree.clone();
|
||||
let file_scan_exclusions = worktree.file_scan_exclusions.unwrap();
|
||||
let file_scan_inclusions = worktree.file_scan_inclusions.unwrap();
|
||||
let private_files = worktree.private_files.unwrap();
|
||||
let parsed_file_scan_inclusions: Vec<String> = file_scan_inclusions
|
||||
.iter()
|
||||
.flat_map(|glob| {
|
||||
Path::new(glob)
|
||||
@@ -91,30 +45,71 @@ impl Settings for WorktreeSettings {
|
||||
})
|
||||
.filter(|p: &String| !p.is_empty())
|
||||
.collect();
|
||||
file_scan_exclusions.sort();
|
||||
private_files.sort();
|
||||
parsed_file_scan_inclusions.sort();
|
||||
Ok(Self {
|
||||
file_scan_exclusions: path_matchers(&file_scan_exclusions, "file_scan_exclusions")?,
|
||||
private_files: path_matchers(&private_files, "private_files")?,
|
||||
|
||||
Self {
|
||||
project_name: None,
|
||||
file_scan_exclusions: path_matchers(file_scan_exclusions, "file_scan_exclusions")
|
||||
.unwrap(),
|
||||
file_scan_inclusions: path_matchers(
|
||||
&parsed_file_scan_inclusions,
|
||||
parsed_file_scan_inclusions,
|
||||
"file_scan_inclusions",
|
||||
)?,
|
||||
project_name: result.project_name,
|
||||
})
|
||||
)
|
||||
.unwrap(),
|
||||
private_files: path_matchers(private_files, "private_files").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
|
||||
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
|
||||
let worktree = &content.project.worktree;
|
||||
|
||||
if let Some(project_name) = worktree.project_name.clone() {
|
||||
self.project_name = Some(project_name);
|
||||
}
|
||||
|
||||
if let Some(mut private_files) = worktree.private_files.clone() {
|
||||
let sources = self.private_files.sources();
|
||||
private_files.extend_from_slice(sources);
|
||||
if let Some(matchers) = path_matchers(private_files, "private_files").log_err() {
|
||||
self.private_files = matchers;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(file_scan_exclusions) = worktree.file_scan_exclusions.clone() {
|
||||
if let Some(matchers) =
|
||||
path_matchers(file_scan_exclusions, "file_scan_exclusions").log_err()
|
||||
{
|
||||
self.file_scan_exclusions = matchers
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(file_scan_inclusions) = worktree.file_scan_inclusions.clone() {
|
||||
let parsed_file_scan_inclusions: Vec<String> = file_scan_inclusions
|
||||
.iter()
|
||||
.flat_map(|glob| {
|
||||
Path::new(glob)
|
||||
.ancestors()
|
||||
.map(|a| a.to_string_lossy().into())
|
||||
})
|
||||
.filter(|p: &String| !p.is_empty())
|
||||
.collect();
|
||||
if let Some(matchers) =
|
||||
path_matchers(parsed_file_scan_inclusions, "file_scan_inclusions").log_err()
|
||||
{
|
||||
self.file_scan_inclusions = matchers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
|
||||
if let Some(inclusions) = vscode
|
||||
.read_value("files.watcherInclude")
|
||||
.and_then(|v| v.as_array())
|
||||
.and_then(|v| v.iter().map(|n| n.as_str().map(str::to_owned)).collect())
|
||||
{
|
||||
if let Some(old) = current.file_scan_inclusions.as_mut() {
|
||||
if let Some(old) = current.project.worktree.file_scan_inclusions.as_mut() {
|
||||
old.extend(inclusions)
|
||||
} else {
|
||||
current.file_scan_inclusions = Some(inclusions)
|
||||
current.project.worktree.file_scan_inclusions = Some(inclusions)
|
||||
}
|
||||
}
|
||||
if let Some(exclusions) = vscode
|
||||
@@ -122,15 +117,16 @@ impl Settings for WorktreeSettings {
|
||||
.and_then(|v| v.as_array())
|
||||
.and_then(|v| v.iter().map(|n| n.as_str().map(str::to_owned)).collect())
|
||||
{
|
||||
if let Some(old) = current.file_scan_exclusions.as_mut() {
|
||||
if let Some(old) = current.project.worktree.file_scan_exclusions.as_mut() {
|
||||
old.extend(exclusions)
|
||||
} else {
|
||||
current.file_scan_exclusions = Some(exclusions)
|
||||
current.project.worktree.file_scan_exclusions = Some(exclusions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn path_matchers(values: &[String], context: &'static str) -> anyhow::Result<PathMatcher> {
|
||||
fn path_matchers(mut values: Vec<String>, context: &'static str) -> anyhow::Result<PathMatcher> {
|
||||
values.sort();
|
||||
PathMatcher::new(values).with_context(|| format!("Failed to parse globs from {}", context))
|
||||
}
|
||||
|
||||
@@ -770,9 +770,9 @@ async fn test_file_scan_inclusions(cx: &mut TestAppContext) {
|
||||
}));
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||
project_settings.file_scan_exclusions = Some(vec![]);
|
||||
project_settings.file_scan_inclusions = Some(vec![
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions = Some(vec![]);
|
||||
settings.project.worktree.file_scan_inclusions = Some(vec![
|
||||
"node_modules/**/package.json".to_string(),
|
||||
"**/.DS_Store".to_string(),
|
||||
]);
|
||||
@@ -836,9 +836,11 @@ async fn test_file_scan_exclusions_overrules_inclusions(cx: &mut TestAppContext)
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||
project_settings.file_scan_exclusions = Some(vec!["**/.DS_Store".to_string()]);
|
||||
project_settings.file_scan_inclusions = Some(vec!["**/.DS_Store".to_string()]);
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/.DS_Store".to_string()]);
|
||||
settings.project.worktree.file_scan_inclusions =
|
||||
Some(vec!["**/.DS_Store".to_string()]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -894,9 +896,10 @@ async fn test_file_scan_inclusions_reindexes_on_setting_change(cx: &mut TestAppC
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||
project_settings.file_scan_exclusions = Some(vec![]);
|
||||
project_settings.file_scan_inclusions = Some(vec!["node_modules/**".to_string()]);
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions = Some(vec![]);
|
||||
settings.project.worktree.file_scan_inclusions =
|
||||
Some(vec!["node_modules/**".to_string()]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -926,9 +929,9 @@ async fn test_file_scan_inclusions_reindexes_on_setting_change(cx: &mut TestAppC
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||
project_settings.file_scan_exclusions = Some(vec![]);
|
||||
project_settings.file_scan_inclusions = Some(vec![]);
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions = Some(vec![]);
|
||||
settings.project.worktree.file_scan_inclusions = Some(vec![]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -978,8 +981,8 @@ async fn test_file_scan_exclusions(cx: &mut TestAppContext) {
|
||||
}));
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||
project_settings.file_scan_exclusions =
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/foo/**".to_string(), "**/.DS_Store".to_string()]);
|
||||
});
|
||||
});
|
||||
@@ -1015,8 +1018,8 @@ async fn test_file_scan_exclusions(cx: &mut TestAppContext) {
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||
project_settings.file_scan_exclusions =
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/node_modules/**".to_string()]);
|
||||
});
|
||||
});
|
||||
@@ -1080,8 +1083,8 @@ async fn test_fs_events_in_exclusions(cx: &mut TestAppContext) {
|
||||
}));
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<WorktreeSettings>(cx, |project_settings| {
|
||||
project_settings.file_scan_exclusions = Some(vec![
|
||||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions = Some(vec![
|
||||
"**/.git".to_string(),
|
||||
"node_modules/".to_string(),
|
||||
"build_output".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user