Prepare to sync other kinds of settings (#18616)
This PR does not change how things work for settings, but lays the ground work for the future functionality. After this change, Zed is prepared to sync more than just `settings.json` files from local worktree and user config. * ssh tasks Part of this work is to streamline the task sync mechanism. Instead of having an extra set of requests to fetch the task contents from the server (as remote-via-collab does now and does not cover all sync cases), we want to reuse the existing mechanism for synchronizing user and local settings. * editorconfig Part of the task is to sync .editorconfig file changes to everyone which involves sending and storing those configs. Both ssh (and remove-over-collab) .zed/tasks.json and .editorconfig files behave similar to .zed/settings.json local files: they belong to a certain path in a certain worktree; may update over time, changing Zed's functionality; can be merged hierarchically. Settings sync follows the same "config file changed -> send to watchers -> parse and merge locally and on watchers" path that's needed for both new kinds of files, ergo the messaging layer is extended to send more types of settings for future watch & parse and merge impls to follow. Release Notes: - N/A
This commit is contained in:
parent
7c4615519b
commit
778dedec6c
12 changed files with 221 additions and 73 deletions
|
@ -112,6 +112,7 @@ CREATE TABLE "worktree_settings_files" (
|
|||
"worktree_id" INTEGER NOT NULL,
|
||||
"path" VARCHAR NOT NULL,
|
||||
"content" TEXT,
|
||||
"kind" VARCHAR,
|
||||
PRIMARY KEY(project_id, worktree_id, path),
|
||||
FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE "worktree_settings_files" ADD COLUMN "kind" VARCHAR;
|
|
@ -35,6 +35,7 @@ use std::{
|
|||
};
|
||||
use time::PrimitiveDateTime;
|
||||
use tokio::sync::{Mutex, OwnedMutexGuard};
|
||||
use worktree_settings_file::LocalSettingsKind;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use tests::TestDb;
|
||||
|
@ -766,6 +767,7 @@ pub struct Worktree {
|
|||
pub struct WorktreeSettingsFile {
|
||||
pub path: String,
|
||||
pub content: String,
|
||||
pub kind: LocalSettingsKind,
|
||||
}
|
||||
|
||||
pub struct NewExtensionVersion {
|
||||
|
@ -783,3 +785,21 @@ pub struct ExtensionVersionConstraints {
|
|||
pub schema_versions: RangeInclusive<i32>,
|
||||
pub wasm_api_versions: RangeInclusive<SemanticVersion>,
|
||||
}
|
||||
|
||||
impl LocalSettingsKind {
|
||||
pub fn from_proto(proto_kind: proto::LocalSettingsKind) -> Self {
|
||||
match proto_kind {
|
||||
proto::LocalSettingsKind::Settings => Self::Settings,
|
||||
proto::LocalSettingsKind::Tasks => Self::Tasks,
|
||||
proto::LocalSettingsKind::Editorconfig => Self::Editorconfig,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> proto::LocalSettingsKind {
|
||||
match self {
|
||||
Self::Settings => proto::LocalSettingsKind::Settings,
|
||||
Self::Tasks => proto::LocalSettingsKind::Tasks,
|
||||
Self::Editorconfig => proto::LocalSettingsKind::Editorconfig,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow::Context as _;
|
||||
use util::ResultExt;
|
||||
|
||||
use super::*;
|
||||
|
@ -527,6 +528,12 @@ impl Database {
|
|||
connection: ConnectionId,
|
||||
) -> Result<TransactionGuard<Vec<ConnectionId>>> {
|
||||
let project_id = ProjectId::from_proto(update.project_id);
|
||||
let kind = match update.kind {
|
||||
Some(kind) => proto::LocalSettingsKind::from_i32(kind)
|
||||
.with_context(|| format!("unknown worktree settings kind: {kind}"))?,
|
||||
None => proto::LocalSettingsKind::Settings,
|
||||
};
|
||||
let kind = LocalSettingsKind::from_proto(kind);
|
||||
self.project_transaction(project_id, |tx| async move {
|
||||
// Ensure the update comes from the host.
|
||||
let project = project::Entity::find_by_id(project_id)
|
||||
|
@ -543,6 +550,7 @@ impl Database {
|
|||
worktree_id: ActiveValue::Set(update.worktree_id as i64),
|
||||
path: ActiveValue::Set(update.path.clone()),
|
||||
content: ActiveValue::Set(content.clone()),
|
||||
kind: ActiveValue::Set(kind),
|
||||
})
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
|
@ -800,6 +808,7 @@ impl Database {
|
|||
worktree.settings_files.push(WorktreeSettingsFile {
|
||||
path: db_settings_file.path,
|
||||
content: db_settings_file.content,
|
||||
kind: db_settings_file.kind,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -735,6 +735,7 @@ impl Database {
|
|||
worktree.settings_files.push(WorktreeSettingsFile {
|
||||
path: db_settings_file.path,
|
||||
content: db_settings_file.content,
|
||||
kind: db_settings_file.kind,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,25 @@ pub struct Model {
|
|||
#[sea_orm(primary_key)]
|
||||
pub path: String,
|
||||
pub content: String,
|
||||
pub kind: LocalSettingsKind,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default, Hash, serde::Serialize,
|
||||
)]
|
||||
#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum LocalSettingsKind {
|
||||
#[default]
|
||||
#[sea_orm(string_value = "settings")]
|
||||
Settings,
|
||||
#[sea_orm(string_value = "tasks")]
|
||||
Tasks,
|
||||
#[sea_orm(string_value = "editorconfig")]
|
||||
Editorconfig,
|
||||
}
|
||||
|
|
|
@ -1739,6 +1739,7 @@ fn notify_rejoined_projects(
|
|||
worktree_id: worktree.id,
|
||||
path: settings_file.path,
|
||||
content: Some(settings_file.content),
|
||||
kind: Some(settings_file.kind.to_proto().into()),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
@ -2220,6 +2221,7 @@ fn join_project_internal(
|
|||
worktree_id: worktree.id,
|
||||
path: settings_file.path,
|
||||
content: Some(settings_file.content),
|
||||
kind: Some(proto::update_user_settings::Kind::Settings.into()),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ use project::{
|
|||
};
|
||||
use rand::prelude::*;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use settings::{LocalSettingsKind, SettingsStore};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
env, future, mem,
|
||||
|
@ -3327,8 +3327,16 @@ async fn test_local_settings(
|
|||
.local_settings(worktree_b.read(cx).id())
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
(Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
|
||||
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
|
||||
(
|
||||
Path::new("").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
r#"{"tab_size":2}"#.to_string()
|
||||
),
|
||||
(
|
||||
Path::new("a").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
r#"{"tab_size":8}"#.to_string()
|
||||
),
|
||||
]
|
||||
)
|
||||
});
|
||||
|
@ -3346,8 +3354,16 @@ async fn test_local_settings(
|
|||
.local_settings(worktree_b.read(cx).id())
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
(Path::new("").into(), r#"{}"#.to_string()),
|
||||
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
|
||||
(
|
||||
Path::new("").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
r#"{}"#.to_string()
|
||||
),
|
||||
(
|
||||
Path::new("a").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
r#"{"tab_size":8}"#.to_string()
|
||||
),
|
||||
]
|
||||
)
|
||||
});
|
||||
|
@ -3375,8 +3391,16 @@ async fn test_local_settings(
|
|||
.local_settings(worktree_b.read(cx).id())
|
||||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
|
||||
(Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
|
||||
(
|
||||
Path::new("a").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
r#"{"tab_size":8}"#.to_string()
|
||||
),
|
||||
(
|
||||
Path::new("b").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
r#"{"tab_size":4}"#.to_string()
|
||||
),
|
||||
]
|
||||
)
|
||||
});
|
||||
|
@ -3406,7 +3430,11 @@ async fn test_local_settings(
|
|||
store
|
||||
.local_settings(worktree_b.read(cx).id())
|
||||
.collect::<Vec<_>>(),
|
||||
&[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
|
||||
&[(
|
||||
Path::new("a").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
r#"{"hard_tabs":true}"#.to_string()
|
||||
),]
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow::Context;
|
||||
use collections::HashMap;
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, AsyncAppContext, BorrowAppContext, EventEmitter, Model, ModelContext};
|
||||
|
@ -6,7 +7,7 @@ use paths::local_settings_file_relative_path;
|
|||
use rpc::{proto, AnyProtoClient, TypedEnvelope};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{InvalidSettingsError, Settings, SettingsSources, SettingsStore};
|
||||
use settings::{InvalidSettingsError, LocalSettingsKind, Settings, SettingsSources, SettingsStore};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
|
@ -266,13 +267,14 @@ impl SettingsObserver {
|
|||
let store = cx.global::<SettingsStore>();
|
||||
for worktree in self.worktree_store.read(cx).worktrees() {
|
||||
let worktree_id = worktree.read(cx).id().to_proto();
|
||||
for (path, content) in store.local_settings(worktree.read(cx).id()) {
|
||||
for (path, kind, content) in store.local_settings(worktree.read(cx).id()) {
|
||||
downstream_client
|
||||
.send(proto::UpdateWorktreeSettings {
|
||||
project_id,
|
||||
worktree_id,
|
||||
path: path.to_string_lossy().into(),
|
||||
content: Some(content),
|
||||
kind: Some(local_settings_kind_to_proto(kind).into()),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
@ -288,6 +290,11 @@ impl SettingsObserver {
|
|||
envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> anyhow::Result<()> {
|
||||
let kind = match envelope.payload.kind {
|
||||
Some(kind) => proto::LocalSettingsKind::from_i32(kind)
|
||||
.with_context(|| format!("unknown kind {kind}"))?,
|
||||
None => proto::LocalSettingsKind::Settings,
|
||||
};
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
let Some(worktree) = this
|
||||
|
@ -297,10 +304,12 @@ impl SettingsObserver {
|
|||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
this.update_settings(
|
||||
worktree,
|
||||
[(
|
||||
PathBuf::from(&envelope.payload.path).into(),
|
||||
local_settings_kind_from_proto(kind),
|
||||
envelope.payload.content,
|
||||
)],
|
||||
cx,
|
||||
|
@ -327,6 +336,7 @@ impl SettingsObserver {
|
|||
ssh.send(proto::UpdateUserSettings {
|
||||
project_id: 0,
|
||||
content,
|
||||
kind: Some(proto::LocalSettingsKind::Settings.into()),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
@ -342,6 +352,7 @@ impl SettingsObserver {
|
|||
ssh.send(proto::UpdateUserSettings {
|
||||
project_id: 0,
|
||||
content,
|
||||
kind: Some(proto::LocalSettingsKind::Settings.into()),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
@ -397,6 +408,7 @@ impl SettingsObserver {
|
|||
settings_contents.push(async move {
|
||||
(
|
||||
settings_dir,
|
||||
LocalSettingsKind::Settings,
|
||||
if removed {
|
||||
None
|
||||
} else {
|
||||
|
@ -413,15 +425,15 @@ impl SettingsObserver {
|
|||
|
||||
let worktree = worktree.clone();
|
||||
cx.spawn(move |this, cx| async move {
|
||||
let settings_contents: Vec<(Arc<Path>, _)> =
|
||||
let settings_contents: Vec<(Arc<Path>, _, _)> =
|
||||
futures::future::join_all(settings_contents).await;
|
||||
cx.update(|cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.update_settings(
|
||||
worktree,
|
||||
settings_contents
|
||||
.into_iter()
|
||||
.map(|(path, content)| (path, content.and_then(|c| c.log_err()))),
|
||||
settings_contents.into_iter().map(|(path, kind, content)| {
|
||||
(path, kind, content.and_then(|c| c.log_err()))
|
||||
}),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
@ -433,17 +445,18 @@ impl SettingsObserver {
|
|||
fn update_settings(
|
||||
&mut self,
|
||||
worktree: Model<Worktree>,
|
||||
settings_contents: impl IntoIterator<Item = (Arc<Path>, Option<String>)>,
|
||||
settings_contents: impl IntoIterator<Item = (Arc<Path>, LocalSettingsKind, Option<String>)>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let worktree_id = worktree.read(cx).id();
|
||||
let remote_worktree_id = worktree.read(cx).id();
|
||||
|
||||
let result = cx.update_global::<SettingsStore, anyhow::Result<()>>(|store, cx| {
|
||||
for (directory, file_content) in settings_contents {
|
||||
for (directory, kind, file_content) in settings_contents {
|
||||
store.set_local_settings(
|
||||
worktree_id,
|
||||
directory.clone(),
|
||||
kind,
|
||||
file_content.as_deref(),
|
||||
cx,
|
||||
)?;
|
||||
|
@ -455,6 +468,7 @@ impl SettingsObserver {
|
|||
worktree_id: remote_worktree_id.to_proto(),
|
||||
path: directory.to_string_lossy().into_owned(),
|
||||
content: file_content,
|
||||
kind: Some(local_settings_kind_to_proto(kind).into()),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
@ -481,3 +495,19 @@ impl SettingsObserver {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_settings_kind_from_proto(kind: proto::LocalSettingsKind) -> LocalSettingsKind {
|
||||
match kind {
|
||||
proto::LocalSettingsKind::Settings => LocalSettingsKind::Settings,
|
||||
proto::LocalSettingsKind::Tasks => LocalSettingsKind::Tasks,
|
||||
proto::LocalSettingsKind::Editorconfig => LocalSettingsKind::Editorconfig,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_settings_kind_to_proto(kind: LocalSettingsKind) -> proto::LocalSettingsKind {
|
||||
match kind {
|
||||
LocalSettingsKind::Settings => proto::LocalSettingsKind::Settings,
|
||||
LocalSettingsKind::Tasks => proto::LocalSettingsKind::Tasks,
|
||||
LocalSettingsKind::Editorconfig => proto::LocalSettingsKind::Editorconfig,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -642,6 +642,13 @@ message UpdateWorktreeSettings {
|
|||
uint64 worktree_id = 2;
|
||||
string path = 3;
|
||||
optional string content = 4;
|
||||
optional LocalSettingsKind kind = 5;
|
||||
}
|
||||
|
||||
enum LocalSettingsKind {
|
||||
Settings = 0;
|
||||
Tasks = 1;
|
||||
Editorconfig = 2;
|
||||
}
|
||||
|
||||
message CreateProjectEntry {
|
||||
|
@ -2487,6 +2494,12 @@ message AddWorktreeResponse {
|
|||
message UpdateUserSettings {
|
||||
uint64 project_id = 1;
|
||||
string content = 2;
|
||||
optional Kind kind = 3;
|
||||
|
||||
enum Kind {
|
||||
Settings = 0;
|
||||
Tasks = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message CheckFileExists {
|
||||
|
|
|
@ -14,7 +14,8 @@ pub use json_schema::*;
|
|||
pub use keymap_file::KeymapFile;
|
||||
pub use settings_file::*;
|
||||
pub use settings_store::{
|
||||
InvalidSettingsError, Settings, SettingsLocation, SettingsSources, SettingsStore,
|
||||
InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation, SettingsSources,
|
||||
SettingsStore,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
||||
|
|
|
@ -157,13 +157,14 @@ pub struct SettingsLocation<'a> {
|
|||
pub path: &'a Path,
|
||||
}
|
||||
|
||||
/// A set of strongly-typed setting values defined via multiple JSON files.
|
||||
/// A set of strongly-typed setting values defined via multiple config files.
|
||||
pub struct SettingsStore {
|
||||
setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
|
||||
raw_default_settings: serde_json::Value,
|
||||
raw_user_settings: serde_json::Value,
|
||||
raw_extension_settings: serde_json::Value,
|
||||
raw_local_settings: BTreeMap<(WorktreeId, Arc<Path>), serde_json::Value>,
|
||||
raw_local_settings:
|
||||
BTreeMap<(WorktreeId, Arc<Path>), HashMap<LocalSettingsKind, serde_json::Value>>,
|
||||
tab_size_callback: Option<(
|
||||
TypeId,
|
||||
Box<dyn Fn(&dyn Any) -> Option<usize> + Send + Sync + 'static>,
|
||||
|
@ -174,6 +175,13 @@ pub struct SettingsStore {
|
|||
>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum LocalSettingsKind {
|
||||
Settings,
|
||||
Tasks,
|
||||
Editorconfig,
|
||||
}
|
||||
|
||||
impl Global for SettingsStore {}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -520,19 +528,21 @@ impl SettingsStore {
|
|||
pub fn set_local_settings(
|
||||
&mut self,
|
||||
root_id: WorktreeId,
|
||||
path: Arc<Path>,
|
||||
directory_path: Arc<Path>,
|
||||
kind: LocalSettingsKind,
|
||||
settings_content: Option<&str>,
|
||||
cx: &mut AppContext,
|
||||
) -> Result<()> {
|
||||
let raw_local_settings = self
|
||||
.raw_local_settings
|
||||
.entry((root_id, directory_path.clone()))
|
||||
.or_default();
|
||||
if settings_content.is_some_and(|content| !content.is_empty()) {
|
||||
self.raw_local_settings.insert(
|
||||
(root_id, path.clone()),
|
||||
parse_json_with_comments(settings_content.unwrap())?,
|
||||
);
|
||||
raw_local_settings.insert(kind, parse_json_with_comments(settings_content.unwrap())?);
|
||||
} else {
|
||||
self.raw_local_settings.remove(&(root_id, path.clone()));
|
||||
raw_local_settings.remove(&kind);
|
||||
}
|
||||
self.recompute_values(Some((root_id, &path)), cx)?;
|
||||
self.recompute_values(Some((root_id, &directory_path)), cx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -553,7 +563,8 @@ impl SettingsStore {
|
|||
|
||||
/// Add or remove a set of local settings via a JSON string.
|
||||
pub fn clear_local_settings(&mut self, root_id: WorktreeId, cx: &mut AppContext) -> Result<()> {
|
||||
self.raw_local_settings.retain(|k, _| k.0 != root_id);
|
||||
self.raw_local_settings
|
||||
.retain(|(worktree_id, _), _| worktree_id != &root_id);
|
||||
self.recompute_values(Some((root_id, "".as_ref())), cx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -561,7 +572,7 @@ impl SettingsStore {
|
|||
pub fn local_settings(
|
||||
&self,
|
||||
root_id: WorktreeId,
|
||||
) -> impl '_ + Iterator<Item = (Arc<Path>, String)> {
|
||||
) -> impl '_ + Iterator<Item = (Arc<Path>, LocalSettingsKind, String)> {
|
||||
self.raw_local_settings
|
||||
.range(
|
||||
(root_id, Path::new("").into())
|
||||
|
@ -570,7 +581,12 @@ impl SettingsStore {
|
|||
Path::new("").into(),
|
||||
),
|
||||
)
|
||||
.map(|((_, path), content)| (path.clone(), serde_json::to_string(content).unwrap()))
|
||||
.flat_map(|((_, path), content)| {
|
||||
content.iter().filter_map(|(&kind, raw_content)| {
|
||||
let parsed_content = serde_json::to_string(raw_content).log_err()?;
|
||||
Some((path.clone(), kind, parsed_content))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn json_schema(
|
||||
|
@ -739,56 +755,63 @@ impl SettingsStore {
|
|||
// Reload the local values for the setting.
|
||||
paths_stack.clear();
|
||||
project_settings_stack.clear();
|
||||
for ((root_id, path), local_settings) in &self.raw_local_settings {
|
||||
// Build a stack of all of the local values for that setting.
|
||||
while let Some(prev_entry) = paths_stack.last() {
|
||||
if let Some((prev_root_id, prev_path)) = prev_entry {
|
||||
if root_id != prev_root_id || !path.starts_with(prev_path) {
|
||||
paths_stack.pop();
|
||||
project_settings_stack.pop();
|
||||
continue;
|
||||
for ((root_id, directory_path), local_settings) in &self.raw_local_settings {
|
||||
if let Some(local_settings) = local_settings.get(&LocalSettingsKind::Settings) {
|
||||
// Build a stack of all of the local values for that setting.
|
||||
while let Some(prev_entry) = paths_stack.last() {
|
||||
if let Some((prev_root_id, prev_path)) = prev_entry {
|
||||
if root_id != prev_root_id || !directory_path.starts_with(prev_path) {
|
||||
paths_stack.pop();
|
||||
project_settings_stack.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
match setting_value.deserialize_setting(local_settings) {
|
||||
Ok(local_settings) => {
|
||||
paths_stack.push(Some((*root_id, path.as_ref())));
|
||||
project_settings_stack.push(local_settings);
|
||||
match setting_value.deserialize_setting(local_settings) {
|
||||
Ok(local_settings) => {
|
||||
paths_stack.push(Some((*root_id, directory_path.as_ref())));
|
||||
project_settings_stack.push(local_settings);
|
||||
|
||||
// If a local settings file changed, then avoid recomputing local
|
||||
// settings for any path outside of that directory.
|
||||
if changed_local_path.map_or(
|
||||
false,
|
||||
|(changed_root_id, changed_local_path)| {
|
||||
*root_id != changed_root_id || !path.starts_with(changed_local_path)
|
||||
},
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(value) = setting_value
|
||||
.load_setting(
|
||||
SettingsSources {
|
||||
default: &default_settings,
|
||||
extensions: extension_settings.as_ref(),
|
||||
user: user_settings.as_ref(),
|
||||
release_channel: release_channel_settings.as_ref(),
|
||||
project: &project_settings_stack.iter().collect::<Vec<_>>(),
|
||||
// If a local settings file changed, then avoid recomputing local
|
||||
// settings for any path outside of that directory.
|
||||
if changed_local_path.map_or(
|
||||
false,
|
||||
|(changed_root_id, changed_local_path)| {
|
||||
*root_id != changed_root_id
|
||||
|| !directory_path.starts_with(changed_local_path)
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.log_err()
|
||||
{
|
||||
setting_value.set_local_value(*root_id, path.clone(), value);
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(value) = setting_value
|
||||
.load_setting(
|
||||
SettingsSources {
|
||||
default: &default_settings,
|
||||
extensions: extension_settings.as_ref(),
|
||||
user: user_settings.as_ref(),
|
||||
release_channel: release_channel_settings.as_ref(),
|
||||
project: &project_settings_stack.iter().collect::<Vec<_>>(),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.log_err()
|
||||
{
|
||||
setting_value.set_local_value(
|
||||
*root_id,
|
||||
directory_path.clone(),
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
return Err(anyhow!(InvalidSettingsError::LocalSettings {
|
||||
path: directory_path.join(local_settings_file_relative_path()),
|
||||
message: error.to_string()
|
||||
}));
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
return Err(anyhow!(InvalidSettingsError::LocalSettings {
|
||||
path: path.join(local_settings_file_relative_path()),
|
||||
message: error.to_string()
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1201,6 +1224,7 @@ mod tests {
|
|||
.set_local_settings(
|
||||
WorktreeId::from_usize(1),
|
||||
Path::new("/root1").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
Some(r#"{ "user": { "staff": true } }"#),
|
||||
cx,
|
||||
)
|
||||
|
@ -1209,6 +1233,7 @@ mod tests {
|
|||
.set_local_settings(
|
||||
WorktreeId::from_usize(1),
|
||||
Path::new("/root1/subdir").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
Some(r#"{ "user": { "name": "Jane Doe" } }"#),
|
||||
cx,
|
||||
)
|
||||
|
@ -1218,6 +1243,7 @@ mod tests {
|
|||
.set_local_settings(
|
||||
WorktreeId::from_usize(1),
|
||||
Path::new("/root2").into(),
|
||||
LocalSettingsKind::Settings,
|
||||
Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#),
|
||||
cx,
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue