mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
settings: Use a derive macro for refine (#38451)
When we refactored settings to not pass JSON blobs around, we ended up needing to write *a lot* of code that just merged things (like json merge used to do). Use a derive macro to prevent typos in this logic. Release Notes: - N/A
This commit is contained in:
parent
5f4f0a873e
commit
b09764c54a
74 changed files with 1089 additions and 2818 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
|
@ -3144,7 +3144,6 @@ dependencies = [
|
|||
"release_channel",
|
||||
"rpc",
|
||||
"rustls-pki-types",
|
||||
"schemars 1.0.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
|
|
@ -9302,7 +9301,6 @@ dependencies = [
|
|||
"serde",
|
||||
"settings",
|
||||
"shellexpand 2.1.2",
|
||||
"util",
|
||||
"workspace",
|
||||
"workspace-hack",
|
||||
]
|
||||
|
|
@ -9523,7 +9521,6 @@ dependencies = [
|
|||
"http_client",
|
||||
"imara-diff",
|
||||
"indoc",
|
||||
"inventory",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"lsp",
|
||||
|
|
@ -15499,7 +15496,7 @@ dependencies = [
|
|||
"serde_path_to_error",
|
||||
"serde_repr",
|
||||
"serde_with",
|
||||
"settings_ui_macros",
|
||||
"settings_macros",
|
||||
"smallvec",
|
||||
"tree-sitter",
|
||||
"tree-sitter-json",
|
||||
|
|
@ -15509,6 +15506,16 @@ dependencies = [
|
|||
"zlog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "settings_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"settings",
|
||||
"syn 2.0.101",
|
||||
"workspace-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "settings_profile_selector"
|
||||
version = "0.1.0"
|
||||
|
|
@ -15530,18 +15537,6 @@ dependencies = [
|
|||
"zed_actions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "settings_ui_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"settings",
|
||||
"syn 2.0.101",
|
||||
"workspace-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
|
|
@ -17190,7 +17185,6 @@ dependencies = [
|
|||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"indexmap 2.9.0",
|
||||
"inventory",
|
||||
"log",
|
||||
"palette",
|
||||
"parking_lot",
|
||||
|
|
|
|||
|
|
@ -150,8 +150,8 @@ members = [
|
|||
"crates/semantic_version",
|
||||
"crates/session",
|
||||
"crates/settings",
|
||||
"crates/settings_macros",
|
||||
"crates/settings_profile_selector",
|
||||
"crates/settings_ui_macros",
|
||||
"crates/snippet",
|
||||
"crates/snippet_provider",
|
||||
"crates/snippets_ui",
|
||||
|
|
@ -383,7 +383,6 @@ semantic_version = { path = "crates/semantic_version" }
|
|||
session = { path = "crates/session" }
|
||||
settings = { path = "crates/settings" }
|
||||
settings_ui = { path = "crates/settings_ui" }
|
||||
settings_ui_macros = { path = "crates/settings_ui_macros" }
|
||||
snippet = { path = "crates/snippet" }
|
||||
snippet_provider = { path = "crates/snippet_provider" }
|
||||
snippets_ui = { path = "crates/snippets_ui" }
|
||||
|
|
|
|||
|
|
@ -834,11 +834,14 @@ mod tests {
|
|||
"**/.secretdir".to_string(),
|
||||
"**/.mymetadata".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(
|
||||
vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1064,7 +1067,8 @@ mod tests {
|
|||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
|
||||
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
|
||||
settings.project.worktree.private_files =
|
||||
Some(vec!["**/.env".to_string()].into());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -427,11 +427,14 @@ mod tests {
|
|||
"**/.mymetadata".to_string(),
|
||||
"**/.hidden_subdir".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(
|
||||
vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -568,7 +571,8 @@ mod tests {
|
|||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
|
||||
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
|
||||
settings.project.worktree.private_files =
|
||||
Some(vec!["**/.env".to_string()].into());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -593,11 +593,14 @@ mod test {
|
|||
"**/.secretdir".to_string(),
|
||||
"**/.mymetadata".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(
|
||||
vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -804,7 +807,8 @@ mod test {
|
|||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
|
||||
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
|
||||
settings.project.worktree.private_files =
|
||||
Some(vec!["**/.env".to_string()].into());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use settings::{
|
|||
DefaultAgentView, DockPosition, LanguageModelParameters, LanguageModelSelection,
|
||||
NotifyWhenAgentWaiting, Settings, SettingsContent,
|
||||
};
|
||||
use util::MergeFrom;
|
||||
|
||||
pub use crate::agent_profile::*;
|
||||
|
||||
|
|
@ -147,7 +146,7 @@ impl Default for AgentProfileId {
|
|||
}
|
||||
|
||||
impl Settings for AgentSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let agent = content.agent.clone().unwrap();
|
||||
Self {
|
||||
enabled: agent.enabled.unwrap(),
|
||||
|
|
@ -183,66 +182,6 @@ impl Settings for AgentSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
|
||||
let Some(value) = &content.agent else { return };
|
||||
self.enabled.merge_from(&value.enabled);
|
||||
self.button.merge_from(&value.button);
|
||||
self.dock.merge_from(&value.dock);
|
||||
self.default_width
|
||||
.merge_from(&value.default_width.map(Into::into));
|
||||
self.default_height
|
||||
.merge_from(&value.default_height.map(Into::into));
|
||||
self.default_model = value.default_model.clone().or(self.default_model.take());
|
||||
|
||||
self.inline_assistant_model = value
|
||||
.inline_assistant_model
|
||||
.clone()
|
||||
.or(self.inline_assistant_model.take());
|
||||
self.commit_message_model = value
|
||||
.clone()
|
||||
.commit_message_model
|
||||
.or(self.commit_message_model.take());
|
||||
self.thread_summary_model = value
|
||||
.clone()
|
||||
.thread_summary_model
|
||||
.or(self.thread_summary_model.take());
|
||||
self.inline_alternatives
|
||||
.merge_from(&value.inline_alternatives.clone());
|
||||
self.default_profile
|
||||
.merge_from(&value.default_profile.clone().map(AgentProfileId));
|
||||
self.default_view.merge_from(&value.default_view);
|
||||
self.always_allow_tool_actions
|
||||
.merge_from(&value.always_allow_tool_actions);
|
||||
self.notify_when_agent_waiting
|
||||
.merge_from(&value.notify_when_agent_waiting);
|
||||
self.play_sound_when_agent_done
|
||||
.merge_from(&value.play_sound_when_agent_done);
|
||||
self.stream_edits.merge_from(&value.stream_edits);
|
||||
self.single_file_review
|
||||
.merge_from(&value.single_file_review);
|
||||
self.preferred_completion_mode
|
||||
.merge_from(&value.preferred_completion_mode.map(Into::into));
|
||||
self.enable_feedback.merge_from(&value.enable_feedback);
|
||||
self.expand_edit_card.merge_from(&value.expand_edit_card);
|
||||
self.expand_terminal_card
|
||||
.merge_from(&value.expand_terminal_card);
|
||||
self.use_modifier_to_send
|
||||
.merge_from(&value.use_modifier_to_send);
|
||||
|
||||
self.model_parameters
|
||||
.extend_from_slice(&value.model_parameters);
|
||||
self.message_editor_min_lines
|
||||
.merge_from(&value.message_editor_min_lines);
|
||||
|
||||
if let Some(profiles) = value.profiles.as_ref() {
|
||||
self.profiles.extend(
|
||||
profiles
|
||||
.into_iter()
|
||||
.map(|(id, profile)| (AgentProfileId(id.clone()), profile.clone().into())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
|
||||
if let Some(b) = vscode
|
||||
.read_value("chat.agent.enabled")
|
||||
|
|
|
|||
|
|
@ -1067,7 +1067,7 @@ impl AgentPanel {
|
|||
let _ = settings
|
||||
.theme
|
||||
.agent_font_size
|
||||
.insert(Some(theme::clamp_font_size(agent_font_size).into()));
|
||||
.insert(theme::clamp_font_size(agent_font_size).into());
|
||||
});
|
||||
} else {
|
||||
theme::adjust_agent_font_size(cx, |size| size + delta);
|
||||
|
|
|
|||
|
|
@ -856,11 +856,14 @@ mod tests {
|
|||
"**/.secretdir".to_string(),
|
||||
"**/.mymetadata".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(
|
||||
vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1160,7 +1163,8 @@ mod tests {
|
|||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
|
||||
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
|
||||
settings.project.worktree.private_files =
|
||||
Some(vec!["**/.env".to_string()].into());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -513,11 +513,14 @@ mod tests {
|
|||
"**/.mymetadata".to_string(),
|
||||
"**/.hidden_subdir".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(
|
||||
vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -701,7 +704,8 @@ mod tests {
|
|||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
|
||||
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
|
||||
settings.project.worktree.private_files =
|
||||
Some(vec!["**/.env".to_string()].into());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -684,11 +684,14 @@ mod test {
|
|||
"**/.secretdir".to_string(),
|
||||
"**/.mymetadata".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]);
|
||||
settings.project.worktree.private_files = Some(
|
||||
vec![
|
||||
"**/.mysecrets".to_string(),
|
||||
"**/*.privatekey".to_string(),
|
||||
"**/*.mysensitive".to_string(),
|
||||
]
|
||||
.into(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -970,7 +973,8 @@ mod test {
|
|||
store.update_user_settings(cx, |settings| {
|
||||
settings.project.worktree.file_scan_exclusions =
|
||||
Some(vec!["**/.git".to_string(), "**/node_modules".to_string()]);
|
||||
settings.project.worktree.private_files = Some(vec!["**/.env".to_string()]);
|
||||
settings.project.worktree.private_files =
|
||||
Some(vec!["**/.env".to_string()].into());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||
|
||||
use gpui::App;
|
||||
use settings::{Settings, SettingsStore};
|
||||
use util::MergeFrom as _;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AudioSettings {
|
||||
|
|
@ -23,7 +22,7 @@ pub struct AudioSettings {
|
|||
|
||||
/// Configuration of audio in Zed
|
||||
impl Settings for AudioSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let audio = &content.audio.as_ref().unwrap();
|
||||
AudioSettings {
|
||||
control_input_volume: audio.control_input_volume.unwrap(),
|
||||
|
|
@ -32,17 +31,6 @@ impl Settings for AudioSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(audio) = content.audio.as_ref() else {
|
||||
return;
|
||||
};
|
||||
self.control_input_volume
|
||||
.merge_from(&audio.control_input_volume);
|
||||
self.control_output_volume
|
||||
.merge_from(&audio.control_output_volume);
|
||||
self.rodio_audio.merge_from(&audio.rodio_audio);
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
_vscode: &settings::VsCodeSettings,
|
||||
_current: &mut settings::SettingsContent,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
|
|||
use paths::remote_servers_dir;
|
||||
use release_channel::{AppCommitSha, ReleaseChannel};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsContent, SettingsStore};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use smol::{fs, io::AsyncReadExt};
|
||||
use smol::{fs::File, process::Command};
|
||||
use std::{
|
||||
|
|
@ -119,21 +119,9 @@ struct AutoUpdateSetting(bool);
|
|||
///
|
||||
/// Default: true
|
||||
impl Settings for AutoUpdateSetting {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
debug_assert_eq!(content.auto_update.unwrap(), true);
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
Self(content.auto_update.unwrap())
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
if let Some(auto_update) = content.auto_update {
|
||||
self.0 = auto_update;
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(_: &settings::VsCodeSettings, _: &mut SettingsContent) {
|
||||
// We could match on vscode's update.mode here, but
|
||||
// I think it's more important to have more people updating zed by default.
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use gpui::App;
|
||||
use settings::Settings;
|
||||
use util::MergeFrom;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CallSettings {
|
||||
|
|
@ -9,7 +8,7 @@ pub struct CallSettings {
|
|||
}
|
||||
|
||||
impl Settings for CallSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let call = content.calls.clone().unwrap();
|
||||
CallSettings {
|
||||
mute_on_join: call.mute_on_join.unwrap(),
|
||||
|
|
@ -17,13 +16,6 @@ impl Settings for CallSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
if let Some(call) = content.calls.clone() {
|
||||
self.mute_on_join.merge_from(&call.mute_on_join);
|
||||
self.share_on_join.merge_from(&call.share_on_join);
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
_vscode: &settings::VsCodeSettings,
|
||||
_current: &mut settings::SettingsContent,
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ rand.workspace = true
|
|||
regex.workspace = true
|
||||
release_channel.workspace = true
|
||||
rpc = { workspace = true, features = ["gpui"] }
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_urlencoded.workspace = true
|
||||
|
|
|
|||
|
|
@ -29,9 +29,8 @@ use proxy::connect_proxy_stream;
|
|||
use rand::prelude::*;
|
||||
use release_channel::{AppVersion, ReleaseChannel};
|
||||
use rpc::proto::{AnyTypedEnvelope, EnvelopedMessage, PeerId, RequestMessage};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{Settings, SettingsContent, SettingsKey, SettingsUi};
|
||||
use settings::{Settings, SettingsContent};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
convert::TryFrom,
|
||||
|
|
@ -50,7 +49,7 @@ use telemetry::Telemetry;
|
|||
use thiserror::Error;
|
||||
use tokio::net::TcpStream;
|
||||
use url::Url;
|
||||
use util::{ConnectionResult, MergeFrom, ResultExt};
|
||||
use util::{ConnectionResult, ResultExt};
|
||||
|
||||
pub use rpc::*;
|
||||
pub use telemetry_events::Event;
|
||||
|
|
@ -102,7 +101,7 @@ pub struct ClientSettings {
|
|||
}
|
||||
|
||||
impl Settings for ClientSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
if let Some(server_url) = &*ZED_SERVER_URL {
|
||||
return Self {
|
||||
server_url: server_url.clone(),
|
||||
|
|
@ -112,23 +111,6 @@ impl Settings for ClientSettings {
|
|||
server_url: content.server_url.clone().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
|
||||
if ZED_SERVER_URL.is_some() {
|
||||
return;
|
||||
}
|
||||
if let Some(server_url) = content.server_url.clone() {
|
||||
self.server_url = server_url;
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
|
||||
#[settings_key(None)]
|
||||
pub struct ProxySettingsContent {
|
||||
proxy: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
|
|
@ -151,18 +133,12 @@ impl ProxySettings {
|
|||
}
|
||||
|
||||
impl Settings for ProxySettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
Self {
|
||||
proxy: content.proxy.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
|
||||
if let Some(proxy) = content.proxy.clone() {
|
||||
self.proxy = Some(proxy)
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
|
||||
vscode.string_setting("http.proxy", &mut current.proxy);
|
||||
}
|
||||
|
|
@ -543,21 +519,13 @@ pub struct TelemetrySettings {
|
|||
}
|
||||
|
||||
impl settings::Settings for TelemetrySettings {
|
||||
fn from_defaults(content: &SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &SettingsContent, _cx: &mut App) -> Self {
|
||||
Self {
|
||||
diagnostics: content.telemetry.as_ref().unwrap().diagnostics.unwrap(),
|
||||
metrics: content.telemetry.as_ref().unwrap().metrics.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
|
||||
let Some(telemetry) = &content.telemetry else {
|
||||
return;
|
||||
};
|
||||
self.diagnostics.merge_from(&telemetry.diagnostics);
|
||||
self.metrics.merge_from(&telemetry.metrics);
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
|
||||
let mut telemetry = settings::TelemetrySettingsContent::default();
|
||||
vscode.enum_setting("telemetry.telemetryLevel", &mut telemetry.metrics, |s| {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use gpui::Pixels;
|
||||
use settings::Settings;
|
||||
use ui::px;
|
||||
use util::MergeFrom as _;
|
||||
use workspace::dock::DockPosition;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -19,7 +18,7 @@ pub struct NotificationPanelSettings {
|
|||
}
|
||||
|
||||
impl Settings for CollaborationPanelSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
let panel = content.collaboration_panel.as_ref().unwrap();
|
||||
|
||||
Self {
|
||||
|
|
@ -28,25 +27,10 @@ impl Settings for CollaborationPanelSettings {
|
|||
default_width: panel.default_width.map(px).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
|
||||
if let Some(panel) = content.collaboration_panel.as_ref() {
|
||||
self.button.merge_from(&panel.button);
|
||||
self.default_width
|
||||
.merge_from(&panel.default_width.map(Pixels::from));
|
||||
self.dock.merge_from(&panel.dock.map(Into::into));
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
_vscode: &settings::VsCodeSettings,
|
||||
_content: &mut settings::SettingsContent,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings for NotificationPanelSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
let panel = content.notification_panel.as_ref().unwrap();
|
||||
return Self {
|
||||
button: panel.button.unwrap(),
|
||||
|
|
@ -54,19 +38,4 @@ impl Settings for NotificationPanelSettings {
|
|||
default_width: panel.default_width.map(px).unwrap(),
|
||||
};
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
|
||||
let Some(panel) = content.notification_panel.as_ref() else {
|
||||
return;
|
||||
};
|
||||
self.button.merge_from(&panel.button);
|
||||
self.dock.merge_from(&panel.dock.map(Into::into));
|
||||
self.default_width.merge_from(&panel.default_width.map(px));
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
_vscode: &settings::VsCodeSettings,
|
||||
_current: &mut settings::SettingsContent,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub type IndexMap<K, V> = indexmap::IndexMap<K, V>;
|
|||
#[cfg(not(feature = "test-support"))]
|
||||
pub type IndexSet<T> = indexmap::IndexSet<T>;
|
||||
|
||||
pub use indexmap::Equivalent;
|
||||
pub use rustc_hash::FxHasher;
|
||||
pub use rustc_hash::{FxHashMap, FxHashSet};
|
||||
pub use std::collections::*;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use dap_types::SteppingGranularity;
|
||||
use gpui::App;
|
||||
use settings::{Settings, SettingsContent};
|
||||
use util::MergeFrom;
|
||||
|
||||
pub struct DebuggerSettings {
|
||||
/// Determines the stepping granularity.
|
||||
|
|
@ -35,7 +34,7 @@ pub struct DebuggerSettings {
|
|||
}
|
||||
|
||||
impl Settings for DebuggerSettings {
|
||||
fn from_defaults(content: &SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &SettingsContent, _cx: &mut App) -> Self {
|
||||
let content = content.debugger.clone().unwrap();
|
||||
Self {
|
||||
stepping_granularity: dap_granularity_from_settings(
|
||||
|
|
@ -49,27 +48,6 @@ impl Settings for DebuggerSettings {
|
|||
dock: content.dock.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
|
||||
let Some(content) = &content.debugger else {
|
||||
return;
|
||||
};
|
||||
self.stepping_granularity.merge_from(
|
||||
&content
|
||||
.stepping_granularity
|
||||
.map(dap_granularity_from_settings),
|
||||
);
|
||||
self.save_breakpoints.merge_from(&content.save_breakpoints);
|
||||
self.button.merge_from(&content.button);
|
||||
self.timeout.merge_from(&content.timeout);
|
||||
self.log_dap_communications
|
||||
.merge_from(&content.log_dap_communications);
|
||||
self.format_dap_log_messages
|
||||
.merge_from(&content.format_dap_log_messages);
|
||||
self.dock.merge_from(&content.dock);
|
||||
}
|
||||
|
||||
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
|
||||
}
|
||||
|
||||
fn dap_granularity_from_settings(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ pub use settings::{
|
|||
};
|
||||
use settings::{Settings, SettingsContent};
|
||||
use ui::scrollbars::{ScrollbarVisibility, ShowScrollbar};
|
||||
use util::MergeFrom;
|
||||
|
||||
/// Imports from the VSCode settings at
|
||||
/// https://code.visualstudio.com/docs/reference/default-settings
|
||||
|
|
@ -190,7 +189,7 @@ impl ScrollbarVisibility for EditorSettings {
|
|||
}
|
||||
|
||||
impl Settings for EditorSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let editor = content.editor.clone();
|
||||
let scrollbar = editor.scrollbar.unwrap();
|
||||
let minimap = editor.minimap.unwrap();
|
||||
|
|
@ -238,7 +237,7 @@ impl Settings for EditorSettings {
|
|||
display_in: minimap.display_in.unwrap(),
|
||||
thumb: minimap.thumb.unwrap(),
|
||||
thumb_border: minimap.thumb_border.unwrap(),
|
||||
current_line_highlight: minimap.current_line_highlight.flatten(),
|
||||
current_line_highlight: minimap.current_line_highlight,
|
||||
max_width_columns: minimap.max_width_columns.unwrap(),
|
||||
},
|
||||
gutter: Gutter {
|
||||
|
|
@ -290,162 +289,6 @@ impl Settings for EditorSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let editor = &content.editor;
|
||||
self.cursor_blink.merge_from(&editor.cursor_blink);
|
||||
if let Some(cursor_shape) = editor.cursor_shape {
|
||||
self.cursor_shape = Some(cursor_shape.into())
|
||||
}
|
||||
self.current_line_highlight
|
||||
.merge_from(&editor.current_line_highlight);
|
||||
self.selection_highlight
|
||||
.merge_from(&editor.selection_highlight);
|
||||
self.rounded_selection.merge_from(&editor.rounded_selection);
|
||||
self.lsp_highlight_debounce
|
||||
.merge_from(&editor.lsp_highlight_debounce);
|
||||
self.hover_popover_enabled
|
||||
.merge_from(&editor.hover_popover_enabled);
|
||||
self.hover_popover_delay
|
||||
.merge_from(&editor.hover_popover_delay);
|
||||
self.scroll_beyond_last_line
|
||||
.merge_from(&editor.scroll_beyond_last_line);
|
||||
self.vertical_scroll_margin
|
||||
.merge_from(&editor.vertical_scroll_margin);
|
||||
self.autoscroll_on_clicks
|
||||
.merge_from(&editor.autoscroll_on_clicks);
|
||||
self.horizontal_scroll_margin
|
||||
.merge_from(&editor.horizontal_scroll_margin);
|
||||
self.scroll_sensitivity
|
||||
.merge_from(&editor.scroll_sensitivity);
|
||||
self.fast_scroll_sensitivity
|
||||
.merge_from(&editor.fast_scroll_sensitivity);
|
||||
self.relative_line_numbers
|
||||
.merge_from(&editor.relative_line_numbers);
|
||||
self.seed_search_query_from_cursor
|
||||
.merge_from(&editor.seed_search_query_from_cursor);
|
||||
self.use_smartcase_search
|
||||
.merge_from(&editor.use_smartcase_search);
|
||||
self.multi_cursor_modifier
|
||||
.merge_from(&editor.multi_cursor_modifier);
|
||||
self.redact_private_values
|
||||
.merge_from(&editor.redact_private_values);
|
||||
self.expand_excerpt_lines
|
||||
.merge_from(&editor.expand_excerpt_lines);
|
||||
self.excerpt_context_lines
|
||||
.merge_from(&editor.excerpt_context_lines);
|
||||
self.middle_click_paste
|
||||
.merge_from(&editor.middle_click_paste);
|
||||
self.double_click_in_multibuffer
|
||||
.merge_from(&editor.double_click_in_multibuffer);
|
||||
self.search_wrap.merge_from(&editor.search_wrap);
|
||||
self.auto_signature_help
|
||||
.merge_from(&editor.auto_signature_help);
|
||||
self.show_signature_help_after_edits
|
||||
.merge_from(&editor.show_signature_help_after_edits);
|
||||
self.go_to_definition_fallback
|
||||
.merge_from(&editor.go_to_definition_fallback);
|
||||
if let Some(hide_mouse) = editor.hide_mouse {
|
||||
self.hide_mouse = Some(hide_mouse)
|
||||
}
|
||||
self.snippet_sort_order
|
||||
.merge_from(&editor.snippet_sort_order);
|
||||
if let Some(diagnostics_max_severity) = editor.diagnostics_max_severity {
|
||||
self.diagnostics_max_severity = Some(diagnostics_max_severity.into());
|
||||
}
|
||||
self.inline_code_actions
|
||||
.merge_from(&editor.inline_code_actions);
|
||||
self.lsp_document_colors
|
||||
.merge_from(&editor.lsp_document_colors);
|
||||
self.minimum_contrast_for_highlights
|
||||
.merge_from(&editor.minimum_contrast_for_highlights);
|
||||
|
||||
if let Some(status_bar) = &editor.status_bar {
|
||||
self.status_bar
|
||||
.active_language_button
|
||||
.merge_from(&status_bar.active_language_button);
|
||||
self.status_bar
|
||||
.cursor_position_button
|
||||
.merge_from(&status_bar.cursor_position_button);
|
||||
}
|
||||
if let Some(toolbar) = &editor.toolbar {
|
||||
self.toolbar.breadcrumbs.merge_from(&toolbar.breadcrumbs);
|
||||
self.toolbar
|
||||
.quick_actions
|
||||
.merge_from(&toolbar.quick_actions);
|
||||
self.toolbar
|
||||
.selections_menu
|
||||
.merge_from(&toolbar.selections_menu);
|
||||
self.toolbar.agent_review.merge_from(&toolbar.agent_review);
|
||||
self.toolbar.code_actions.merge_from(&toolbar.code_actions);
|
||||
}
|
||||
if let Some(scrollbar) = &editor.scrollbar {
|
||||
self.scrollbar
|
||||
.show
|
||||
.merge_from(&scrollbar.show.map(Into::into));
|
||||
self.scrollbar.git_diff.merge_from(&scrollbar.git_diff);
|
||||
self.scrollbar
|
||||
.selected_text
|
||||
.merge_from(&scrollbar.selected_text);
|
||||
self.scrollbar
|
||||
.selected_symbol
|
||||
.merge_from(&scrollbar.selected_symbol);
|
||||
self.scrollbar
|
||||
.search_results
|
||||
.merge_from(&scrollbar.search_results);
|
||||
self.scrollbar
|
||||
.diagnostics
|
||||
.merge_from(&scrollbar.diagnostics);
|
||||
self.scrollbar.cursors.merge_from(&scrollbar.cursors);
|
||||
if let Some(axes) = &scrollbar.axes {
|
||||
self.scrollbar.axes.horizontal.merge_from(&axes.horizontal);
|
||||
self.scrollbar.axes.vertical.merge_from(&axes.vertical);
|
||||
}
|
||||
}
|
||||
if let Some(minimap) = &editor.minimap {
|
||||
self.minimap.show.merge_from(&minimap.show);
|
||||
self.minimap.display_in.merge_from(&minimap.display_in);
|
||||
self.minimap.thumb.merge_from(&minimap.thumb);
|
||||
self.minimap.thumb_border.merge_from(&minimap.thumb_border);
|
||||
self.minimap
|
||||
.current_line_highlight
|
||||
.merge_from(&minimap.current_line_highlight);
|
||||
self.minimap
|
||||
.max_width_columns
|
||||
.merge_from(&minimap.max_width_columns);
|
||||
}
|
||||
if let Some(gutter) = &editor.gutter {
|
||||
self.gutter
|
||||
.min_line_number_digits
|
||||
.merge_from(&gutter.min_line_number_digits);
|
||||
self.gutter.line_numbers.merge_from(&gutter.line_numbers);
|
||||
self.gutter.runnables.merge_from(&gutter.runnables);
|
||||
self.gutter.breakpoints.merge_from(&gutter.breakpoints);
|
||||
self.gutter.folds.merge_from(&gutter.folds);
|
||||
}
|
||||
if let Some(search) = &editor.search {
|
||||
self.search.button.merge_from(&search.button);
|
||||
self.search.whole_word.merge_from(&search.whole_word);
|
||||
self.search
|
||||
.case_sensitive
|
||||
.merge_from(&search.case_sensitive);
|
||||
self.search
|
||||
.include_ignored
|
||||
.merge_from(&search.include_ignored);
|
||||
self.search.regex.merge_from(&search.regex);
|
||||
}
|
||||
if let Some(enabled) = editor.jupyter.as_ref().and_then(|jupyter| jupyter.enabled) {
|
||||
self.jupyter.enabled = enabled;
|
||||
}
|
||||
if let Some(drag_and_drop_selection) = &editor.drag_and_drop_selection {
|
||||
self.drag_and_drop_selection
|
||||
.enabled
|
||||
.merge_from(&drag_and_drop_selection.enabled);
|
||||
self.drag_and_drop_selection
|
||||
.delay
|
||||
.merge_from(&drag_and_drop_selection.delay);
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &VsCodeSettings, current: &mut SettingsContent) {
|
||||
vscode.enum_setting(
|
||||
"editor.cursorBlinking",
|
||||
|
|
|
|||
|
|
@ -33,26 +33,10 @@ impl ExtensionSettings {
|
|||
}
|
||||
|
||||
impl Settings for ExtensionSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
Self {
|
||||
auto_install_extensions: content.extension.auto_install_extensions.clone(),
|
||||
auto_update_extensions: content.extension.auto_update_extensions.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
self.auto_install_extensions
|
||||
.extend(content.extension.auto_install_extensions.clone());
|
||||
self.auto_update_extensions
|
||||
.extend(content.extension.auto_update_extensions.clone());
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
_vscode: &settings::VsCodeSettings,
|
||||
_current: &mut settings::SettingsContent,
|
||||
) {
|
||||
// settingsSync.ignoredExtensions controls autoupdate for vscode extensions, but we
|
||||
// don't have a mapping to zed-extensions. there's also extensions.autoCheckUpdates
|
||||
// and extensions.autoUpdate which are global switches, we don't support those yet
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use util::MergeFrom;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||
pub struct FileFinderSettings {
|
||||
|
|
@ -12,30 +11,16 @@ pub struct FileFinderSettings {
|
|||
}
|
||||
|
||||
impl Settings for FileFinderSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
let file_finder = content.file_finder.as_ref().unwrap();
|
||||
|
||||
Self {
|
||||
file_icons: file_finder.file_icons.unwrap(),
|
||||
modal_max_width: file_finder.modal_max_width.unwrap().into(),
|
||||
skip_focus_for_active_in_search: file_finder.skip_focus_for_active_in_search.unwrap(),
|
||||
include_ignored: file_finder.include_ignored.flatten(),
|
||||
include_ignored: file_finder.include_ignored,
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
|
||||
let Some(file_finder) = content.file_finder.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.file_icons.merge_from(&file_finder.file_icons);
|
||||
self.modal_max_width
|
||||
.merge_from(&file_finder.modal_max_width.map(Into::into));
|
||||
self.skip_focus_for_active_in_search
|
||||
.merge_from(&file_finder.skip_focus_for_active_in_search);
|
||||
self.include_ignored
|
||||
.merge_from(&file_finder.include_ignored);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
|
||||
|
|
|
|||
|
|
@ -58,17 +58,14 @@ pub struct GitHostingProviderSettings {
|
|||
}
|
||||
|
||||
impl Settings for GitHostingProviderSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
Self {
|
||||
git_hosting_providers: content.project.git_hosting_providers.clone().unwrap(),
|
||||
git_hosting_providers: content
|
||||
.project
|
||||
.git_hosting_providers
|
||||
.clone()
|
||||
.unwrap()
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
|
||||
if let Some(more) = &content.project.git_hosting_providers {
|
||||
self.git_hosting_providers.extend_from_slice(&more.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(_: &settings::VsCodeSettings, _: &mut settings::SettingsContent) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use ui::{
|
|||
px,
|
||||
scrollbars::{ScrollbarVisibility, ShowScrollbar},
|
||||
};
|
||||
use util::MergeFrom;
|
||||
use workspace::dock::DockPosition;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
|
|
@ -52,7 +51,7 @@ impl ScrollbarVisibility for GitPanelSettings {
|
|||
}
|
||||
|
||||
impl Settings for GitPanelSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
let git_panel = content.git_panel.clone().unwrap();
|
||||
Self {
|
||||
button: git_panel.button.unwrap(),
|
||||
|
|
@ -68,25 +67,6 @@ impl Settings for GitPanelSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut ui::App) {
|
||||
let Some(git_panel) = &content.git_panel else {
|
||||
return;
|
||||
};
|
||||
self.button.merge_from(&git_panel.button);
|
||||
self.dock.merge_from(&git_panel.dock.map(Into::into));
|
||||
self.default_width
|
||||
.merge_from(&git_panel.default_width.map(px));
|
||||
self.status_style.merge_from(&git_panel.status_style);
|
||||
self.fallback_branch_name
|
||||
.merge_from(&git_panel.fallback_branch_name);
|
||||
self.sort_by_path.merge_from(&git_panel.sort_by_path);
|
||||
self.collapse_untracked_diff
|
||||
.merge_from(&git_panel.collapse_untracked_diff);
|
||||
if let Some(show) = git_panel.scrollbar.as_ref().and_then(|s| s.show) {
|
||||
self.scrollbar.show = Some(show.into())
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
|
||||
if let Some(git_enabled) = vscode.read_bool("git.enabled") {
|
||||
current.git_panel.get_or_insert_default().button = Some(git_enabled);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use ui::{
|
|||
Button, ButtonCommon, Clickable, Context, FluentBuilder, IntoElement, LabelSize, ParentElement,
|
||||
Render, Tooltip, Window, div,
|
||||
};
|
||||
use util::{MergeFrom, paths::FILE_ROW_COLUMN_DELIMITER};
|
||||
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
||||
use workspace::{StatusItemView, Workspace, item::ItemHandle};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq)]
|
||||
|
|
@ -307,11 +307,7 @@ impl From<settings::LineIndicatorFormat> for LineIndicatorFormat {
|
|||
}
|
||||
|
||||
impl Settings for LineIndicatorFormat {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
content.line_indicator_format.unwrap().into()
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
self.merge_from(&content.line_indicator_format.map(Into::into));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use gpui::App;
|
||||
pub use settings::ImageFileSizeUnit;
|
||||
use settings::Settings;
|
||||
use util::MergeFrom;
|
||||
|
||||
/// The settings for the image viewer.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
|
@ -13,18 +12,9 @@ pub struct ImageViewerSettings {
|
|||
}
|
||||
|
||||
impl Settings for ImageViewerSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
Self {
|
||||
unit: content.image_viewer.clone().unwrap().unit.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
self.unit.merge_from(
|
||||
&content
|
||||
.image_viewer
|
||||
.as_ref()
|
||||
.and_then(|image_viewer| image_viewer.unit),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ settings.workspace = true
|
|||
shellexpand.workspace = true
|
||||
workspace.workspace = true
|
||||
workspace-hack.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
editor = { workspace = true, features = ["test-support"] }
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::MergeFrom;
|
||||
use workspace::{AppState, OpenVisible, Workspace};
|
||||
|
||||
actions!(
|
||||
|
|
@ -34,7 +33,7 @@ pub struct JournalSettings {
|
|||
}
|
||||
|
||||
impl settings::Settings for JournalSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let journal = content.journal.clone().unwrap();
|
||||
|
||||
Self {
|
||||
|
|
@ -42,14 +41,6 @@ impl settings::Settings for JournalSettings {
|
|||
hour_format: journal.hour_format.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(journal) = content.journal.as_ref() else {
|
||||
return;
|
||||
};
|
||||
self.path.merge_from(&journal.path);
|
||||
self.hour_format.merge_from(&journal.hour_format);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(_: Arc<AppState>, cx: &mut App) {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ globset.workspace = true
|
|||
gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
imara-diff.workspace = true
|
||||
inventory.workspace = true
|
||||
itertools.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
|
|
|
|||
|
|
@ -269,15 +269,15 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
|
|||
cx.update(|cx| {
|
||||
init_settings(cx, |settings| {
|
||||
settings.file_types.extend([
|
||||
("TypeScript".into(), vec!["js".into()]),
|
||||
("TypeScript".into(), vec!["js".into()].into()),
|
||||
(
|
||||
"JavaScript".into(),
|
||||
vec!["*longer.ts".into(), "ecmascript".into()],
|
||||
vec!["*longer.ts".into(), "ecmascript".into()].into(),
|
||||
),
|
||||
("C++".into(), vec!["c".into(), "*.dev".into()]),
|
||||
("C++".into(), vec!["c".into(), "*.dev".into()].into()),
|
||||
(
|
||||
"Dockerfile".into(),
|
||||
vec!["Dockerfile".into(), "Dockerfile.*".into()],
|
||||
vec!["Dockerfile".into(), "Dockerfile.*".into()].into(),
|
||||
),
|
||||
]);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,22 +9,15 @@ use ec4rs::{
|
|||
use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
|
||||
use gpui::{App, Modifiers};
|
||||
use itertools::{Either, Itertools};
|
||||
use schemars::json_schema;
|
||||
|
||||
pub use settings::{
|
||||
CompletionSettingsContent, EditPredictionProvider, EditPredictionsMode, FormatOnSave,
|
||||
Formatter, FormatterList, InlayHintKind, LanguageSettingsContent, LspInsertMode,
|
||||
RewrapBehavior, SelectedFormatter, ShowWhitespaceSetting, SoftWrap, WordsCompletionMode,
|
||||
};
|
||||
use settings::{
|
||||
IndentGuideSettingsContent, LanguageTaskSettingsContent, ParameterizedJsonSchema,
|
||||
PrettierSettingsContent, Settings, SettingsContent, SettingsLocation, SettingsStore,
|
||||
SettingsUi,
|
||||
};
|
||||
use settings::{ExtendingVec, Settings, SettingsContent, SettingsLocation, SettingsStore};
|
||||
use shellexpand;
|
||||
use std::{borrow::Cow, num::NonZeroU32, path::Path, sync::Arc};
|
||||
use util::MergeFrom;
|
||||
use util::{ResultExt, schemars::replace_subschema};
|
||||
|
||||
/// Initializes the language settings.
|
||||
pub fn init(cx: &mut App) {
|
||||
|
|
@ -64,7 +57,6 @@ pub struct AllLanguageSettings {
|
|||
pub defaults: LanguageSettings,
|
||||
languages: HashMap<LanguageName, LanguageSettings>,
|
||||
pub(crate) file_types: FxHashMap<Arc<str>, GlobSet>,
|
||||
pub(crate) file_globs: FxHashMap<Arc<str>, Vec<String>>,
|
||||
}
|
||||
|
||||
/// The settings for a particular language.
|
||||
|
|
@ -189,18 +181,6 @@ pub struct CompletionSettings {
|
|||
pub lsp_insert_mode: LspInsertMode,
|
||||
}
|
||||
|
||||
impl CompletionSettings {
|
||||
pub fn merge_from(&mut self, src: &Option<CompletionSettingsContent>) {
|
||||
let Some(src) = src else { return };
|
||||
self.words.merge_from(&src.words);
|
||||
self.words_min_length.merge_from(&src.words_min_length);
|
||||
self.lsp.merge_from(&src.lsp);
|
||||
self.lsp_fetch_timeout_ms
|
||||
.merge_from(&src.lsp_fetch_timeout_ms);
|
||||
self.lsp_insert_mode.merge_from(&src.lsp_insert_mode);
|
||||
}
|
||||
}
|
||||
|
||||
/// The settings for indent guides.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct IndentGuideSettings {
|
||||
|
|
@ -226,19 +206,6 @@ pub struct IndentGuideSettings {
|
|||
pub background_coloring: settings::IndentGuideBackgroundColoring,
|
||||
}
|
||||
|
||||
impl IndentGuideSettings {
|
||||
pub fn merge_from(&mut self, src: &Option<IndentGuideSettingsContent>) {
|
||||
let Some(src) = src else { return };
|
||||
|
||||
self.enabled.merge_from(&src.enabled);
|
||||
self.line_width.merge_from(&src.line_width);
|
||||
self.active_line_width.merge_from(&src.active_line_width);
|
||||
self.coloring.merge_from(&src.coloring);
|
||||
self.background_coloring
|
||||
.merge_from(&src.background_coloring);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LanguageTaskSettings {
|
||||
/// Extra task variables to set for a particular language.
|
||||
|
|
@ -254,17 +221,6 @@ pub struct LanguageTaskSettings {
|
|||
pub prefer_lsp: bool,
|
||||
}
|
||||
|
||||
impl LanguageTaskSettings {
|
||||
pub fn merge_from(&mut self, src: &Option<LanguageTaskSettingsContent>) {
|
||||
let Some(src) = src.clone() else {
|
||||
return;
|
||||
};
|
||||
self.variables.extend(src.variables);
|
||||
self.enabled.merge_from(&src.enabled);
|
||||
self.prefer_lsp.merge_from(&src.prefer_lsp);
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows to enable/disable formatting with Prettier
|
||||
/// and configure default Prettier, used when no project-level Prettier installation is found.
|
||||
/// Prettier formatting is disabled by default.
|
||||
|
|
@ -285,16 +241,6 @@ pub struct PrettierSettings {
|
|||
pub options: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
impl PrettierSettings {
|
||||
pub fn merge_from(&mut self, src: &Option<PrettierSettingsContent>) {
|
||||
let Some(src) = src.clone() else { return };
|
||||
self.allowed.merge_from(&src.allowed);
|
||||
self.parser = src.parser.clone();
|
||||
self.plugins.extend(src.plugins);
|
||||
self.options.extend(src.options);
|
||||
}
|
||||
}
|
||||
|
||||
impl LanguageSettings {
|
||||
/// A token representing the rest of the available language servers.
|
||||
const REST_OF_LANGUAGE_SERVERS: &'static str = "...";
|
||||
|
|
@ -413,14 +359,13 @@ impl InlayHintSettings {
|
|||
|
||||
/// The settings for edit predictions, such as [GitHub Copilot](https://github.com/features/copilot)
|
||||
/// or [Supermaven](https://supermaven.com).
|
||||
#[derive(Clone, Debug, Default, SettingsUi)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct EditPredictionSettings {
|
||||
/// The provider that supplies edit predictions.
|
||||
pub provider: settings::EditPredictionProvider,
|
||||
/// A list of globs representing files that edit predictions should be disabled for.
|
||||
/// This list adds to a pre-existing, sensible default set of globs.
|
||||
/// Any additional ones you add are combined with them.
|
||||
#[settings_ui(skip)]
|
||||
pub disabled_globs: Vec<DisabledGlob>,
|
||||
/// Configures how edit predictions are displayed in the buffer.
|
||||
pub mode: settings::EditPredictionsMode,
|
||||
|
|
@ -451,41 +396,16 @@ pub struct DisabledGlob {
|
|||
is_absolute: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, SettingsUi)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CopilotSettings {
|
||||
/// HTTP/HTTPS proxy to use for Copilot.
|
||||
#[settings_ui(skip)]
|
||||
pub proxy: Option<String>,
|
||||
/// Disable certificate verification for proxy (not recommended).
|
||||
pub proxy_no_verify: Option<bool>,
|
||||
/// Enterprise URI for Copilot.
|
||||
#[settings_ui(skip)]
|
||||
pub enterprise_uri: Option<String>,
|
||||
}
|
||||
|
||||
inventory::submit! {
|
||||
ParameterizedJsonSchema {
|
||||
add_and_get_ref: |generator, params, _cx| {
|
||||
let language_settings_content_ref = generator
|
||||
.subschema_for::<LanguageSettingsContent>()
|
||||
.to_value();
|
||||
replace_subschema::<settings::LanguageToSettingsMap>(generator, || json_schema!({
|
||||
"type": "object",
|
||||
"properties": params
|
||||
.language_names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
(
|
||||
name.clone(),
|
||||
language_settings_content_ref.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>()
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AllLanguageSettings {
|
||||
/// Returns the [`LanguageSettings`] for the language with the specified name.
|
||||
pub fn language<'a>(
|
||||
|
|
@ -574,93 +494,99 @@ fn merge_with_editorconfig(settings: &mut LanguageSettings, cfg: &EditorconfigPr
|
|||
}
|
||||
|
||||
impl settings::Settings for AllLanguageSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let all_languages = &content.project.all_languages;
|
||||
let defaults = all_languages.defaults.clone();
|
||||
let inlay_hints = defaults.inlay_hints.unwrap();
|
||||
let completions = defaults.completions.unwrap();
|
||||
let prettier = defaults.prettier.unwrap();
|
||||
let indent_guides = defaults.indent_guides.unwrap();
|
||||
let tasks = defaults.tasks.unwrap();
|
||||
|
||||
let default_language_settings = LanguageSettings {
|
||||
tab_size: defaults.tab_size.unwrap(),
|
||||
hard_tabs: defaults.hard_tabs.unwrap(),
|
||||
soft_wrap: defaults.soft_wrap.unwrap(),
|
||||
preferred_line_length: defaults.preferred_line_length.unwrap(),
|
||||
show_wrap_guides: defaults.show_wrap_guides.unwrap(),
|
||||
wrap_guides: defaults.wrap_guides.unwrap(),
|
||||
indent_guides: IndentGuideSettings {
|
||||
enabled: indent_guides.enabled.unwrap(),
|
||||
line_width: indent_guides.line_width.unwrap(),
|
||||
active_line_width: indent_guides.active_line_width.unwrap(),
|
||||
coloring: indent_guides.coloring.unwrap(),
|
||||
background_coloring: indent_guides.background_coloring.unwrap(),
|
||||
},
|
||||
format_on_save: defaults.format_on_save.unwrap(),
|
||||
remove_trailing_whitespace_on_save: defaults
|
||||
.remove_trailing_whitespace_on_save
|
||||
.unwrap(),
|
||||
ensure_final_newline_on_save: defaults.ensure_final_newline_on_save.unwrap(),
|
||||
formatter: defaults.formatter.unwrap(),
|
||||
prettier: PrettierSettings {
|
||||
allowed: prettier.allowed.unwrap(),
|
||||
parser: prettier.parser,
|
||||
plugins: prettier.plugins,
|
||||
options: prettier.options,
|
||||
},
|
||||
jsx_tag_auto_close: defaults.jsx_tag_auto_close.unwrap().enabled.unwrap(),
|
||||
enable_language_server: defaults.enable_language_server.unwrap(),
|
||||
language_servers: defaults.language_servers.unwrap(),
|
||||
allow_rewrap: defaults.allow_rewrap.unwrap(),
|
||||
show_edit_predictions: defaults.show_edit_predictions.unwrap(),
|
||||
edit_predictions_disabled_in: defaults.edit_predictions_disabled_in.unwrap(),
|
||||
show_whitespaces: defaults.show_whitespaces.unwrap(),
|
||||
whitespace_map: defaults.whitespace_map.unwrap(),
|
||||
extend_comment_on_newline: defaults.extend_comment_on_newline.unwrap(),
|
||||
inlay_hints: InlayHintSettings {
|
||||
enabled: inlay_hints.enabled.unwrap(),
|
||||
show_value_hints: inlay_hints.show_value_hints.unwrap(),
|
||||
show_type_hints: inlay_hints.show_type_hints.unwrap(),
|
||||
show_parameter_hints: inlay_hints.show_parameter_hints.unwrap(),
|
||||
show_other_hints: inlay_hints.show_other_hints.unwrap(),
|
||||
show_background: inlay_hints.show_background.unwrap(),
|
||||
edit_debounce_ms: inlay_hints.edit_debounce_ms.unwrap(),
|
||||
scroll_debounce_ms: inlay_hints.scroll_debounce_ms.unwrap(),
|
||||
toggle_on_modifiers_press: inlay_hints.toggle_on_modifiers_press,
|
||||
},
|
||||
use_autoclose: defaults.use_autoclose.unwrap(),
|
||||
use_auto_surround: defaults.use_auto_surround.unwrap(),
|
||||
use_on_type_format: defaults.use_on_type_format.unwrap(),
|
||||
auto_indent: defaults.auto_indent.unwrap(),
|
||||
auto_indent_on_paste: defaults.auto_indent_on_paste.unwrap(),
|
||||
always_treat_brackets_as_autoclosed: defaults
|
||||
.always_treat_brackets_as_autoclosed
|
||||
.unwrap(),
|
||||
code_actions_on_format: defaults.code_actions_on_format.unwrap(),
|
||||
linked_edits: defaults.linked_edits.unwrap(),
|
||||
tasks: LanguageTaskSettings {
|
||||
variables: tasks.variables,
|
||||
enabled: tasks.enabled.unwrap(),
|
||||
prefer_lsp: tasks.prefer_lsp.unwrap(),
|
||||
},
|
||||
show_completions_on_input: defaults.show_completions_on_input.unwrap(),
|
||||
show_completion_documentation: defaults.show_completion_documentation.unwrap(),
|
||||
completions: CompletionSettings {
|
||||
words: completions.words.unwrap(),
|
||||
words_min_length: completions.words_min_length.unwrap(),
|
||||
lsp: completions.lsp.unwrap(),
|
||||
lsp_fetch_timeout_ms: completions.lsp_fetch_timeout_ms.unwrap(),
|
||||
lsp_insert_mode: completions.lsp_insert_mode.unwrap(),
|
||||
},
|
||||
debuggers: defaults.debuggers.unwrap(),
|
||||
};
|
||||
fn load_from_content(settings: LanguageSettingsContent) -> LanguageSettings {
|
||||
let inlay_hints = settings.inlay_hints.unwrap();
|
||||
let completions = settings.completions.unwrap();
|
||||
let prettier = settings.prettier.unwrap();
|
||||
let indent_guides = settings.indent_guides.unwrap();
|
||||
let tasks = settings.tasks.unwrap();
|
||||
LanguageSettings {
|
||||
tab_size: settings.tab_size.unwrap(),
|
||||
hard_tabs: settings.hard_tabs.unwrap(),
|
||||
soft_wrap: settings.soft_wrap.unwrap(),
|
||||
preferred_line_length: settings.preferred_line_length.unwrap(),
|
||||
show_wrap_guides: settings.show_wrap_guides.unwrap(),
|
||||
wrap_guides: settings.wrap_guides.unwrap(),
|
||||
indent_guides: IndentGuideSettings {
|
||||
enabled: indent_guides.enabled.unwrap(),
|
||||
line_width: indent_guides.line_width.unwrap(),
|
||||
active_line_width: indent_guides.active_line_width.unwrap(),
|
||||
coloring: indent_guides.coloring.unwrap(),
|
||||
background_coloring: indent_guides.background_coloring.unwrap(),
|
||||
},
|
||||
format_on_save: settings.format_on_save.unwrap(),
|
||||
remove_trailing_whitespace_on_save: settings
|
||||
.remove_trailing_whitespace_on_save
|
||||
.unwrap(),
|
||||
ensure_final_newline_on_save: settings.ensure_final_newline_on_save.unwrap(),
|
||||
formatter: settings.formatter.unwrap(),
|
||||
prettier: PrettierSettings {
|
||||
allowed: prettier.allowed.unwrap(),
|
||||
parser: prettier.parser,
|
||||
plugins: prettier.plugins,
|
||||
options: prettier.options,
|
||||
},
|
||||
jsx_tag_auto_close: settings.jsx_tag_auto_close.unwrap().enabled.unwrap(),
|
||||
enable_language_server: settings.enable_language_server.unwrap(),
|
||||
language_servers: settings.language_servers.unwrap(),
|
||||
allow_rewrap: settings.allow_rewrap.unwrap(),
|
||||
show_edit_predictions: settings.show_edit_predictions.unwrap(),
|
||||
edit_predictions_disabled_in: settings.edit_predictions_disabled_in.unwrap(),
|
||||
show_whitespaces: settings.show_whitespaces.unwrap(),
|
||||
whitespace_map: settings.whitespace_map.unwrap(),
|
||||
extend_comment_on_newline: settings.extend_comment_on_newline.unwrap(),
|
||||
inlay_hints: InlayHintSettings {
|
||||
enabled: inlay_hints.enabled.unwrap(),
|
||||
show_value_hints: inlay_hints.show_value_hints.unwrap(),
|
||||
show_type_hints: inlay_hints.show_type_hints.unwrap(),
|
||||
show_parameter_hints: inlay_hints.show_parameter_hints.unwrap(),
|
||||
show_other_hints: inlay_hints.show_other_hints.unwrap(),
|
||||
show_background: inlay_hints.show_background.unwrap(),
|
||||
edit_debounce_ms: inlay_hints.edit_debounce_ms.unwrap(),
|
||||
scroll_debounce_ms: inlay_hints.scroll_debounce_ms.unwrap(),
|
||||
toggle_on_modifiers_press: inlay_hints.toggle_on_modifiers_press,
|
||||
},
|
||||
use_autoclose: settings.use_autoclose.unwrap(),
|
||||
use_auto_surround: settings.use_auto_surround.unwrap(),
|
||||
use_on_type_format: settings.use_on_type_format.unwrap(),
|
||||
auto_indent: settings.auto_indent.unwrap(),
|
||||
auto_indent_on_paste: settings.auto_indent_on_paste.unwrap(),
|
||||
always_treat_brackets_as_autoclosed: settings
|
||||
.always_treat_brackets_as_autoclosed
|
||||
.unwrap(),
|
||||
code_actions_on_format: settings.code_actions_on_format.unwrap(),
|
||||
linked_edits: settings.linked_edits.unwrap(),
|
||||
tasks: LanguageTaskSettings {
|
||||
variables: tasks.variables,
|
||||
enabled: tasks.enabled.unwrap(),
|
||||
prefer_lsp: tasks.prefer_lsp.unwrap(),
|
||||
},
|
||||
show_completions_on_input: settings.show_completions_on_input.unwrap(),
|
||||
show_completion_documentation: settings.show_completion_documentation.unwrap(),
|
||||
completions: CompletionSettings {
|
||||
words: completions.words.unwrap(),
|
||||
words_min_length: completions.words_min_length.unwrap(),
|
||||
lsp: completions.lsp.unwrap(),
|
||||
lsp_fetch_timeout_ms: completions.lsp_fetch_timeout_ms.unwrap(),
|
||||
lsp_insert_mode: completions.lsp_insert_mode.unwrap(),
|
||||
},
|
||||
debuggers: settings.debuggers.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
let default_language_settings = load_from_content(all_languages.defaults.clone());
|
||||
|
||||
let mut languages = HashMap::default();
|
||||
for (language_name, settings) in &all_languages.languages.0 {
|
||||
let mut language_settings = default_language_settings.clone();
|
||||
merge_settings(&mut language_settings, settings);
|
||||
languages.insert(LanguageName(language_name.clone()), language_settings);
|
||||
let mut language_settings = all_languages.defaults.clone();
|
||||
settings::merge_from::MergeFrom::merge_from(&mut language_settings, Some(settings));
|
||||
languages.insert(
|
||||
LanguageName(language_name.clone()),
|
||||
load_from_content(language_settings),
|
||||
);
|
||||
}
|
||||
|
||||
let edit_prediction_provider = all_languages
|
||||
|
|
@ -688,17 +614,15 @@ impl settings::Settings for AllLanguageSettings {
|
|||
let enabled_in_text_threads = edit_predictions.enabled_in_text_threads.unwrap();
|
||||
|
||||
let mut file_types: FxHashMap<Arc<str>, GlobSet> = FxHashMap::default();
|
||||
let mut file_globs: FxHashMap<Arc<str>, Vec<String>> = FxHashMap::default();
|
||||
|
||||
for (language, patterns) in &all_languages.file_types {
|
||||
let mut builder = GlobSetBuilder::new();
|
||||
|
||||
for pattern in patterns {
|
||||
for pattern in &patterns.0 {
|
||||
builder.add(Glob::new(pattern).unwrap());
|
||||
}
|
||||
|
||||
file_types.insert(language.clone(), builder.build().unwrap());
|
||||
file_globs.insert(language.clone(), patterns.clone());
|
||||
}
|
||||
|
||||
Self {
|
||||
|
|
@ -725,110 +649,6 @@ impl settings::Settings for AllLanguageSettings {
|
|||
defaults: default_language_settings,
|
||||
languages,
|
||||
file_types,
|
||||
file_globs,
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
|
||||
let all_languages = &content.project.all_languages;
|
||||
if let Some(provider) = all_languages
|
||||
.features
|
||||
.as_ref()
|
||||
.and_then(|f| f.edit_prediction_provider)
|
||||
{
|
||||
self.edit_predictions.provider = provider;
|
||||
}
|
||||
|
||||
if let Some(edit_predictions) = all_languages.edit_predictions.as_ref() {
|
||||
self.edit_predictions
|
||||
.mode
|
||||
.merge_from(&edit_predictions.mode);
|
||||
self.edit_predictions
|
||||
.enabled_in_text_threads
|
||||
.merge_from(&edit_predictions.enabled_in_text_threads);
|
||||
|
||||
if let Some(disabled_globs) = edit_predictions.disabled_globs.as_ref() {
|
||||
self.edit_predictions
|
||||
.disabled_globs
|
||||
.extend(disabled_globs.iter().filter_map(|g| {
|
||||
let expanded_g = shellexpand::tilde(g).into_owned();
|
||||
Some(DisabledGlob {
|
||||
matcher: globset::Glob::new(&expanded_g).ok()?.compile_matcher(),
|
||||
is_absolute: Path::new(&expanded_g).is_absolute(),
|
||||
})
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(proxy) = all_languages
|
||||
.edit_predictions
|
||||
.as_ref()
|
||||
.and_then(|settings| settings.copilot.as_ref()?.proxy.clone())
|
||||
{
|
||||
self.edit_predictions.copilot.proxy = Some(proxy);
|
||||
}
|
||||
|
||||
if let Some(proxy_no_verify) = all_languages
|
||||
.edit_predictions
|
||||
.as_ref()
|
||||
.and_then(|settings| settings.copilot.as_ref()?.proxy_no_verify)
|
||||
{
|
||||
self.edit_predictions.copilot.proxy_no_verify = Some(proxy_no_verify);
|
||||
}
|
||||
|
||||
if let Some(enterprise_uri) = all_languages
|
||||
.edit_predictions
|
||||
.as_ref()
|
||||
.and_then(|settings| settings.copilot.as_ref()?.enterprise_uri.clone())
|
||||
{
|
||||
self.edit_predictions.copilot.enterprise_uri = Some(enterprise_uri);
|
||||
}
|
||||
|
||||
// A user's global settings override the default global settings and
|
||||
// all default language-specific settings.
|
||||
merge_settings(&mut self.defaults, &all_languages.defaults);
|
||||
for language_settings in self.languages.values_mut() {
|
||||
merge_settings(language_settings, &all_languages.defaults);
|
||||
}
|
||||
|
||||
// A user's language-specific settings override default language-specific settings.
|
||||
for (language_name, user_language_settings) in &all_languages.languages.0 {
|
||||
merge_settings(
|
||||
self.languages
|
||||
.entry(LanguageName(language_name.clone()))
|
||||
.or_insert_with(|| self.defaults.clone()),
|
||||
user_language_settings,
|
||||
);
|
||||
}
|
||||
|
||||
for (language, patterns) in &all_languages.file_types {
|
||||
let mut builder = GlobSetBuilder::new();
|
||||
|
||||
let default_value = self.file_globs.get(&language.clone());
|
||||
|
||||
// Merge the default value with the user's value.
|
||||
if let Some(patterns) = default_value {
|
||||
for pattern in patterns {
|
||||
if let Some(glob) = Glob::new(pattern).log_err() {
|
||||
builder.add(glob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for pattern in patterns {
|
||||
if let Some(glob) = Glob::new(pattern).log_err() {
|
||||
builder.add(glob);
|
||||
}
|
||||
}
|
||||
|
||||
self.file_globs
|
||||
.entry(language.clone())
|
||||
.or_default()
|
||||
.extend(patterns.clone());
|
||||
|
||||
if let Some(matcher) = builder.build().log_err() {
|
||||
self.file_types.insert(language.clone(), matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -917,14 +737,14 @@ impl settings::Settings for AllLanguageSettings {
|
|||
// TODO: pull ^ out into helper and reuse for per-language settings
|
||||
|
||||
// vscodes file association map is inverted from ours, so we flip the mapping before merging
|
||||
let mut associations: HashMap<Arc<str>, Vec<String>> = HashMap::default();
|
||||
let mut associations: HashMap<Arc<str>, ExtendingVec<String>> = HashMap::default();
|
||||
if let Some(map) = vscode
|
||||
.read_value("files.associations")
|
||||
.and_then(|v| v.as_object())
|
||||
{
|
||||
for (k, v) in map {
|
||||
let Some(v) = v.as_str() else { continue };
|
||||
associations.entry(v.into()).or_default().push(k.clone());
|
||||
associations.entry(v.into()).or_default().0.push(k.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -957,121 +777,7 @@ impl settings::Settings for AllLanguageSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
|
||||
settings.tab_size.merge_from(&src.tab_size);
|
||||
settings.tab_size = settings
|
||||
.tab_size
|
||||
.clamp(NonZeroU32::new(1).unwrap(), NonZeroU32::new(16).unwrap());
|
||||
|
||||
settings.hard_tabs.merge_from(&src.hard_tabs);
|
||||
settings.soft_wrap.merge_from(&src.soft_wrap);
|
||||
settings.use_autoclose.merge_from(&src.use_autoclose);
|
||||
settings
|
||||
.use_auto_surround
|
||||
.merge_from(&src.use_auto_surround);
|
||||
settings
|
||||
.use_on_type_format
|
||||
.merge_from(&src.use_on_type_format);
|
||||
settings.auto_indent.merge_from(&src.auto_indent);
|
||||
settings
|
||||
.auto_indent_on_paste
|
||||
.merge_from(&src.auto_indent_on_paste);
|
||||
settings
|
||||
.always_treat_brackets_as_autoclosed
|
||||
.merge_from(&src.always_treat_brackets_as_autoclosed);
|
||||
settings.show_wrap_guides.merge_from(&src.show_wrap_guides);
|
||||
settings.wrap_guides.merge_from(&src.wrap_guides);
|
||||
settings.indent_guides.merge_from(&src.indent_guides);
|
||||
settings
|
||||
.code_actions_on_format
|
||||
.merge_from(&src.code_actions_on_format.clone());
|
||||
settings.linked_edits.merge_from(&src.linked_edits);
|
||||
settings.tasks.merge_from(&src.tasks);
|
||||
|
||||
settings
|
||||
.preferred_line_length
|
||||
.merge_from(&src.preferred_line_length);
|
||||
settings.formatter.merge_from(&src.formatter.clone());
|
||||
settings.prettier.merge_from(&src.prettier.clone());
|
||||
settings
|
||||
.jsx_tag_auto_close
|
||||
.merge_from(&src.jsx_tag_auto_close.as_ref().and_then(|v| v.enabled));
|
||||
settings
|
||||
.format_on_save
|
||||
.merge_from(&src.format_on_save.clone());
|
||||
settings
|
||||
.remove_trailing_whitespace_on_save
|
||||
.merge_from(&src.remove_trailing_whitespace_on_save);
|
||||
settings
|
||||
.ensure_final_newline_on_save
|
||||
.merge_from(&src.ensure_final_newline_on_save);
|
||||
settings
|
||||
.enable_language_server
|
||||
.merge_from(&src.enable_language_server);
|
||||
settings
|
||||
.language_servers
|
||||
.merge_from(&src.language_servers.clone());
|
||||
settings.allow_rewrap.merge_from(&src.allow_rewrap);
|
||||
settings
|
||||
.show_edit_predictions
|
||||
.merge_from(&src.show_edit_predictions);
|
||||
settings
|
||||
.edit_predictions_disabled_in
|
||||
.merge_from(&src.edit_predictions_disabled_in.clone());
|
||||
settings.show_whitespaces.merge_from(&src.show_whitespaces);
|
||||
settings
|
||||
.whitespace_map
|
||||
.merge_from(&src.whitespace_map.clone());
|
||||
settings
|
||||
.extend_comment_on_newline
|
||||
.merge_from(&src.extend_comment_on_newline);
|
||||
if let Some(inlay_hints) = &src.inlay_hints {
|
||||
settings
|
||||
.inlay_hints
|
||||
.enabled
|
||||
.merge_from(&inlay_hints.enabled);
|
||||
settings
|
||||
.inlay_hints
|
||||
.show_value_hints
|
||||
.merge_from(&inlay_hints.show_value_hints);
|
||||
settings
|
||||
.inlay_hints
|
||||
.show_type_hints
|
||||
.merge_from(&inlay_hints.show_type_hints);
|
||||
settings
|
||||
.inlay_hints
|
||||
.show_parameter_hints
|
||||
.merge_from(&inlay_hints.show_parameter_hints);
|
||||
settings
|
||||
.inlay_hints
|
||||
.show_other_hints
|
||||
.merge_from(&inlay_hints.show_other_hints);
|
||||
settings
|
||||
.inlay_hints
|
||||
.show_background
|
||||
.merge_from(&inlay_hints.show_background);
|
||||
settings
|
||||
.inlay_hints
|
||||
.edit_debounce_ms
|
||||
.merge_from(&inlay_hints.edit_debounce_ms);
|
||||
settings
|
||||
.inlay_hints
|
||||
.scroll_debounce_ms
|
||||
.merge_from(&inlay_hints.scroll_debounce_ms);
|
||||
if let Some(toggle_on_modifiers_press) = &inlay_hints.toggle_on_modifiers_press {
|
||||
settings.inlay_hints.toggle_on_modifiers_press = Some(*toggle_on_modifiers_press);
|
||||
}
|
||||
}
|
||||
settings
|
||||
.show_completions_on_input
|
||||
.merge_from(&src.show_completions_on_input);
|
||||
settings
|
||||
.show_completion_documentation
|
||||
.merge_from(&src.show_completion_documentation);
|
||||
settings.completions.merge_from(&src.completions);
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, SettingsUi)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct JsxTagAutoCloseSettings {
|
||||
/// Enables or disables auto-closing of JSX tags.
|
||||
pub enabled: bool,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use std::sync::Arc;
|
|||
use collections::HashMap;
|
||||
use gpui::App;
|
||||
use settings::Settings;
|
||||
use util::MergeFrom;
|
||||
|
||||
use crate::provider::{
|
||||
anthropic::AnthropicSettings, bedrock::AmazonBedrockSettings, cloud::ZedDotDevSettings,
|
||||
|
|
@ -37,7 +36,7 @@ pub struct AllLanguageModelSettings {
|
|||
impl settings::Settings for AllLanguageModelSettings {
|
||||
const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
|
||||
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let language_models = content.language_models.clone().unwrap();
|
||||
let anthropic = language_models.anthropic.unwrap();
|
||||
let bedrock = language_models.bedrock.unwrap();
|
||||
|
|
@ -118,113 +117,4 @@ impl settings::Settings for AllLanguageModelSettings {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(models) = content.language_models.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(anthropic) = models.anthropic.as_ref() {
|
||||
self.anthropic
|
||||
.available_models
|
||||
.merge_from(&anthropic.available_models);
|
||||
self.anthropic.api_url.merge_from(&anthropic.api_url);
|
||||
}
|
||||
|
||||
if let Some(bedrock) = models.bedrock.clone() {
|
||||
self.bedrock
|
||||
.available_models
|
||||
.merge_from(&bedrock.available_models);
|
||||
|
||||
if let Some(endpoint_url) = bedrock.endpoint_url {
|
||||
self.bedrock.endpoint = Some(endpoint_url)
|
||||
}
|
||||
|
||||
if let Some(region) = bedrock.region {
|
||||
self.bedrock.region = Some(region)
|
||||
}
|
||||
|
||||
if let Some(profile_name) = bedrock.profile {
|
||||
self.bedrock.profile_name = Some(profile_name);
|
||||
}
|
||||
|
||||
if let Some(auth_method) = bedrock.authentication_method {
|
||||
self.bedrock.authentication_method = Some(auth_method.into());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(deepseek) = models.deepseek.as_ref() {
|
||||
self.deepseek
|
||||
.available_models
|
||||
.merge_from(&deepseek.available_models);
|
||||
self.deepseek.api_url.merge_from(&deepseek.api_url);
|
||||
}
|
||||
|
||||
if let Some(google) = models.google.as_ref() {
|
||||
self.google
|
||||
.available_models
|
||||
.merge_from(&google.available_models);
|
||||
self.google.api_url.merge_from(&google.api_url);
|
||||
}
|
||||
|
||||
if let Some(lmstudio) = models.lmstudio.as_ref() {
|
||||
self.lmstudio
|
||||
.available_models
|
||||
.merge_from(&lmstudio.available_models);
|
||||
self.lmstudio.api_url.merge_from(&lmstudio.api_url);
|
||||
}
|
||||
|
||||
if let Some(mistral) = models.mistral.as_ref() {
|
||||
self.mistral
|
||||
.available_models
|
||||
.merge_from(&mistral.available_models);
|
||||
self.mistral.api_url.merge_from(&mistral.api_url);
|
||||
}
|
||||
if let Some(ollama) = models.ollama.as_ref() {
|
||||
self.ollama
|
||||
.available_models
|
||||
.merge_from(&ollama.available_models);
|
||||
self.ollama.api_url.merge_from(&ollama.api_url);
|
||||
}
|
||||
if let Some(open_router) = models.open_router.as_ref() {
|
||||
self.open_router
|
||||
.available_models
|
||||
.merge_from(&open_router.available_models);
|
||||
self.open_router.api_url.merge_from(&open_router.api_url);
|
||||
}
|
||||
if let Some(openai) = models.openai.as_ref() {
|
||||
self.openai
|
||||
.available_models
|
||||
.merge_from(&openai.available_models);
|
||||
self.openai.api_url.merge_from(&openai.api_url);
|
||||
}
|
||||
if let Some(openai_compatible) = models.openai_compatible.clone() {
|
||||
for (name, value) in openai_compatible {
|
||||
self.openai_compatible.insert(
|
||||
name,
|
||||
OpenAiCompatibleSettings {
|
||||
api_url: value.api_url,
|
||||
available_models: value.available_models,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(vercel) = models.vercel.as_ref() {
|
||||
self.vercel
|
||||
.available_models
|
||||
.merge_from(&vercel.available_models);
|
||||
self.vercel.api_url.merge_from(&vercel.api_url);
|
||||
}
|
||||
if let Some(x_ai) = models.x_ai.as_ref() {
|
||||
self.x_ai
|
||||
.available_models
|
||||
.merge_from(&x_ai.available_models);
|
||||
self.x_ai.api_url.merge_from(&x_ai.api_url);
|
||||
}
|
||||
if let Some(zed_dot_dev) = models.zed_dot_dev.as_ref() {
|
||||
self.zed_dot_dev
|
||||
.available_models
|
||||
.merge_from(&zed_dot_dev.available_models);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ snippet_provider.workspace = true
|
|||
url.workspace = true
|
||||
task.workspace = true
|
||||
tempfile.workspace = true
|
||||
theme.workspace = true
|
||||
toml.workspace = true
|
||||
tree-sitter = { workspace = true, optional = true }
|
||||
tree-sitter-bash = { workspace = true, optional = true }
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use async_trait::async_trait;
|
|||
use collections::HashMap;
|
||||
use dap::DapRegistry;
|
||||
use futures::StreamExt;
|
||||
use gpui::{App, AsyncApp, Task};
|
||||
use gpui::{App, AsyncApp, SharedString, Task};
|
||||
use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
|
||||
use language::{
|
||||
ContextProvider, LanguageName, LanguageRegistry, LocalFile as _, LspAdapter,
|
||||
|
|
@ -29,6 +29,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
use task::{AdapterSchemas, TaskTemplate, TaskTemplates, VariableName};
|
||||
use theme::ThemeRegistry;
|
||||
use util::{ResultExt, archive::extract_zip, fs::remove_matching, maybe, merge_json_value_into};
|
||||
|
||||
use crate::PackageJsonData;
|
||||
|
|
@ -156,13 +157,20 @@ impl JsonLspAdapter {
|
|||
) -> Value {
|
||||
let keymap_schema = KeymapFile::generate_json_schema_for_registered_actions(cx);
|
||||
let font_names = &cx.text_system().all_font_names();
|
||||
let settings_schema = cx.global::<SettingsStore>().json_schema(
|
||||
&SettingsJsonSchemaParams {
|
||||
let theme_names = &ThemeRegistry::global(cx).list_names();
|
||||
let icon_theme_names = &ThemeRegistry::global(cx)
|
||||
.list_icon_themes()
|
||||
.into_iter()
|
||||
.map(|icon_theme| icon_theme.name)
|
||||
.collect::<Vec<SharedString>>();
|
||||
let settings_schema = cx
|
||||
.global::<SettingsStore>()
|
||||
.json_schema(&SettingsJsonSchemaParams {
|
||||
language_names: &language_names,
|
||||
font_names,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
theme_names,
|
||||
icon_theme_names,
|
||||
});
|
||||
|
||||
let tasks_schema = task::TaskTemplates::generate_json_schema();
|
||||
let debug_schema = task::DebugTaskFile::generate_json_schema(&adapter_schemas);
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ pub(crate) fn render_ai_setup_page(
|
|||
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
update_settings_file(fs, cx, move |settings, _| {
|
||||
settings.disable_ai = Some(enabled);
|
||||
settings.disable_ai = Some(enabled.into());
|
||||
});
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use editor::EditorSettings;
|
|||
use gpui::{App, Pixels};
|
||||
pub use settings::{DockSide, Settings, ShowIndentGuides};
|
||||
use ui::scrollbars::{ScrollbarVisibility, ShowScrollbar};
|
||||
use util::MergeFrom;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct OutlinePanelSettings {
|
||||
|
|
@ -42,7 +41,7 @@ impl ScrollbarVisibility for OutlinePanelSettings {
|
|||
}
|
||||
|
||||
impl Settings for OutlinePanelSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let panel = content.outline_panel.as_ref().unwrap();
|
||||
Self {
|
||||
button: panel.button.unwrap(),
|
||||
|
|
@ -58,40 +57,12 @@ impl Settings for OutlinePanelSettings {
|
|||
auto_reveal_entries: panel.auto_reveal_entries.unwrap(),
|
||||
auto_fold_dirs: panel.auto_fold_dirs.unwrap(),
|
||||
scrollbar: ScrollbarSettings {
|
||||
show: panel.scrollbar.unwrap().show.flatten().map(Into::into),
|
||||
show: panel.scrollbar.unwrap().show.map(Into::into),
|
||||
},
|
||||
expand_outlines_with_depth: panel.expand_outlines_with_depth.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(panel) = content.outline_panel.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.button.merge_from(&panel.button);
|
||||
self.default_width
|
||||
.merge_from(&panel.default_width.map(Pixels::from));
|
||||
self.dock.merge_from(&panel.dock);
|
||||
self.file_icons.merge_from(&panel.file_icons);
|
||||
self.folder_icons.merge_from(&panel.folder_icons);
|
||||
self.git_status.merge_from(&panel.git_status);
|
||||
self.indent_size.merge_from(&panel.indent_size);
|
||||
|
||||
if let Some(indent_guides) = panel.indent_guides.as_ref() {
|
||||
self.indent_guides.show.merge_from(&indent_guides.show);
|
||||
}
|
||||
|
||||
self.auto_reveal_entries
|
||||
.merge_from(&panel.auto_reveal_entries);
|
||||
self.auto_fold_dirs.merge_from(&panel.auto_fold_dirs);
|
||||
|
||||
if let Some(scrollbar) = panel.scrollbar.as_ref()
|
||||
&& let Some(show) = scrollbar.show.flatten()
|
||||
{
|
||||
self.scrollbar.show = Some(show.into())
|
||||
}
|
||||
}
|
||||
fn import_from_vscode(
|
||||
vscode: &settings::VsCodeSettings,
|
||||
current: &mut settings::SettingsContent,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use rpc::{
|
|||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{SettingsContent, SettingsKey, SettingsStore, SettingsUi};
|
||||
use settings::{SettingsContent, SettingsStore};
|
||||
use util::{ResultExt as _, debug_panic};
|
||||
|
||||
use crate::ProjectEnvironment;
|
||||
|
|
@ -989,8 +989,7 @@ impl ExternalAgentServer for LocalCustomAgent {
|
|||
pub const GEMINI_NAME: &'static str = "gemini";
|
||||
pub const CLAUDE_CODE_NAME: &'static str = "claude";
|
||||
|
||||
#[derive(Default, Clone, JsonSchema, Debug, SettingsUi, SettingsKey, PartialEq)]
|
||||
#[settings_key(key = "agent_servers")]
|
||||
#[derive(Default, Clone, JsonSchema, Debug, PartialEq)]
|
||||
pub struct AllAgentServersSettings {
|
||||
pub gemini: Option<BuiltinAgentServerSettings>,
|
||||
pub claude: Option<BuiltinAgentServerSettings>,
|
||||
|
|
@ -1063,7 +1062,7 @@ impl From<settings::CustomAgentServerSettings> for CustomAgentServerSettings {
|
|||
}
|
||||
|
||||
impl settings::Settings for AllAgentServersSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let agent_settings = content.agent_servers.clone().unwrap();
|
||||
Self {
|
||||
gemini: agent_settings.gemini.map(Into::into),
|
||||
|
|
@ -1076,20 +1075,5 @@ impl settings::Settings for AllAgentServersSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(content) = &content.agent_servers else {
|
||||
return;
|
||||
};
|
||||
if let Some(gemini) = content.gemini.clone() {
|
||||
self.gemini = Some(gemini.into())
|
||||
};
|
||||
if let Some(claude) = content.claude.clone() {
|
||||
self.claude = Some(claude.into());
|
||||
}
|
||||
for (name, config) in content.custom.clone() {
|
||||
self.custom.insert(name, config.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -971,23 +971,18 @@ pub enum PulledDiagnostics {
|
|||
/// Whether to disable all AI features in Zed.
|
||||
///
|
||||
/// Default: false
|
||||
#[derive(Copy, Clone, Debug, settings::SettingsUi)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct DisableAiSettings {
|
||||
pub disable_ai: bool,
|
||||
}
|
||||
|
||||
impl settings::Settings for DisableAiSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
Self {
|
||||
disable_ai: content.disable_ai.unwrap(),
|
||||
disable_ai: content.disable_ai.unwrap().0,
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
// If disable_ai is true *in any file*, it is disabled.
|
||||
self.disable_ai = self.disable_ai || content.disable_ai.unwrap_or(false);
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
_vscode: &settings::VsCodeSettings,
|
||||
_current: &mut settings::SettingsContent,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub use settings::DirenvSettings;
|
|||
pub use settings::LspSettings;
|
||||
use settings::{
|
||||
DapSettingsContent, InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation,
|
||||
SettingsStore, SettingsUi, parse_json_with_comments, watch_config_file,
|
||||
SettingsStore, parse_json_with_comments, watch_config_file,
|
||||
};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
|
|
@ -29,7 +29,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
use task::{DebugTaskFile, TaskTemplates, VsCodeDebugTaskFile, VsCodeTaskFile};
|
||||
use util::{MergeFrom as _, ResultExt, serde::default_true};
|
||||
use util::{ResultExt, serde::default_true};
|
||||
use worktree::{PathChange, UpdatedEntriesSet, Worktree, WorktreeId};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -189,20 +189,7 @@ impl ContextServerSettings {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
JsonSchema,
|
||||
SettingsUi,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum DiagnosticSeverity {
|
||||
// No diagnostics are shown.
|
||||
Off,
|
||||
|
|
@ -444,7 +431,7 @@ pub struct LspPullDiagnosticsSettings {
|
|||
}
|
||||
|
||||
impl Settings for ProjectSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let project = &content.project.clone();
|
||||
let diagnostics = content.diagnostics.as_ref().unwrap();
|
||||
let lsp_pull_diagnostics = diagnostics.lsp_pull_diagnostics.as_ref().unwrap();
|
||||
|
|
@ -523,118 +510,6 @@ impl Settings for ProjectSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let project = &content.project;
|
||||
self.context_servers.extend(
|
||||
project
|
||||
.context_servers
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(key, value)| (key, value.into())),
|
||||
);
|
||||
self.dap.extend(
|
||||
project
|
||||
.dap
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(key, value)| (DebugAdapterName(key.into()), DapSettings::from(value))),
|
||||
);
|
||||
if let Some(diagnostics) = content.diagnostics.as_ref() {
|
||||
if let Some(inline) = &diagnostics.inline {
|
||||
self.diagnostics.inline.enabled.merge_from(&inline.enabled);
|
||||
self.diagnostics
|
||||
.inline
|
||||
.update_debounce_ms
|
||||
.merge_from(&inline.update_debounce_ms);
|
||||
self.diagnostics.inline.padding.merge_from(&inline.padding);
|
||||
self.diagnostics
|
||||
.inline
|
||||
.min_column
|
||||
.merge_from(&inline.min_column);
|
||||
if let Some(max_severity) = inline.max_severity {
|
||||
self.diagnostics.inline.max_severity = Some(max_severity.into())
|
||||
}
|
||||
}
|
||||
|
||||
self.diagnostics.button.merge_from(&diagnostics.button);
|
||||
self.diagnostics
|
||||
.include_warnings
|
||||
.merge_from(&diagnostics.include_warnings);
|
||||
if let Some(pull_diagnostics) = &diagnostics.lsp_pull_diagnostics {
|
||||
self.diagnostics
|
||||
.lsp_pull_diagnostics
|
||||
.enabled
|
||||
.merge_from(&pull_diagnostics.enabled);
|
||||
self.diagnostics
|
||||
.lsp_pull_diagnostics
|
||||
.debounce_ms
|
||||
.merge_from(&pull_diagnostics.debounce_ms);
|
||||
}
|
||||
}
|
||||
if let Some(git) = content.git.as_ref() {
|
||||
if let Some(branch_picker) = git.branch_picker.as_ref() {
|
||||
self.git
|
||||
.branch_picker
|
||||
.show_author_name
|
||||
.merge_from(&branch_picker.show_author_name);
|
||||
}
|
||||
if let Some(inline_blame) = git.inline_blame.as_ref() {
|
||||
self.git
|
||||
.inline_blame
|
||||
.enabled
|
||||
.merge_from(&inline_blame.enabled);
|
||||
self.git
|
||||
.inline_blame
|
||||
.delay_ms
|
||||
.merge_from(&inline_blame.delay_ms.map(std::time::Duration::from_millis));
|
||||
self.git
|
||||
.inline_blame
|
||||
.padding
|
||||
.merge_from(&inline_blame.padding);
|
||||
self.git
|
||||
.inline_blame
|
||||
.min_column
|
||||
.merge_from(&inline_blame.min_column);
|
||||
self.git
|
||||
.inline_blame
|
||||
.show_commit_summary
|
||||
.merge_from(&inline_blame.show_commit_summary);
|
||||
}
|
||||
self.git.git_gutter.merge_from(&git.git_gutter);
|
||||
self.git.hunk_style.merge_from(&git.hunk_style);
|
||||
if let Some(debounce) = git.gutter_debounce {
|
||||
self.git.gutter_debounce = Some(debounce);
|
||||
}
|
||||
}
|
||||
self.global_lsp_settings.button.merge_from(
|
||||
&content
|
||||
.global_lsp_settings
|
||||
.as_ref()
|
||||
.and_then(|settings| settings.button),
|
||||
);
|
||||
self.load_direnv
|
||||
.merge_from(&content.project.load_direnv.clone());
|
||||
|
||||
for (key, value) in content.project.lsp.clone() {
|
||||
self.lsp.insert(LanguageServerName(key.into()), value);
|
||||
}
|
||||
|
||||
if let Some(node) = content.node.as_ref() {
|
||||
self.node
|
||||
.ignore_system_version
|
||||
.merge_from(&node.ignore_system_version);
|
||||
if let Some(path) = node.path.clone() {
|
||||
self.node.path = Some(path);
|
||||
}
|
||||
if let Some(npm_path) = node.npm_path.clone() {
|
||||
self.node.npm_path = Some(npm_path);
|
||||
}
|
||||
}
|
||||
self.session
|
||||
.restore_unsaved_buffers
|
||||
.merge_from(&content.session.and_then(|s| s.restore_unsaved_buffers));
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
vscode: &settings::VsCodeSettings,
|
||||
current: &mut settings::SettingsContent,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use ui::{
|
|||
px,
|
||||
scrollbars::{ScrollbarVisibility, ShowScrollbar},
|
||||
};
|
||||
use util::MergeFrom;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ProjectPanelSettings {
|
||||
|
|
@ -56,7 +55,7 @@ impl ScrollbarVisibility for ProjectPanelSettings {
|
|||
}
|
||||
|
||||
impl Settings for ProjectPanelSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
|
||||
let project_panel = content.project_panel.clone().unwrap();
|
||||
Self {
|
||||
button: project_panel.button.unwrap(),
|
||||
|
|
@ -76,12 +75,7 @@ impl Settings for ProjectPanelSettings {
|
|||
auto_fold_dirs: project_panel.auto_fold_dirs.unwrap(),
|
||||
starts_open: project_panel.starts_open.unwrap(),
|
||||
scrollbar: ScrollbarSettings {
|
||||
show: project_panel
|
||||
.scrollbar
|
||||
.unwrap()
|
||||
.show
|
||||
.flatten()
|
||||
.map(Into::into),
|
||||
show: project_panel.scrollbar.unwrap().show.map(Into::into),
|
||||
},
|
||||
show_diagnostics: project_panel.show_diagnostics.unwrap(),
|
||||
hide_root: project_panel.hide_root.unwrap(),
|
||||
|
|
@ -89,47 +83,6 @@ impl Settings for ProjectPanelSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _cx: &mut ui::App) {
|
||||
let Some(project_panel) = content.project_panel.as_ref() else {
|
||||
return;
|
||||
};
|
||||
self.button.merge_from(&project_panel.button);
|
||||
self.hide_gitignore
|
||||
.merge_from(&project_panel.hide_gitignore);
|
||||
self.default_width
|
||||
.merge_from(&project_panel.default_width.map(px));
|
||||
self.dock.merge_from(&project_panel.dock);
|
||||
self.entry_spacing.merge_from(&project_panel.entry_spacing);
|
||||
self.file_icons.merge_from(&project_panel.file_icons);
|
||||
self.folder_icons.merge_from(&project_panel.folder_icons);
|
||||
self.git_status.merge_from(&project_panel.git_status);
|
||||
self.indent_size.merge_from(&project_panel.indent_size);
|
||||
self.sticky_scroll.merge_from(&project_panel.sticky_scroll);
|
||||
self.auto_reveal_entries
|
||||
.merge_from(&project_panel.auto_reveal_entries);
|
||||
self.auto_fold_dirs
|
||||
.merge_from(&project_panel.auto_fold_dirs);
|
||||
self.starts_open.merge_from(&project_panel.starts_open);
|
||||
self.show_diagnostics
|
||||
.merge_from(&project_panel.show_diagnostics);
|
||||
self.hide_root.merge_from(&project_panel.hide_root);
|
||||
self.drag_and_drop.merge_from(&project_panel.drag_and_drop);
|
||||
if let Some(show) = project_panel
|
||||
.indent_guides
|
||||
.as_ref()
|
||||
.and_then(|indent| indent.show)
|
||||
{
|
||||
self.indent_guides.show = show;
|
||||
}
|
||||
if let Some(show) = project_panel
|
||||
.scrollbar
|
||||
.as_ref()
|
||||
.and_then(|scrollbar| scrollbar.show)
|
||||
{
|
||||
self.scrollbar.show = show.map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
|
||||
if let Some(hide_gitignore) = vscode.read_bool("explorer.excludeGitIgnore") {
|
||||
current.project_panel.get_or_insert_default().hide_gitignore = Some(hide_gitignore);
|
||||
|
|
|
|||
|
|
@ -19,29 +19,28 @@ use remote::{
|
|||
SshConnectionOptions,
|
||||
};
|
||||
pub use settings::SshConnection;
|
||||
use settings::{Settings, WslConnection};
|
||||
use settings::{ExtendingVec, Settings, WslConnection};
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
ActiveTheme, Color, CommonAnimationExt, Context, Icon, IconName, IconSize, InteractiveElement,
|
||||
IntoElement, Label, LabelCommon, Styled, Window, prelude::*,
|
||||
};
|
||||
use util::MergeFrom;
|
||||
use workspace::{AppState, ModalView, Workspace};
|
||||
|
||||
pub struct SshSettings {
|
||||
pub ssh_connections: Vec<SshConnection>,
|
||||
pub wsl_connections: Vec<WslConnection>,
|
||||
pub ssh_connections: ExtendingVec<SshConnection>,
|
||||
pub wsl_connections: ExtendingVec<WslConnection>,
|
||||
/// Whether to read ~/.ssh/config for ssh connection sources.
|
||||
pub read_ssh_config: bool,
|
||||
}
|
||||
|
||||
impl SshSettings {
|
||||
pub fn ssh_connections(&self) -> impl Iterator<Item = SshConnection> + use<> {
|
||||
self.ssh_connections.clone().into_iter()
|
||||
self.ssh_connections.clone().0.into_iter()
|
||||
}
|
||||
|
||||
pub fn wsl_connections(&self) -> impl Iterator<Item = WslConnection> + use<> {
|
||||
self.wsl_connections.clone().into_iter()
|
||||
self.wsl_connections.clone().0.into_iter()
|
||||
}
|
||||
|
||||
pub fn fill_connection_options_from_settings(&self, options: &mut SshConnectionOptions) {
|
||||
|
|
@ -104,25 +103,14 @@ impl From<WslConnection> for Connection {
|
|||
}
|
||||
|
||||
impl Settings for SshSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let remote = &content.remote;
|
||||
Self {
|
||||
ssh_connections: remote.ssh_connections.clone().unwrap_or_default(),
|
||||
wsl_connections: remote.wsl_connections.clone().unwrap_or_default(),
|
||||
ssh_connections: remote.ssh_connections.clone().unwrap_or_default().into(),
|
||||
wsl_connections: remote.wsl_connections.clone().unwrap_or_default().into(),
|
||||
read_ssh_config: remote.read_ssh_config.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
if let Some(ssh_connections) = content.remote.ssh_connections.clone() {
|
||||
self.ssh_connections.extend(ssh_connections)
|
||||
}
|
||||
if let Some(wsl_connections) = content.remote.wsl_connections.clone() {
|
||||
self.wsl_connections.extend(wsl_connections)
|
||||
}
|
||||
self.read_ssh_config
|
||||
.merge_from(&content.remote.read_ssh_config);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RemoteConnectionPrompt {
|
||||
|
|
|
|||
|
|
@ -1885,7 +1885,7 @@ impl RemoteServerProjects {
|
|||
let ssh_settings = SshSettings::get_global(cx);
|
||||
let mut should_rebuild = false;
|
||||
|
||||
let ssh_connections_changed = ssh_settings.ssh_connections.iter().ne(state
|
||||
let ssh_connections_changed = ssh_settings.ssh_connections.0.iter().ne(state
|
||||
.servers
|
||||
.iter()
|
||||
.filter_map(|server| match server {
|
||||
|
|
@ -1896,7 +1896,7 @@ impl RemoteServerProjects {
|
|||
_ => None,
|
||||
}));
|
||||
|
||||
let wsl_connections_changed = ssh_settings.wsl_connections.iter().ne(state
|
||||
let wsl_connections_changed = ssh_settings.wsl_connections.0.iter().ne(state
|
||||
.servers
|
||||
.iter()
|
||||
.filter_map(|server| match server {
|
||||
|
|
|
|||
|
|
@ -19,19 +19,10 @@ impl JupyterSettings {
|
|||
}
|
||||
|
||||
impl Settings for JupyterSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let jupyter = content.editor.jupyter.clone().unwrap();
|
||||
Self {
|
||||
kernel_selections: jupyter.kernel_selections.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(jupyter) = content.editor.jupyter.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if let Some(kernel_selections) = jupyter.kernel_selections.clone() {
|
||||
self.kernel_selections.extend(kernel_selections)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use gpui::App;
|
||||
use settings::Settings;
|
||||
use util::MergeFrom;
|
||||
|
||||
/// Settings for configuring REPL display and behavior.
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -18,7 +17,7 @@ pub struct ReplSettings {
|
|||
}
|
||||
|
||||
impl Settings for ReplSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let repl = content.repl.as_ref().unwrap();
|
||||
|
||||
Self {
|
||||
|
|
@ -26,13 +25,4 @@ impl Settings for ReplSettings {
|
|||
max_columns: repl.max_columns.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(repl) = content.repl.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.max_columns.merge_from(&repl.max_columns);
|
||||
self.max_lines.merge_from(&repl.max_lines);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ rust-embed.workspace = true
|
|||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
settings_ui_macros.workspace = true
|
||||
settings_macros = { path = "../settings_macros" }
|
||||
serde_json_lenient.workspace = true
|
||||
serde_repr.workspace = true
|
||||
serde_path_to_error.workspace = true
|
||||
|
|
|
|||
|
|
@ -2,21 +2,17 @@ use std::fmt::{Display, Formatter};
|
|||
|
||||
use crate::{
|
||||
self as settings,
|
||||
settings_content::{self, BaseKeymapContent, SettingsContent},
|
||||
settings_content::{BaseKeymapContent, SettingsContent},
|
||||
};
|
||||
use gpui::App;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings::{Settings, VsCodeSettings};
|
||||
use settings_ui_macros::{SettingsKey, SettingsUi};
|
||||
|
||||
/// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
|
||||
///
|
||||
/// Default: VSCode
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default, SettingsUi,
|
||||
)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
|
||||
pub enum BaseKeymap {
|
||||
#[default]
|
||||
VSCode,
|
||||
|
|
@ -134,37 +130,11 @@ impl BaseKeymap {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
JsonSchema,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Default,
|
||||
SettingsUi,
|
||||
SettingsKey,
|
||||
)]
|
||||
// extracted so that it can be an option, and still work with derive(SettingsUi)
|
||||
#[settings_key(None)]
|
||||
#[skip_serializing_none]
|
||||
pub struct BaseKeymapSetting {
|
||||
pub base_keymap: Option<BaseKeymap>,
|
||||
}
|
||||
|
||||
impl Settings for BaseKeymap {
|
||||
fn from_defaults(s: &crate::settings_content::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(s: &crate::settings_content::SettingsContent, _cx: &mut App) -> Self {
|
||||
s.base_keymap.unwrap().into()
|
||||
}
|
||||
|
||||
fn refine(&mut self, s: &settings_content::SettingsContent, _cx: &mut App) {
|
||||
if let Some(base_keymap) = s.base_keymap {
|
||||
*self = base_keymap.into();
|
||||
};
|
||||
}
|
||||
|
||||
fn import_from_vscode(_vscode: &VsCodeSettings, current: &mut SettingsContent) {
|
||||
current.base_keymap = Some(BaseKeymapContent::VSCode);
|
||||
}
|
||||
|
|
|
|||
164
crates/settings/src/merge_from.rs
Normal file
164
crates/settings/src/merge_from.rs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
/// Trait for recursively merging settings structures.
|
||||
///
|
||||
/// This trait allows settings objects to be merged from optional sources,
|
||||
/// where `None` values are ignored and `Some` values override existing values.
|
||||
///
|
||||
/// HashMaps, structs and similar types are merged by combining their contents key-wise,
|
||||
/// but all other types (including Vecs) are last-write-wins.
|
||||
/// (Though see also ExtendingVec and SaturatingBool)
|
||||
#[allow(unused)]
|
||||
pub trait MergeFrom {
|
||||
/// Merge from an optional source of the same type.
|
||||
/// If `other` is `None`, no changes are made.
|
||||
/// If `other` is `Some(value)`, fields from `value` are merged into `self`.
|
||||
fn merge_from(&mut self, other: Option<&Self>);
|
||||
}
|
||||
|
||||
macro_rules! merge_from_overwrites {
|
||||
($($type:ty),+) => {
|
||||
$(
|
||||
impl MergeFrom for $type {
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
if let Some(value) = other {
|
||||
*self = value.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
merge_from_overwrites!(
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
usize,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
bool,
|
||||
f64,
|
||||
f32,
|
||||
std::num::NonZeroUsize,
|
||||
std::num::NonZeroU32,
|
||||
String,
|
||||
std::sync::Arc<str>,
|
||||
gpui::SharedString,
|
||||
std::path::PathBuf,
|
||||
gpui::Modifiers,
|
||||
gpui::FontFeatures
|
||||
);
|
||||
|
||||
impl<T: Clone> MergeFrom for Vec<T> {
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
if let Some(other) = other {
|
||||
*self = other.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementations for collections that extend/merge their contents
|
||||
impl<K, V> MergeFrom for collections::HashMap<K, V>
|
||||
where
|
||||
K: Clone + std::hash::Hash + Eq,
|
||||
V: Clone + MergeFrom,
|
||||
{
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
let Some(other) = other else { return };
|
||||
for (k, v) in other {
|
||||
if let Some(existing) = self.get_mut(k) {
|
||||
existing.merge_from(Some(v));
|
||||
} else {
|
||||
self.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> MergeFrom for collections::BTreeMap<K, V>
|
||||
where
|
||||
K: Clone + std::hash::Hash + Eq + Ord,
|
||||
V: Clone + MergeFrom,
|
||||
{
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
let Some(other) = other else { return };
|
||||
for (k, v) in other {
|
||||
if let Some(existing) = self.get_mut(k) {
|
||||
existing.merge_from(Some(v));
|
||||
} else {
|
||||
self.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> MergeFrom for collections::IndexMap<K, V>
|
||||
where
|
||||
K: std::hash::Hash + Eq + Clone,
|
||||
// Q: ?Sized + std::hash::Hash + collections::Equivalent<K> + Eq,
|
||||
V: Clone + MergeFrom,
|
||||
{
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
let Some(other) = other else { return };
|
||||
for (k, v) in other {
|
||||
if let Some(existing) = self.get_mut(k) {
|
||||
existing.merge_from(Some(v));
|
||||
} else {
|
||||
self.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MergeFrom for collections::BTreeSet<T>
|
||||
where
|
||||
T: Clone + Ord,
|
||||
{
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
let Some(other) = other else { return };
|
||||
for item in other {
|
||||
self.insert(item.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MergeFrom for collections::HashSet<T>
|
||||
where
|
||||
T: Clone + std::hash::Hash + Eq,
|
||||
{
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
let Some(other) = other else { return };
|
||||
for item in other {
|
||||
self.insert(item.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeFrom for serde_json::Value {
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
let Some(other) = other else { return };
|
||||
match (self, other) {
|
||||
(serde_json::Value::Object(this), serde_json::Value::Object(other)) => {
|
||||
for (k, v) in other {
|
||||
if let Some(existing) = this.get_mut(k) {
|
||||
existing.merge_from(other.get(k));
|
||||
} else {
|
||||
this.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
(this, other) => *this = other.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MergeFrom + Clone> MergeFrom for Rc<T> {
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
let Some(other) = other else { return };
|
||||
let mut this: T = self.as_ref().clone();
|
||||
this.merge_from(Some(other.as_ref()));
|
||||
*self = Rc::new(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
mod base_keymap_setting;
|
||||
mod editable_setting_control;
|
||||
mod keymap_file;
|
||||
pub mod merge_from;
|
||||
mod settings_content;
|
||||
mod settings_file;
|
||||
mod settings_json;
|
||||
|
|
@ -27,8 +28,7 @@ pub use settings_store::{
|
|||
InvalidSettingsError, LocalSettingsKind, Settings, SettingsKey, SettingsLocation, SettingsStore,
|
||||
};
|
||||
pub use settings_ui_core::*;
|
||||
// Re-export the derive macro
|
||||
pub use settings_ui_macros::{SettingsKey, SettingsUi};
|
||||
|
||||
pub use vscode_import::{VsCodeSettings, VsCodeSettingsSource};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -22,15 +22,16 @@ use release_channel::ReleaseChannel;
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
use std::collections::BTreeSet;
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
pub use util::serde::default_true;
|
||||
|
||||
use crate::ActiveSettingsProfileName;
|
||||
use crate::{ActiveSettingsProfileName, merge_from};
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct SettingsContent {
|
||||
#[serde(flatten)]
|
||||
pub project: ProjectSettingsContent,
|
||||
|
|
@ -153,7 +154,7 @@ pub struct SettingsContent {
|
|||
/// Whether to disable all AI features in Zed.
|
||||
///
|
||||
/// Default: false
|
||||
pub disable_ai: Option<bool>,
|
||||
pub disable_ai: Option<SaturatingBool>,
|
||||
|
||||
/// Settings related to Vim mode in Zed.
|
||||
pub vim: Option<VimSettingsContent>,
|
||||
|
|
@ -166,14 +167,14 @@ impl SettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct ServerSettingsContent {
|
||||
#[serde(flatten)]
|
||||
pub project: ProjectSettingsContent,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct UserSettingsContent {
|
||||
#[serde(flatten)]
|
||||
pub content: Box<SettingsContent>,
|
||||
|
|
@ -225,7 +226,9 @@ impl UserSettingsContent {
|
|||
/// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
|
||||
///
|
||||
/// Default: VSCode
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq, Default,
|
||||
)]
|
||||
pub enum BaseKeymapContent {
|
||||
#[default]
|
||||
VSCode,
|
||||
|
|
@ -239,7 +242,7 @@ pub enum BaseKeymapContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
|
||||
pub struct TitleBarSettingsContent {
|
||||
/// Controls when the title bar is visible: "always" | "never" | "hide_in_full_screen".
|
||||
///
|
||||
|
|
@ -275,7 +278,7 @@ pub struct TitleBarSettingsContent {
|
|||
pub show_menus: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TitleBarVisibility {
|
||||
Always,
|
||||
|
|
@ -285,7 +288,7 @@ pub enum TitleBarVisibility {
|
|||
|
||||
/// Configuration of audio in Zed.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
|
||||
pub struct AudioSettingsContent {
|
||||
/// Opt into the new audio system.
|
||||
#[serde(rename = "experimental.rodio_audio", default)]
|
||||
|
|
@ -307,7 +310,7 @@ pub struct AudioSettingsContent {
|
|||
|
||||
/// Control what info is collected by Zed.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Debug, MergeFrom)]
|
||||
pub struct TelemetrySettingsContent {
|
||||
/// Send debug info like crash reports.
|
||||
///
|
||||
|
|
@ -320,7 +323,7 @@ pub struct TelemetrySettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
#[derive(Default, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Clone, MergeFrom)]
|
||||
pub struct DebuggerSettingsContent {
|
||||
/// Determines the stepping granularity.
|
||||
///
|
||||
|
|
@ -353,7 +356,9 @@ pub struct DebuggerSettingsContent {
|
|||
}
|
||||
|
||||
/// The granularity of one 'step' in the stepping requests `next`, `stepIn`, `stepOut`, and `stepBack`.
|
||||
#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy, Deserialize, Serialize, JsonSchema)]
|
||||
#[derive(
|
||||
PartialEq, Eq, Debug, Hash, Clone, Copy, Deserialize, Serialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SteppingGranularity {
|
||||
/// The step should allow the program to run until the current statement has finished executing.
|
||||
|
|
@ -366,7 +371,7 @@ pub enum SteppingGranularity {
|
|||
Instruction,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DockPosition {
|
||||
Left,
|
||||
|
|
@ -376,7 +381,7 @@ pub enum DockPosition {
|
|||
|
||||
/// Settings for slash commands.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct SlashCommandSettings {
|
||||
/// Settings for the `/cargo-workspace` slash command.
|
||||
pub cargo_workspace: Option<CargoWorkspaceCommandSettings>,
|
||||
|
|
@ -384,7 +389,7 @@ pub struct SlashCommandSettings {
|
|||
|
||||
/// Settings for the `/cargo-workspace` slash command.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct CargoWorkspaceCommandSettings {
|
||||
/// Whether `/cargo-workspace` is enabled.
|
||||
pub enabled: Option<bool>,
|
||||
|
|
@ -392,7 +397,7 @@ pub struct CargoWorkspaceCommandSettings {
|
|||
|
||||
/// Configuration of voice calls in Zed.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
|
||||
pub struct CallSettingsContent {
|
||||
/// Whether the microphone should be muted when joining a channel or a call.
|
||||
///
|
||||
|
|
@ -406,7 +411,7 @@ pub struct CallSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug, Default, Clone, JsonSchema)]
|
||||
#[derive(Deserialize, Serialize, PartialEq, Debug, Default, Clone, JsonSchema, MergeFrom)]
|
||||
pub struct ExtensionSettingsContent {
|
||||
/// The extensions that should be automatically installed by Zed.
|
||||
///
|
||||
|
|
@ -421,7 +426,7 @@ pub struct ExtensionSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
|
||||
pub struct GitPanelSettingsContent {
|
||||
/// Whether to show the panel button in the status bar.
|
||||
///
|
||||
|
|
@ -462,7 +467,9 @@ pub struct GitPanelSettingsContent {
|
|||
pub collapse_untracked_diff: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StatusStyle {
|
||||
#[default]
|
||||
|
|
@ -471,13 +478,13 @@ pub enum StatusStyle {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct ScrollbarSettings {
|
||||
pub show: Option<ShowScrollbar>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
|
||||
pub struct NotificationPanelSettingsContent {
|
||||
/// Whether to show the panel button in the status bar.
|
||||
///
|
||||
|
|
@ -494,7 +501,7 @@ pub struct NotificationPanelSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
|
||||
pub struct PanelSettingsContent {
|
||||
/// Whether to show the panel button in the status bar.
|
||||
///
|
||||
|
|
@ -511,7 +518,7 @@ pub struct PanelSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
|
||||
pub struct MessageEditorSettings {
|
||||
/// Whether to automatically replace emoji shortcodes with emoji characters.
|
||||
/// For example: typing `:wave:` gets replaced with `👋`.
|
||||
|
|
@ -521,7 +528,7 @@ pub struct MessageEditorSettings {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
|
||||
pub struct FileFinderSettingsContent {
|
||||
/// Whether to show file icons in the file finder.
|
||||
///
|
||||
|
|
@ -549,10 +556,12 @@ pub struct FileFinderSettingsContent {
|
|||
///
|
||||
/// Default: None
|
||||
/// todo() -> Change this type to an enum
|
||||
pub include_ignored: Option<Option<bool>>,
|
||||
pub include_ignored: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum FileFinderWidthContent {
|
||||
#[default]
|
||||
|
|
@ -564,7 +573,7 @@ pub enum FileFinderWidthContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Debug, JsonSchema)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Debug, JsonSchema, MergeFrom)]
|
||||
pub struct VimSettingsContent {
|
||||
pub default_mode: Option<ModeContent>,
|
||||
pub toggle_relative_line_numbers: Option<bool>,
|
||||
|
|
@ -575,7 +584,7 @@ pub struct VimSettingsContent {
|
|||
pub cursor_shape: Option<CursorShapeSettings>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Debug)]
|
||||
#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ModeContent {
|
||||
#[default]
|
||||
|
|
@ -585,7 +594,7 @@ pub enum ModeContent {
|
|||
}
|
||||
|
||||
/// Controls when to use system clipboard.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum UseSystemClipboard {
|
||||
/// Don't use system clipboard.
|
||||
|
|
@ -598,7 +607,7 @@ pub enum UseSystemClipboard {
|
|||
|
||||
/// The settings for cursor shape.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
pub struct CursorShapeSettings {
|
||||
/// Cursor shape for the normal mode.
|
||||
///
|
||||
|
|
@ -620,7 +629,7 @@ pub struct CursorShapeSettings {
|
|||
|
||||
/// Settings specific to journaling
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct JournalSettingsContent {
|
||||
/// The path of the directory where journal entries are stored.
|
||||
///
|
||||
|
|
@ -632,7 +641,7 @@ pub struct JournalSettingsContent {
|
|||
pub hour_format: Option<HourFormat>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HourFormat {
|
||||
#[default]
|
||||
|
|
@ -641,7 +650,7 @@ pub enum HourFormat {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
|
||||
pub struct OutlinePanelSettingsContent {
|
||||
/// Whether to show the outline panel button in the status bar.
|
||||
///
|
||||
|
|
@ -695,14 +704,14 @@ pub struct OutlinePanelSettingsContent {
|
|||
pub expand_outlines_with_depth: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, Copy, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DockSide {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowIndentGuides {
|
||||
Always,
|
||||
|
|
@ -710,13 +719,13 @@ pub enum ShowIndentGuides {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct IndentGuidesSettingsContent {
|
||||
/// When to show the scrollbar in the outline panel.
|
||||
pub show: Option<ShowIndentGuides>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialEq, Debug, JsonSchema, Deserialize, Serialize)]
|
||||
#[derive(Clone, Copy, Default, PartialEq, Debug, JsonSchema, MergeFrom, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum LineIndicatorFormat {
|
||||
Short,
|
||||
|
|
@ -726,7 +735,7 @@ pub enum LineIndicatorFormat {
|
|||
|
||||
/// The settings for the image viewer.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Default, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, Default, PartialEq)]
|
||||
pub struct ImageViewerSettingsContent {
|
||||
/// The unit to use for displaying image file sizes.
|
||||
///
|
||||
|
|
@ -735,7 +744,7 @@ pub struct ImageViewerSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, Default, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, Default, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ImageFileSizeUnit {
|
||||
/// Displays file size in binary units (e.g., KiB, MiB).
|
||||
|
|
@ -746,7 +755,7 @@ pub enum ImageFileSizeUnit {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct RemoteSettingsContent {
|
||||
pub ssh_connections: Option<Vec<SshConnection>>,
|
||||
pub wsl_connections: Option<Vec<WslConnection>>,
|
||||
|
|
@ -754,7 +763,7 @@ pub struct RemoteSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct SshConnection {
|
||||
pub host: SharedString,
|
||||
pub username: Option<String>,
|
||||
|
|
@ -774,7 +783,7 @@ pub struct SshConnection {
|
|||
pub port_forwards: Option<Vec<SshPortForwardOption>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, JsonSchema, Debug)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Debug)]
|
||||
pub struct WslConnection {
|
||||
pub distro_name: SharedString,
|
||||
pub user: Option<String>,
|
||||
|
|
@ -791,7 +800,7 @@ pub struct SshProject {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, JsonSchema, MergeFrom)]
|
||||
pub struct SshPortForwardOption {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub local_host: Option<String>,
|
||||
|
|
@ -803,7 +812,7 @@ pub struct SshPortForwardOption {
|
|||
|
||||
/// Settings for configuring REPL display and behavior.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct ReplSettingsContent {
|
||||
/// Maximum number of lines to keep in REPL's scrollback buffer.
|
||||
/// Clamped with [4, 256] range.
|
||||
|
|
@ -816,3 +825,42 @@ pub struct ReplSettingsContent {
|
|||
/// Default: 128
|
||||
pub max_columns: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ExtendingVec<T>(pub Vec<T>);
|
||||
|
||||
impl<T> Into<Vec<T>> for ExtendingVec<T> {
|
||||
fn into(self) -> Vec<T> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl<T> From<Vec<T>> for ExtendingVec<T> {
|
||||
fn from(vec: Vec<T>) -> Self {
|
||||
ExtendingVec(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> merge_from::MergeFrom for ExtendingVec<T> {
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
if let Some(other) = other {
|
||||
self.0.extend_from_slice(other.0.as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct SaturatingBool(pub bool);
|
||||
|
||||
impl From<bool> for SaturatingBool {
|
||||
fn from(value: bool) -> Self {
|
||||
SaturatingBool(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl merge_from::MergeFrom for SaturatingBool {
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
if let Some(other) = other {
|
||||
self.0 |= other.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ use gpui::SharedString;
|
|||
use schemars::{JsonSchema, json_schema};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
use std::{borrow::Cow, path::PathBuf, sync::Arc};
|
||||
|
||||
use crate::DockPosition;
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, JsonSchema, Debug, Default)]
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, Default)]
|
||||
pub struct AgentSettingsContent {
|
||||
/// Whether the Agent is enabled.
|
||||
///
|
||||
|
|
@ -168,7 +169,7 @@ impl AgentSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct AgentProfileContent {
|
||||
pub name: Arc<str>,
|
||||
#[serde(default)]
|
||||
|
|
@ -180,12 +181,12 @@ pub struct AgentProfileContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct ContextServerPresetContent {
|
||||
pub tools: IndexMap<Arc<str>, bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DefaultAgentView {
|
||||
#[default]
|
||||
|
|
@ -193,7 +194,7 @@ pub enum DefaultAgentView {
|
|||
TextThread,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum NotifyWhenAgentWaiting {
|
||||
#[default]
|
||||
|
|
@ -203,13 +204,13 @@ pub enum NotifyWhenAgentWaiting {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct LanguageModelSelection {
|
||||
pub provider: LanguageModelProviderSetting,
|
||||
pub model: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CompletionMode {
|
||||
#[default]
|
||||
|
|
@ -219,14 +220,14 @@ pub enum CompletionMode {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct LanguageModelParameters {
|
||||
pub provider: Option<LanguageModelProviderSetting>,
|
||||
pub model: Option<SharedString>,
|
||||
pub temperature: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, MergeFrom)]
|
||||
pub struct LanguageModelProviderSetting(pub String);
|
||||
|
||||
impl JsonSchema for LanguageModelProviderSetting {
|
||||
|
|
@ -277,7 +278,7 @@ impl From<&str> for LanguageModelProviderSetting {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, PartialEq, Deserialize, Serialize, Clone, JsonSchema, Debug)]
|
||||
#[derive(Default, PartialEq, Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug)]
|
||||
pub struct AllAgentServersSettings {
|
||||
pub gemini: Option<BuiltinAgentServerSettings>,
|
||||
pub claude: Option<BuiltinAgentServerSettings>,
|
||||
|
|
@ -288,7 +289,7 @@ pub struct AllAgentServersSettings {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug, PartialEq)]
|
||||
pub struct BuiltinAgentServerSettings {
|
||||
/// Absolute path to a binary to be used when launching this agent.
|
||||
///
|
||||
|
|
@ -320,7 +321,7 @@ pub struct BuiltinAgentServerSettings {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Deserialize, Serialize, Clone, JsonSchema, MergeFrom, Debug, PartialEq)]
|
||||
pub struct CustomAgentServerSettings {
|
||||
#[serde(rename = "command")]
|
||||
pub path: PathBuf,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ use collections::HashMap;
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
|
||||
use crate::{DiagnosticSeverityContent, ShowScrollbar};
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct EditorSettingsContent {
|
||||
/// Whether the cursor blinks in the editor.
|
||||
///
|
||||
|
|
@ -194,7 +195,7 @@ pub struct EditorSettingsContent {
|
|||
|
||||
// Status bar related settings
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct StatusBarContent {
|
||||
/// Whether to display the active language button in the status bar.
|
||||
///
|
||||
|
|
@ -208,7 +209,7 @@ pub struct StatusBarContent {
|
|||
|
||||
// Toolbar related settings
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct ToolbarContent {
|
||||
/// Whether to display breadcrumbs in the editor toolbar.
|
||||
///
|
||||
|
|
@ -235,7 +236,7 @@ pub struct ToolbarContent {
|
|||
|
||||
/// Scrollbar related settings
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Default)]
|
||||
pub struct ScrollbarContent {
|
||||
/// When to show the scrollbar in the editor.
|
||||
///
|
||||
|
|
@ -271,7 +272,7 @@ pub struct ScrollbarContent {
|
|||
|
||||
/// Minimap related settings
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct MinimapContent {
|
||||
/// When to show the minimap in the editor.
|
||||
///
|
||||
|
|
@ -296,7 +297,7 @@ pub struct MinimapContent {
|
|||
/// How to highlight the current line in the minimap.
|
||||
///
|
||||
/// Default: inherits editor line highlights setting
|
||||
pub current_line_highlight: Option<Option<CurrentLineHighlight>>,
|
||||
pub current_line_highlight: Option<CurrentLineHighlight>,
|
||||
|
||||
/// Maximum number of columns to display in the minimap.
|
||||
///
|
||||
|
|
@ -306,7 +307,7 @@ pub struct MinimapContent {
|
|||
|
||||
/// Forcefully enable or disable the scrollbar for each axis
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Default)]
|
||||
pub struct ScrollbarAxesContent {
|
||||
/// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings.
|
||||
///
|
||||
|
|
@ -321,7 +322,7 @@ pub struct ScrollbarAxesContent {
|
|||
|
||||
/// Gutter related settings
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct GutterContent {
|
||||
/// Whether to show line numbers in the gutter.
|
||||
///
|
||||
|
|
@ -346,7 +347,9 @@ pub struct GutterContent {
|
|||
}
|
||||
|
||||
/// How to render LSP `textDocument/documentColor` colors in the editor.
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DocumentColorsRenderMode {
|
||||
/// Do not query and render document colors.
|
||||
|
|
@ -360,7 +363,7 @@ pub enum DocumentColorsRenderMode {
|
|||
Background,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CurrentLineHighlight {
|
||||
// Don't highlight the current line.
|
||||
|
|
@ -374,7 +377,7 @@ pub enum CurrentLineHighlight {
|
|||
}
|
||||
|
||||
/// When to populate a new search's query based on the text under the cursor.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SeedQuerySetting {
|
||||
/// Always populate the search query with the word under the cursor.
|
||||
|
|
@ -386,7 +389,9 @@ pub enum SeedQuerySetting {
|
|||
}
|
||||
|
||||
/// What to do when multibuffer is double clicked in some of its excerpts (parts of singleton buffers).
|
||||
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(
|
||||
Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DoubleClickInMultibuffer {
|
||||
/// Behave as a regular buffer and select the whole word.
|
||||
|
|
@ -400,7 +405,9 @@ pub enum DoubleClickInMultibuffer {
|
|||
/// When to show the minimap thumb.
|
||||
///
|
||||
/// Default: always
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MinimapThumb {
|
||||
/// Show the minimap thumb only when the mouse is hovering over the minimap.
|
||||
|
|
@ -413,7 +420,9 @@ pub enum MinimapThumb {
|
|||
/// Defines the border style for the minimap's scrollbar thumb.
|
||||
///
|
||||
/// Default: left_open
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MinimapThumbBorder {
|
||||
/// Displays a border on all sides of the thumb.
|
||||
|
|
@ -432,7 +441,7 @@ pub enum MinimapThumbBorder {
|
|||
/// Which diagnostic indicators to show in the scrollbar.
|
||||
///
|
||||
/// Default: all
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ScrollbarDiagnostics {
|
||||
/// Show all diagnostic levels: hint, information, warnings, error.
|
||||
|
|
@ -450,7 +459,7 @@ pub enum ScrollbarDiagnostics {
|
|||
/// The key to use for adding multiple cursors
|
||||
///
|
||||
/// Default: alt
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MultiCursorModifier {
|
||||
Alt,
|
||||
|
|
@ -461,7 +470,7 @@ pub enum MultiCursorModifier {
|
|||
/// Whether the editor will scroll beyond the last line.
|
||||
///
|
||||
/// Default: one_page
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ScrollBeyondLastLine {
|
||||
/// The editor will not scroll beyond the last line.
|
||||
|
|
@ -475,7 +484,9 @@ pub enum ScrollBeyondLastLine {
|
|||
}
|
||||
|
||||
/// The shape of a selection cursor.
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CursorShape {
|
||||
/// A vertical bar
|
||||
|
|
@ -490,7 +501,9 @@ pub enum CursorShape {
|
|||
}
|
||||
|
||||
/// What to do when go to definition yields no results.
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GoToDefinitionFallback {
|
||||
/// Disables the fallback.
|
||||
|
|
@ -503,7 +516,9 @@ pub enum GoToDefinitionFallback {
|
|||
/// Determines when the mouse cursor should be hidden in an editor or input box.
|
||||
///
|
||||
/// Default: on_typing_and_movement
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HideMouseMode {
|
||||
/// Never hide the mouse cursor
|
||||
|
|
@ -518,7 +533,9 @@ pub enum HideMouseMode {
|
|||
/// Determines how snippets are sorted relative to other completion items.
|
||||
///
|
||||
/// Default: inline
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SnippetSortOrder {
|
||||
/// Place snippets at the top of the completion list
|
||||
|
|
@ -534,7 +551,7 @@ pub enum SnippetSortOrder {
|
|||
|
||||
/// Default options for buffer and project search items.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct SearchSettingsContent {
|
||||
/// Whether to show the project search button in the status bar.
|
||||
pub button: Option<bool>,
|
||||
|
|
@ -545,7 +562,7 @@ pub struct SearchSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct JupyterContent {
|
||||
/// Whether the Jupyter feature is enabled.
|
||||
|
|
@ -561,7 +578,7 @@ pub struct JupyterContent {
|
|||
|
||||
/// Whether to allow drag and drop text selection in buffer.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct DragAndDropSelectionContent {
|
||||
/// When true, enables drag and drop text selection in buffer.
|
||||
///
|
||||
|
|
@ -577,7 +594,9 @@ pub struct DragAndDropSelectionContent {
|
|||
/// When to show the minimap in the editor.
|
||||
///
|
||||
/// Default: never
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowMinimap {
|
||||
/// Follow the visibility of the scrollbar.
|
||||
|
|
@ -592,7 +611,9 @@ pub enum ShowMinimap {
|
|||
/// Where to show the minimap in the editor.
|
||||
///
|
||||
/// Default: all_editors
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DisplayIn {
|
||||
/// Show on all open editors.
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use serde::{
|
|||
de::{self, IntoDeserializer, MapAccess, SeqAccess, Visitor},
|
||||
};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
use std::sync::Arc;
|
||||
use util::schemars::replace_subschema;
|
||||
|
||||
use crate::ParameterizedJsonSchema;
|
||||
use crate::{ExtendingVec, merge_from};
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
|
|
@ -31,12 +31,50 @@ pub struct AllLanguageSettingsContent {
|
|||
/// Settings for associating file extensions and filenames
|
||||
/// with languages.
|
||||
#[serde(default)]
|
||||
pub file_types: HashMap<Arc<str>, Vec<String>>,
|
||||
pub file_types: HashMap<Arc<str>, ExtendingVec<String>>,
|
||||
}
|
||||
|
||||
fn merge_option<T: merge_from::MergeFrom + Clone>(this: &mut Option<T>, other: Option<&T>) {
|
||||
let Some(other) = other else { return };
|
||||
if let Some(this) = this {
|
||||
this.merge_from(Some(other));
|
||||
} else {
|
||||
this.replace(other.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl merge_from::MergeFrom for AllLanguageSettingsContent {
|
||||
fn merge_from(&mut self, other: Option<&Self>) {
|
||||
let Some(other) = other else { return };
|
||||
self.file_types.merge_from(Some(&other.file_types));
|
||||
merge_option(&mut self.features, other.features.as_ref());
|
||||
merge_option(&mut self.edit_predictions, other.edit_predictions.as_ref());
|
||||
|
||||
// A user's global settings override the default global settings and
|
||||
// all default language-specific settings.
|
||||
//
|
||||
self.defaults.merge_from(Some(&other.defaults));
|
||||
for language_settings in self.languages.0.values_mut() {
|
||||
language_settings.merge_from(Some(&other.defaults));
|
||||
}
|
||||
|
||||
// A user's language-specific settings override default language-specific settings.
|
||||
for (language_name, user_language_settings) in &other.languages.0 {
|
||||
if let Some(existing) = self.languages.0.get_mut(language_name) {
|
||||
existing.merge_from(Some(&user_language_settings));
|
||||
} else {
|
||||
let mut new_settings = self.defaults.clone();
|
||||
new_settings.merge_from(Some(&user_language_settings));
|
||||
|
||||
self.languages.0.insert(language_name.clone(), new_settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The settings for enabling/disabling features.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct FeaturesContent {
|
||||
/// Determines which edit prediction provider to use.
|
||||
|
|
@ -44,7 +82,9 @@ pub struct FeaturesContent {
|
|||
}
|
||||
|
||||
/// The provider that supplies edit predictions.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum EditPredictionProvider {
|
||||
None,
|
||||
|
|
@ -56,7 +96,7 @@ pub enum EditPredictionProvider {
|
|||
|
||||
/// The contents of the edit prediction settings.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct EditPredictionSettingsContent {
|
||||
/// A list of globs representing files that edit predictions should be disabled for.
|
||||
/// This list adds to a pre-existing, sensible default set of globs.
|
||||
|
|
@ -73,7 +113,7 @@ pub struct EditPredictionSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct CopilotSettingsContent {
|
||||
/// HTTP/HTTPS proxy to use for Copilot.
|
||||
///
|
||||
|
|
@ -90,7 +130,9 @@ pub struct CopilotSettingsContent {
|
|||
}
|
||||
|
||||
/// The mode in which edit predictions should be displayed.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum EditPredictionsMode {
|
||||
/// If provider supports it, display inline when holding modifier key (e.g., alt).
|
||||
|
|
@ -104,7 +146,7 @@ pub enum EditPredictionsMode {
|
|||
}
|
||||
|
||||
/// Controls the soft-wrapping behavior in the editor.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SoftWrap {
|
||||
/// Prefer a single line generally, unless an overly long line is encountered.
|
||||
|
|
@ -122,7 +164,7 @@ pub enum SoftWrap {
|
|||
|
||||
/// The settings for a particular language.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct LanguageSettingsContent {
|
||||
/// How many columns a tab should occupy.
|
||||
///
|
||||
|
|
@ -289,7 +331,7 @@ pub struct LanguageSettingsContent {
|
|||
}
|
||||
|
||||
/// Controls how whitespace should be displayedin the editor.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowWhitespaceSetting {
|
||||
/// Draw whitespace only for the selected text.
|
||||
|
|
@ -310,7 +352,7 @@ pub enum ShowWhitespaceSetting {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct WhitespaceMap {
|
||||
pub space: Option<String>,
|
||||
pub tab: Option<String>,
|
||||
|
|
@ -331,7 +373,7 @@ impl WhitespaceMap {
|
|||
}
|
||||
|
||||
/// The behavior of `editor::Rewrap`.
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RewrapBehavior {
|
||||
/// Only rewrap within comments.
|
||||
|
|
@ -344,7 +386,7 @@ pub enum RewrapBehavior {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct JsxTagAutoCloseSettingsContent {
|
||||
/// Enables or disables auto-closing of JSX tags.
|
||||
pub enabled: Option<bool>,
|
||||
|
|
@ -352,7 +394,7 @@ pub struct JsxTagAutoCloseSettingsContent {
|
|||
|
||||
/// The settings for inlay hints.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct InlayHintSettingsContent {
|
||||
/// Global switch to toggle hints on and off.
|
||||
///
|
||||
|
|
@ -434,7 +476,7 @@ impl InlayHintKind {
|
|||
|
||||
/// Controls how completions are processed for this language.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct CompletionSettingsContent {
|
||||
/// Controls how words are completed.
|
||||
|
|
@ -462,7 +504,7 @@ pub struct CompletionSettingsContent {
|
|||
pub lsp_insert_mode: Option<LspInsertMode>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum LspInsertMode {
|
||||
/// Replaces text before the cursor, using the `insert` range described in the LSP specification.
|
||||
|
|
@ -478,7 +520,7 @@ pub enum LspInsertMode {
|
|||
}
|
||||
|
||||
/// Controls how document's words are completed.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WordsCompletionMode {
|
||||
/// Always fetch document's words for completions along with LSP completions.
|
||||
|
|
@ -495,7 +537,7 @@ pub enum WordsCompletionMode {
|
|||
/// and configure default Prettier, used when no project-level Prettier installation is found.
|
||||
/// Prettier formatting is disabled by default.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct PrettierSettingsContent {
|
||||
/// Enables or disables formatting with Prettier for a given language.
|
||||
pub allowed: Option<bool>,
|
||||
|
|
@ -515,7 +557,7 @@ pub struct PrettierSettingsContent {
|
|||
}
|
||||
|
||||
/// Controls the behavior of formatting files when they are saved.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, MergeFrom)]
|
||||
pub enum FormatOnSave {
|
||||
/// Files should be formatted on save.
|
||||
On,
|
||||
|
|
@ -614,7 +656,7 @@ impl<'de> Deserialize<'de> for FormatOnSave {
|
|||
}
|
||||
|
||||
/// Controls which formatter should be used when formatting code.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, MergeFrom)]
|
||||
pub enum SelectedFormatter {
|
||||
/// Format files using Zed's Prettier integration (if applicable),
|
||||
/// or falling back to formatting via language server.
|
||||
|
|
@ -710,7 +752,7 @@ impl<'de> Deserialize<'de> for SelectedFormatter {
|
|||
}
|
||||
|
||||
/// Controls which formatters should be used when formatting code.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(untagged)]
|
||||
pub enum FormatterList {
|
||||
Single(Formatter),
|
||||
|
|
@ -733,7 +775,7 @@ impl AsRef<[Formatter]> for FormatterList {
|
|||
}
|
||||
|
||||
/// Controls which formatter should be used when formatting code. If there are multiple formatters, they are executed in the order of declaration.
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Formatter {
|
||||
/// Format code using the current language server.
|
||||
|
|
@ -754,7 +796,7 @@ pub enum Formatter {
|
|||
|
||||
/// The settings for indent guides.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct IndentGuideSettingsContent {
|
||||
/// Whether to display indent guides in the editor.
|
||||
///
|
||||
|
|
@ -780,7 +822,7 @@ pub struct IndentGuideSettingsContent {
|
|||
|
||||
/// The task settings for a particular language.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema, MergeFrom)]
|
||||
pub struct LanguageTaskSettingsContent {
|
||||
/// Extra task variables to set for a particular language.
|
||||
#[serde(default)]
|
||||
|
|
@ -796,37 +838,15 @@ pub struct LanguageTaskSettingsContent {
|
|||
pub prefer_lsp: Option<bool>,
|
||||
}
|
||||
|
||||
/// Map from language name to settings. Its `ParameterizedJsonSchema` allows only known language
|
||||
/// names in the keys.
|
||||
/// Map from language name to settings.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct LanguageToSettingsMap(pub HashMap<SharedString, LanguageSettingsContent>);
|
||||
|
||||
inventory::submit! {
|
||||
ParameterizedJsonSchema {
|
||||
add_and_get_ref: |generator, params, _cx| {
|
||||
let language_settings_content_ref = generator
|
||||
.subschema_for::<LanguageSettingsContent>()
|
||||
.to_value();
|
||||
replace_subschema::<LanguageToSettingsMap>(generator, || json_schema!({
|
||||
"type": "object",
|
||||
"properties": params
|
||||
.language_names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
(
|
||||
name.clone(),
|
||||
language_settings_content_ref.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>()
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines how indent guides are colored.
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum IndentGuideColoring {
|
||||
/// Do not render any lines for indent guides.
|
||||
|
|
@ -839,7 +859,9 @@ pub enum IndentGuideColoring {
|
|||
}
|
||||
|
||||
/// Determines how indent guide backgrounds are colored.
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum IndentGuideBackgroundColoring {
|
||||
/// Do not render any background for indent guides.
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ use collections::HashMap;
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct AllLanguageModelSettingsContent {
|
||||
pub anthropic: Option<AnthropicSettingsContent>,
|
||||
pub bedrock: Option<AmazonBedrockSettingsContent>,
|
||||
|
|
@ -25,14 +26,14 @@ pub struct AllLanguageModelSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct AnthropicSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<AnthropicAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct AnthropicAvailableModel {
|
||||
/// The model's name in the Anthropic API. e.g. claude-3-5-sonnet-latest, claude-3-opus-20240229, etc
|
||||
pub name: String,
|
||||
|
|
@ -53,7 +54,7 @@ pub struct AnthropicAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct AmazonBedrockSettingsContent {
|
||||
pub available_models: Option<Vec<BedrockAvailableModel>>,
|
||||
pub endpoint_url: Option<String>,
|
||||
|
|
@ -63,7 +64,7 @@ pub struct AmazonBedrockSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct BedrockAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -74,7 +75,7 @@ pub struct BedrockAvailableModel {
|
|||
pub mode: Option<ModelMode>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub enum BedrockAuthMethodContent {
|
||||
#[serde(rename = "named_profile")]
|
||||
NamedProfile,
|
||||
|
|
@ -86,14 +87,14 @@ pub enum BedrockAuthMethodContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct OllamaSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<OllamaAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct OllamaAvailableModel {
|
||||
/// The model name in the Ollama API (e.g. "llama3.2:latest")
|
||||
pub name: String,
|
||||
|
|
@ -111,7 +112,7 @@ pub struct OllamaAvailableModel {
|
|||
pub supports_thinking: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq, JsonSchema)]
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq, JsonSchema, MergeFrom)]
|
||||
#[serde(untagged)]
|
||||
pub enum KeepAlive {
|
||||
/// Keep model alive for N seconds
|
||||
|
|
@ -134,14 +135,14 @@ impl Default for KeepAlive {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct LmStudioSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<LmStudioAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct LmStudioAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -151,14 +152,14 @@ pub struct LmStudioAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct DeepseekSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<DeepseekAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct DeepseekAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -167,14 +168,14 @@ pub struct DeepseekAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct MistralSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<MistralAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct MistralAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -187,14 +188,14 @@ pub struct MistralAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct OpenAiSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<OpenAiAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct OpenAiAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -204,7 +205,7 @@ pub struct OpenAiAvailableModel {
|
|||
pub reasoning_effort: Option<OpenAiReasoningEffort>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, JsonSchema)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OpenAiReasoningEffort {
|
||||
Minimal,
|
||||
|
|
@ -214,14 +215,14 @@ pub enum OpenAiReasoningEffort {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct OpenAiCompatibleSettingsContent {
|
||||
pub api_url: String,
|
||||
pub available_models: Vec<OpenAiCompatibleAvailableModel>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct OpenAiCompatibleAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -233,7 +234,7 @@ pub struct OpenAiCompatibleAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct OpenAiCompatibleModelCapabilities {
|
||||
pub tools: bool,
|
||||
pub images: bool,
|
||||
|
|
@ -253,14 +254,14 @@ impl Default for OpenAiCompatibleModelCapabilities {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct VercelSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<VercelAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct VercelAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -270,14 +271,14 @@ pub struct VercelAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct GoogleSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<GoogleAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct GoogleAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -286,14 +287,14 @@ pub struct GoogleAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct XAiSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<XaiAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct XaiAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -303,13 +304,13 @@ pub struct XaiAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct ZedDotDevSettingsContent {
|
||||
pub available_models: Option<Vec<ZedDotDevAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct ZedDotDevAvailableModel {
|
||||
/// The provider of the language model.
|
||||
pub provider: ZedDotDevAvailableProvider,
|
||||
|
|
@ -336,7 +337,7 @@ pub struct ZedDotDevAvailableModel {
|
|||
pub mode: Option<ModelMode>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ZedDotDevAvailableProvider {
|
||||
Anthropic,
|
||||
|
|
@ -345,14 +346,14 @@ pub enum ZedDotDevAvailableProvider {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
pub struct OpenRouterSettingsContent {
|
||||
pub api_url: Option<String>,
|
||||
pub available_models: Option<Vec<OpenRouterAvailableModel>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct OpenRouterAvailableModel {
|
||||
pub name: String,
|
||||
pub display_name: Option<String>,
|
||||
|
|
@ -366,7 +367,7 @@ pub struct OpenRouterAvailableModel {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct OpenRouterProvider {
|
||||
order: Option<Vec<String>>,
|
||||
#[serde(default = "default_true")]
|
||||
|
|
@ -381,7 +382,7 @@ pub struct OpenRouterProvider {
|
|||
sort: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum DataCollection {
|
||||
Allow,
|
||||
|
|
@ -400,14 +401,16 @@ fn default_true() -> bool {
|
|||
|
||||
/// Configuration for caching language model messages.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct LanguageModelCacheConfiguration {
|
||||
pub max_cache_anchors: usize,
|
||||
pub should_speculate: bool,
|
||||
pub min_total_token: u64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum ModelMode {
|
||||
#[default]
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ use collections::{BTreeMap, HashMap};
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
use util::serde::default_true;
|
||||
|
||||
use crate::{AllLanguageSettingsContent, SlashCommandSettings};
|
||||
use crate::{AllLanguageSettingsContent, ExtendingVec, SlashCommandSettings};
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct ProjectSettingsContent {
|
||||
#[serde(flatten)]
|
||||
pub all_languages: AllLanguageSettingsContent,
|
||||
|
|
@ -43,11 +44,11 @@ pub struct ProjectSettingsContent {
|
|||
pub slash_commands: Option<SlashCommandSettings>,
|
||||
|
||||
/// The list of custom Git hosting providers.
|
||||
pub git_hosting_providers: Option<Vec<GitHostingProviderConfig>>,
|
||||
pub git_hosting_providers: Option<ExtendingVec<GitHostingProviderConfig>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct WorktreeSettingsContent {
|
||||
/// The displayed name of this project. If not set, the root directory name
|
||||
/// will be displayed.
|
||||
|
|
@ -81,11 +82,11 @@ pub struct WorktreeSettingsContent {
|
|||
|
||||
/// Treat the files matching these globs as `.env` files.
|
||||
/// Default: ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"]
|
||||
pub private_files: Option<Vec<String>>,
|
||||
pub private_files: Option<ExtendingVec<String>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct LspSettings {
|
||||
pub binary: Option<BinarySettings>,
|
||||
|
|
@ -112,7 +113,9 @@ impl Default for LspSettings {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)]
|
||||
#[derive(
|
||||
Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash,
|
||||
)]
|
||||
pub struct BinarySettings {
|
||||
pub path: Option<String>,
|
||||
pub arguments: Option<Vec<String>>,
|
||||
|
|
@ -121,7 +124,9 @@ pub struct BinarySettings {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)]
|
||||
#[derive(
|
||||
Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash,
|
||||
)]
|
||||
pub struct FetchSettings {
|
||||
// Whether to consider pre-releases for fetching
|
||||
pub pre_release: Option<bool>,
|
||||
|
|
@ -129,7 +134,7 @@ pub struct FetchSettings {
|
|||
|
||||
/// Common language server settings.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct GlobalLspSettingsContent {
|
||||
/// Whether to show the LSP servers button in the status bar.
|
||||
///
|
||||
|
|
@ -138,7 +143,7 @@ pub struct GlobalLspSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DapSettingsContent {
|
||||
pub binary: Option<String>,
|
||||
|
|
@ -147,7 +152,9 @@ pub struct DapSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Default, Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
pub struct SessionSettingsContent {
|
||||
/// Whether or not to restore unsaved buffers on restart.
|
||||
///
|
||||
|
|
@ -158,7 +165,7 @@ pub struct SessionSettingsContent {
|
|||
pub restore_unsaved_buffers: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom, Debug)]
|
||||
#[serde(tag = "source", rename_all = "snake_case")]
|
||||
pub enum ContextServerSettingsContent {
|
||||
Custom {
|
||||
|
|
@ -198,7 +205,7 @@ impl ContextServerSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
pub struct ContextServerCommand {
|
||||
#[serde(rename = "command")]
|
||||
pub path: PathBuf,
|
||||
|
|
@ -234,7 +241,7 @@ impl std::fmt::Debug for ContextServerCommand {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct GitSettings {
|
||||
/// Whether or not to show the git gutter.
|
||||
///
|
||||
|
|
@ -259,7 +266,7 @@ pub struct GitSettings {
|
|||
pub hunk_style: Option<GitHunkStyleSetting>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GitGutterSetting {
|
||||
/// Show git gutter in tracked files.
|
||||
|
|
@ -270,7 +277,7 @@ pub enum GitGutterSetting {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct InlineBlameSettings {
|
||||
/// Whether or not to show git blame data inline in
|
||||
|
|
@ -299,7 +306,7 @@ pub struct InlineBlameSettings {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct BranchPickerSettingsContent {
|
||||
/// Whether to show author name as part of the commit information.
|
||||
|
|
@ -308,7 +315,7 @@ pub struct BranchPickerSettingsContent {
|
|||
pub show_author_name: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GitHunkStyleSetting {
|
||||
/// Show unstaged hunks with a filled background and staged hunks hollow.
|
||||
|
|
@ -319,7 +326,7 @@ pub enum GitHunkStyleSetting {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct DiagnosticsSettingsContent {
|
||||
/// Whether to show the project diagnostics button in the status bar.
|
||||
pub button: Option<bool>,
|
||||
|
|
@ -335,7 +342,7 @@ pub struct DiagnosticsSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct LspPullDiagnosticsSettingsContent {
|
||||
/// Whether to pull for diagnostics or not.
|
||||
///
|
||||
|
|
@ -349,7 +356,7 @@ pub struct LspPullDiagnosticsSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, JsonSchema, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom, Eq)]
|
||||
pub struct InlineDiagnosticsSettingsContent {
|
||||
/// Whether or not to show inline diagnostics
|
||||
///
|
||||
|
|
@ -376,7 +383,7 @@ pub struct InlineDiagnosticsSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct NodeBinarySettings {
|
||||
/// The path to the Node binary.
|
||||
pub path: Option<String>,
|
||||
|
|
@ -386,7 +393,7 @@ pub struct NodeBinarySettings {
|
|||
pub ignore_system_version: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DirenvSettings {
|
||||
/// Load direnv configuration through a shell hook
|
||||
|
|
@ -397,7 +404,17 @@ pub enum DirenvSettings {
|
|||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema,
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
JsonSchema,
|
||||
MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DiagnosticSeverityContent {
|
||||
|
|
@ -412,7 +429,7 @@ pub enum DiagnosticSeverityContent {
|
|||
|
||||
/// A custom Git hosting provider.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct GitHostingProviderConfig {
|
||||
/// The type of the provider.
|
||||
///
|
||||
|
|
@ -426,7 +443,7 @@ pub struct GitHostingProviderConfig {
|
|||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GitHostingProviderKind {
|
||||
Github,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ use gpui::{AbsoluteLength, FontFeatures, SharedString, px};
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
|
||||
use crate::FontFamilyName;
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct TerminalSettingsContent {
|
||||
/// What shell to use when opening a terminal.
|
||||
///
|
||||
|
|
@ -127,7 +128,7 @@ pub struct TerminalSettingsContent {
|
|||
}
|
||||
|
||||
/// Shell configuration to open the terminal with.
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Shell {
|
||||
/// Use the system's default terminal configuration in /etc/passwd
|
||||
|
|
@ -146,7 +147,7 @@ pub enum Shell {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WorkingDirectory {
|
||||
/// Use the current file's project directory. Will Fallback to the
|
||||
|
|
@ -163,15 +164,15 @@ pub enum WorkingDirectory {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct ScrollbarSettingsContent {
|
||||
/// When to show the scrollbar in the terminal.
|
||||
///
|
||||
/// Default: inherits editor scrollbar settings
|
||||
pub show: Option<Option<ShowScrollbar>>,
|
||||
pub show: Option<ShowScrollbar>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TerminalLineHeight {
|
||||
/// Use a line height that's comfortable for reading, 1.618
|
||||
|
|
@ -198,7 +199,7 @@ impl TerminalLineHeight {
|
|||
/// When to show the scrollbar.
|
||||
///
|
||||
/// Default: auto
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowScrollbar {
|
||||
/// Show the scrollbar if there's important information or
|
||||
|
|
@ -212,7 +213,9 @@ pub enum ShowScrollbar {
|
|||
Never,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CursorShapeContent {
|
||||
/// Cursor is a block like `█`.
|
||||
|
|
@ -226,7 +229,7 @@ pub enum CursorShapeContent {
|
|||
Hollow,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TerminalBlink {
|
||||
/// Never blink the cursor, ignoring the terminal mode.
|
||||
|
|
@ -238,7 +241,7 @@ pub enum TerminalBlink {
|
|||
On,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AlternateScroll {
|
||||
On,
|
||||
|
|
@ -247,7 +250,7 @@ pub enum AlternateScroll {
|
|||
|
||||
// Toolbar related settings
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct TerminalToolbarContent {
|
||||
/// Whether to display the terminal title in breadcrumbs inside the terminal pane.
|
||||
/// Only shown if the terminal title is not empty.
|
||||
|
|
@ -259,7 +262,7 @@ pub struct TerminalToolbarContent {
|
|||
pub breadcrumbs: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum VenvSettings {
|
||||
#[default]
|
||||
|
|
@ -297,7 +300,7 @@ impl VenvSettings {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TerminalDockPosition {
|
||||
Left,
|
||||
|
|
@ -305,7 +308,7 @@ pub enum TerminalDockPosition {
|
|||
Right,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ActivateScript {
|
||||
#[default]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use schemars::{JsonSchema, JsonSchema_repr};
|
|||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_json::Value;
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use settings_macros::MergeFrom;
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde_with::skip_serializing_none;
|
||||
|
|
@ -11,7 +12,7 @@ use serde_with::skip_serializing_none;
|
|||
/// Settings for rendering text in UI and text buffers.
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct ThemeSettingsContent {
|
||||
/// The default font size for text in the UI.
|
||||
#[serde(default)]
|
||||
|
|
@ -53,7 +54,7 @@ pub struct ThemeSettingsContent {
|
|||
pub buffer_font_features: Option<FontFeatures>,
|
||||
/// The font size for the agent panel. Falls back to the UI font size if unset.
|
||||
#[serde(default)]
|
||||
pub agent_font_size: Option<Option<f32>>,
|
||||
pub agent_font_size: Option<f32>,
|
||||
/// The name of the Zed theme to use.
|
||||
#[serde(default)]
|
||||
pub theme: Option<ThemeSelection>,
|
||||
|
|
@ -93,7 +94,7 @@ fn default_font_fallbacks() -> Option<FontFallbacks> {
|
|||
}
|
||||
|
||||
/// Represents the selection of a theme, which can be either static or dynamic.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
pub enum ThemeSelection {
|
||||
/// A static theme selection, represented by a single theme name.
|
||||
|
|
@ -111,7 +112,7 @@ pub enum ThemeSelection {
|
|||
}
|
||||
|
||||
/// Represents the selection of an icon theme, which can be either static or dynamic.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
pub enum IconThemeSelection {
|
||||
/// A static icon theme selection, represented by a single icon theme name.
|
||||
|
|
@ -134,7 +135,9 @@ pub enum IconThemeSelection {
|
|||
/// `Light` and `Dark` will select their respective themes.
|
||||
///
|
||||
/// `System` will select the theme based on the system's appearance.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ThemeMode {
|
||||
/// Use the specified `light` theme.
|
||||
|
|
@ -163,6 +166,7 @@ pub enum ThemeMode {
|
|||
Serialize,
|
||||
Deserialize,
|
||||
JsonSchema,
|
||||
MergeFrom,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum UiDensity {
|
||||
|
|
@ -190,15 +194,14 @@ impl UiDensity {
|
|||
}
|
||||
}
|
||||
|
||||
/// Newtype for font family name. Its `ParameterizedJsonSchema` lists the font families known at
|
||||
/// runtime.
|
||||
/// Font family name.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(transparent)]
|
||||
pub struct FontFamilyName(pub Arc<str>);
|
||||
|
||||
/// The buffer's line height.
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BufferLineHeight {
|
||||
/// A less dense line height.
|
||||
|
|
@ -226,7 +229,7 @@ where
|
|||
|
||||
/// The content of a serialized theme.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct ThemeStyleContent {
|
||||
#[serde(default, rename = "background.appearance")]
|
||||
|
|
@ -249,31 +252,30 @@ pub struct ThemeStyleContent {
|
|||
pub syntax: IndexMap<String, HighlightStyleContent>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct AccentContent(pub Option<String>);
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
pub struct PlayerColorContent {
|
||||
pub cursor: Option<String>,
|
||||
pub background: Option<String>,
|
||||
pub selection: Option<String>,
|
||||
}
|
||||
|
||||
/// Newtype for a theme name. Its `ParameterizedJsonSchema` lists the theme names known at runtime.
|
||||
/// Theme name.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(transparent)]
|
||||
pub struct ThemeName(pub Arc<str>);
|
||||
|
||||
/// Newtype for a icon theme name. Its `ParameterizedJsonSchema` lists the icon theme names known at
|
||||
/// runtime.
|
||||
/// Icon Theme Name
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
#[serde(transparent)]
|
||||
pub struct IconThemeName(pub Arc<str>);
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct ThemeColorsContent {
|
||||
/// Border color. Used for most borders, is usually a high contrast color.
|
||||
|
|
@ -778,7 +780,7 @@ pub struct ThemeColorsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct HighlightStyleContent {
|
||||
pub color: Option<String>,
|
||||
|
|
@ -812,7 +814,7 @@ where
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct StatusColorsContent {
|
||||
/// Indicates some kind of conflict, like a file changed on disk while it was open, or
|
||||
|
|
@ -958,7 +960,7 @@ pub struct StatusColorsContent {
|
|||
}
|
||||
|
||||
/// The background appearance of the window.
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WindowBackgroundContent {
|
||||
Opaque,
|
||||
|
|
@ -976,7 +978,7 @@ impl Into<gpui::WindowBackgroundAppearance> for WindowBackgroundContent {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum FontStyleContent {
|
||||
Normal,
|
||||
|
|
@ -994,7 +996,9 @@ impl From<FontStyleContent> for FontStyle {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, JsonSchema_repr, PartialEq)]
|
||||
#[derive(
|
||||
Debug, Clone, Copy, Serialize_repr, Deserialize_repr, JsonSchema_repr, PartialEq, MergeFrom,
|
||||
)]
|
||||
#[repr(u16)]
|
||||
pub enum FontWeightContent {
|
||||
Thin = 100,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ use collections::HashMap;
|
|||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
use settings_macros::MergeFrom;
|
||||
|
||||
use crate::{DockPosition, DockSide, ScrollbarSettingsContent, ShowIndentGuides};
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct WorkspaceSettingsContent {
|
||||
/// Active pane styling settings.
|
||||
pub active_pane_modifiers: Option<ActivePanelModifiers>,
|
||||
|
|
@ -108,7 +109,7 @@ pub struct WorkspaceSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct ItemSettingsContent {
|
||||
/// Whether to show the Git file status on a tab item.
|
||||
///
|
||||
|
|
@ -138,7 +139,7 @@ pub struct ItemSettingsContent {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
pub struct PreviewTabsSettingsContent {
|
||||
/// Whether to show opened editors as preview tabs.
|
||||
/// Preview tabs do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic.
|
||||
|
|
@ -155,7 +156,7 @@ pub struct PreviewTabsSettingsContent {
|
|||
pub enable_preview_from_code_navigation: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ClosePosition {
|
||||
Left,
|
||||
|
|
@ -163,7 +164,7 @@ pub enum ClosePosition {
|
|||
Right,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ShowCloseButton {
|
||||
Always,
|
||||
|
|
@ -172,7 +173,9 @@ pub enum ShowCloseButton {
|
|||
Hidden,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowDiagnostics {
|
||||
#[default]
|
||||
|
|
@ -181,7 +184,7 @@ pub enum ShowDiagnostics {
|
|||
All,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ActivateOnClose {
|
||||
#[default]
|
||||
|
|
@ -191,7 +194,7 @@ pub enum ActivateOnClose {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ActivePanelModifiers {
|
||||
/// Size of the border surrounding the active pane.
|
||||
|
|
@ -209,7 +212,7 @@ pub struct ActivePanelModifiers {
|
|||
pub inactive_opacity: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BottomDockLayout {
|
||||
/// Contained between the left and right docks
|
||||
|
|
@ -223,7 +226,7 @@ pub enum BottomDockLayout {
|
|||
RightAligned,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CloseWindowWhenNoItems {
|
||||
/// Match platform conventions by default, so "on" on macOS and "off" everywhere else
|
||||
|
|
@ -245,7 +248,9 @@ impl CloseWindowWhenNoItems {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(
|
||||
Copy, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RestoreOnStartupBehavior {
|
||||
/// Always start with an empty editor
|
||||
|
|
@ -258,7 +263,7 @@ pub enum RestoreOnStartupBehavior {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug, PartialEq)]
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug, PartialEq)]
|
||||
pub struct TabBarSettingsContent {
|
||||
/// Whether or not to show the tab bar in the editor.
|
||||
///
|
||||
|
|
@ -274,7 +279,7 @@ pub struct TabBarSettingsContent {
|
|||
pub show_tab_bar_buttons: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AutosaveSetting {
|
||||
/// Disable autosave.
|
||||
|
|
@ -298,14 +303,14 @@ impl AutosaveSetting {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PaneSplitDirectionHorizontal {
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PaneSplitDirectionVertical {
|
||||
Left,
|
||||
|
|
@ -313,7 +318,7 @@ pub enum PaneSplitDirectionVertical {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct CenteredLayoutSettings {
|
||||
/// The relative width of the left padding of the central pane from the
|
||||
|
|
@ -328,7 +333,7 @@ pub struct CenteredLayoutSettings {
|
|||
pub right_padding: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Debug)]
|
||||
#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OnLastWindowClosed {
|
||||
/// Match platform conventions by default, so don't quit on macOS, and quit on other platforms
|
||||
|
|
@ -348,7 +353,7 @@ impl OnLastWindowClosed {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
|
||||
pub struct ProjectPanelSettingsContent {
|
||||
/// Whether to show the project panel button in the status bar.
|
||||
///
|
||||
|
|
@ -423,7 +428,9 @@ pub struct ProjectPanelSettingsContent {
|
|||
pub drag_and_drop: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ProjectPanelEntrySpacing {
|
||||
/// Comfortable spacing of entries.
|
||||
|
|
@ -434,7 +441,7 @@ pub enum ProjectPanelEntrySpacing {
|
|||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
|
||||
pub struct ProjectPanelIndentGuidesSettings {
|
||||
pub show: Option<ShowIndentGuides>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use gpui::App;
|
||||
use gpui::SharedString;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use serde_json::Value;
|
||||
use std::{ops::Range, sync::LazyLock};
|
||||
|
|
@ -10,16 +10,10 @@ use util::RangeExt;
|
|||
pub struct SettingsJsonSchemaParams<'a> {
|
||||
pub language_names: &'a [String],
|
||||
pub font_names: &'a [String],
|
||||
pub theme_names: &'a [SharedString],
|
||||
pub icon_theme_names: &'a [SharedString],
|
||||
}
|
||||
|
||||
/// Value registered which specifies JSON schemas that are generated at runtime.
|
||||
pub struct ParameterizedJsonSchema {
|
||||
pub add_and_get_ref:
|
||||
fn(&mut schemars::SchemaGenerator, &SettingsJsonSchemaParams, &App) -> schemars::Schema,
|
||||
}
|
||||
|
||||
inventory::collect!(ParameterizedJsonSchema);
|
||||
|
||||
pub fn update_value_in_json_text<'a>(
|
||||
text: &mut String,
|
||||
key_path: &mut Vec<&'a str>,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use futures::{
|
|||
use gpui::{App, AsyncApp, BorrowAppContext, Global, SharedString, Task, UpdateGlobal};
|
||||
|
||||
use paths::{EDITORCONFIG_NAME, local_settings_file_relative_path, task_file_name};
|
||||
use schemars::JsonSchema;
|
||||
use schemars::{JsonSchema, json_schema};
|
||||
use serde_json::Value;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
|
@ -18,16 +18,23 @@ use std::{
|
|||
fmt::Debug,
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
str::{self, FromStr},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::{ResultExt as _, schemars::DefaultDenyUnknownFields};
|
||||
use util::{
|
||||
ResultExt as _,
|
||||
schemars::{DefaultDenyUnknownFields, replace_subschema},
|
||||
};
|
||||
|
||||
pub type EditorconfigProperties = ec4rs::Properties;
|
||||
|
||||
use crate::{
|
||||
ActiveSettingsProfileName, ParameterizedJsonSchema, SettingsJsonSchemaParams, SettingsUiEntry,
|
||||
VsCodeSettings, WorktreeId, parse_json_with_comments, replace_value_in_json_text,
|
||||
ActiveSettingsProfileName, FontFamilyName, IconThemeName, LanguageSettingsContent,
|
||||
LanguageToSettingsMap, SettingsJsonSchemaParams, SettingsUiEntry, ThemeName, VsCodeSettings,
|
||||
WorktreeId,
|
||||
merge_from::MergeFrom,
|
||||
parse_json_with_comments, replace_value_in_json_text,
|
||||
settings_content::{
|
||||
ExtensionsSettingsContent, ProjectSettingsContent, ServerSettingsContent, SettingsContent,
|
||||
UserSettingsContent,
|
||||
|
|
@ -58,15 +65,10 @@ pub trait Settings: 'static + Send + Sync + Sized {
|
|||
const PRESERVED_KEYS: Option<&'static [&'static str]> = None;
|
||||
|
||||
/// Read the value from default.json.
|
||||
///
|
||||
/// This function *should* panic if default values are missing,
|
||||
/// and you should add a default to default.json for documentation.
|
||||
fn from_defaults(content: &SettingsContent, cx: &mut App) -> Self;
|
||||
|
||||
/// Update the value based on the content from the current file.
|
||||
///
|
||||
/// This function *should not* panic if there are problems, as the
|
||||
/// content of user-provided settings files may be incomplete or invalid.
|
||||
fn refine(&mut self, content: &SettingsContent, cx: &mut App);
|
||||
fn from_settings(content: &SettingsContent, cx: &mut App) -> Self;
|
||||
|
||||
fn missing_default() -> anyhow::Error {
|
||||
anyhow::anyhow!("missing default for: {}", std::any::type_name::<Self>())
|
||||
|
|
@ -140,12 +142,15 @@ pub struct SettingsLocation<'a> {
|
|||
/// A set of strongly-typed setting values defined via multiple config files.
|
||||
pub struct SettingsStore {
|
||||
setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
|
||||
default_settings: Box<SettingsContent>,
|
||||
default_settings: Rc<SettingsContent>,
|
||||
user_settings: Option<UserSettingsContent>,
|
||||
global_settings: Option<Box<SettingsContent>>,
|
||||
|
||||
extension_settings: Option<Box<SettingsContent>>,
|
||||
server_settings: Option<Box<SettingsContent>>,
|
||||
|
||||
merged_settings: Rc<SettingsContent>,
|
||||
|
||||
local_settings: BTreeMap<(WorktreeId, Arc<Path>), SettingsContent>,
|
||||
raw_editorconfig_settings: BTreeMap<(WorktreeId, Arc<Path>), (String, Option<Editorconfig>)>,
|
||||
|
||||
|
|
@ -193,8 +198,7 @@ struct SettingValue<T> {
|
|||
trait AnySettingValue: 'static + Send + Sync {
|
||||
fn setting_type_name(&self) -> &'static str;
|
||||
|
||||
fn from_default(&self, s: &SettingsContent, cx: &mut App) -> Box<dyn Any>;
|
||||
fn refine(&self, value: &mut dyn Any, s: &[&SettingsContent], cx: &mut App);
|
||||
fn from_settings(&self, s: &SettingsContent, cx: &mut App) -> Box<dyn Any>;
|
||||
|
||||
fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
|
||||
fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)>;
|
||||
|
|
@ -210,14 +214,17 @@ trait AnySettingValue: 'static + Send + Sync {
|
|||
impl SettingsStore {
|
||||
pub fn new(cx: &App, default_settings: &str) -> Self {
|
||||
let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();
|
||||
let default_settings = parse_json_with_comments(default_settings).unwrap();
|
||||
let default_settings: Rc<SettingsContent> =
|
||||
parse_json_with_comments(default_settings).unwrap();
|
||||
Self {
|
||||
setting_values: Default::default(),
|
||||
default_settings,
|
||||
default_settings: default_settings.clone(),
|
||||
global_settings: None,
|
||||
server_settings: None,
|
||||
user_settings: None,
|
||||
extension_settings: None,
|
||||
|
||||
merged_settings: default_settings,
|
||||
local_settings: BTreeMap::default(),
|
||||
raw_editorconfig_settings: BTreeMap::default(),
|
||||
setting_file_updates_tx,
|
||||
|
|
@ -257,38 +264,7 @@ impl SettingsStore {
|
|||
global_value: None,
|
||||
local_values: Vec::new(),
|
||||
}));
|
||||
|
||||
let mut refinements = Vec::default();
|
||||
|
||||
if let Some(extension_settings) = self.extension_settings.as_deref() {
|
||||
refinements.push(extension_settings)
|
||||
}
|
||||
|
||||
if let Some(global_settings) = self.global_settings.as_deref() {
|
||||
refinements.push(global_settings)
|
||||
}
|
||||
|
||||
if let Some(user_settings) = self.user_settings.as_ref() {
|
||||
refinements.push(&user_settings.content);
|
||||
if let Some(release_channel) = user_settings.for_release_channel() {
|
||||
refinements.push(release_channel)
|
||||
}
|
||||
if let Some(os) = user_settings.for_os() {
|
||||
refinements.push(os)
|
||||
}
|
||||
if let Some(profile) = user_settings.for_profile(cx) {
|
||||
refinements.push(profile)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(server_settings) = self.server_settings.as_ref() {
|
||||
refinements.push(server_settings)
|
||||
}
|
||||
let mut value = T::from_defaults(&self.default_settings, cx);
|
||||
for refinement in refinements {
|
||||
value.refine(refinement, cx)
|
||||
}
|
||||
|
||||
let value = T::from_settings(&self.merged_settings, cx);
|
||||
setting_value.set_global_value(Box::new(value));
|
||||
}
|
||||
|
||||
|
|
@ -831,19 +807,56 @@ impl SettingsStore {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn json_schema(&self, schema_params: &SettingsJsonSchemaParams, cx: &App) -> Value {
|
||||
pub fn json_schema(&self, params: &SettingsJsonSchemaParams) -> Value {
|
||||
let mut generator = schemars::generate::SchemaSettings::draft2019_09()
|
||||
.with_transform(DefaultDenyUnknownFields)
|
||||
.into_generator();
|
||||
|
||||
let schema = UserSettingsContent::json_schema(&mut generator);
|
||||
UserSettingsContent::json_schema(&mut generator);
|
||||
|
||||
// add schemas which are determined at runtime
|
||||
for parameterized_json_schema in inventory::iter::<ParameterizedJsonSchema>() {
|
||||
(parameterized_json_schema.add_and_get_ref)(&mut generator, schema_params, cx);
|
||||
}
|
||||
let language_settings_content_ref = generator
|
||||
.subschema_for::<LanguageSettingsContent>()
|
||||
.to_value();
|
||||
replace_subschema::<LanguageToSettingsMap>(&mut generator, || {
|
||||
json_schema!({
|
||||
"type": "object",
|
||||
"properties": params
|
||||
.language_names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
(
|
||||
name.clone(),
|
||||
language_settings_content_ref.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>()
|
||||
})
|
||||
});
|
||||
|
||||
schema.to_value()
|
||||
replace_subschema::<FontFamilyName>(&mut generator, || {
|
||||
json_schema!({
|
||||
"type": "string",
|
||||
"enum": params.font_names,
|
||||
})
|
||||
});
|
||||
|
||||
replace_subschema::<ThemeName>(&mut generator, || {
|
||||
json_schema!({
|
||||
"type": "string",
|
||||
"enum": params.theme_names,
|
||||
})
|
||||
});
|
||||
|
||||
replace_subschema::<IconThemeName>(&mut generator, || {
|
||||
json_schema!({
|
||||
"type": "string",
|
||||
"enum": params.icon_theme_names,
|
||||
})
|
||||
});
|
||||
|
||||
generator
|
||||
.root_schema_for::<UserSettingsContent>()
|
||||
.to_value()
|
||||
}
|
||||
|
||||
fn recompute_values(
|
||||
|
|
@ -852,74 +865,62 @@ impl SettingsStore {
|
|||
cx: &mut App,
|
||||
) -> std::result::Result<(), InvalidSettingsError> {
|
||||
// Reload the global and local values for every setting.
|
||||
let mut project_settings_stack = Vec::<&SettingsContent>::new();
|
||||
let mut project_settings_stack = Vec::<SettingsContent>::new();
|
||||
let mut paths_stack = Vec::<Option<(WorktreeId, &Path)>>::new();
|
||||
|
||||
let mut refinements = Vec::default();
|
||||
|
||||
if let Some(extension_settings) = self.extension_settings.as_deref() {
|
||||
refinements.push(extension_settings)
|
||||
}
|
||||
|
||||
if let Some(global_settings) = self.global_settings.as_deref() {
|
||||
refinements.push(global_settings)
|
||||
}
|
||||
|
||||
if let Some(user_settings) = self.user_settings.as_ref() {
|
||||
refinements.push(&user_settings.content);
|
||||
if let Some(release_channel) = user_settings.for_release_channel() {
|
||||
refinements.push(release_channel)
|
||||
if changed_local_path.is_none() {
|
||||
let mut merged = self.default_settings.as_ref().clone();
|
||||
merged.merge_from(self.extension_settings.as_deref());
|
||||
merged.merge_from(self.global_settings.as_deref());
|
||||
if let Some(user_settings) = self.user_settings.as_ref() {
|
||||
merged.merge_from(Some(&user_settings.content));
|
||||
merged.merge_from(user_settings.for_release_channel());
|
||||
merged.merge_from(user_settings.for_os());
|
||||
merged.merge_from(user_settings.for_profile(cx));
|
||||
}
|
||||
if let Some(os) = user_settings.for_os() {
|
||||
refinements.push(os)
|
||||
}
|
||||
if let Some(profile) = user_settings.for_profile(cx) {
|
||||
refinements.push(profile)
|
||||
}
|
||||
}
|
||||
merged.merge_from(self.server_settings.as_deref());
|
||||
self.merged_settings = Rc::new(merged);
|
||||
|
||||
if let Some(server_settings) = self.server_settings.as_ref() {
|
||||
refinements.push(server_settings)
|
||||
}
|
||||
|
||||
for setting_value in self.setting_values.values_mut() {
|
||||
// If the global settings file changed, reload the global value for the field.
|
||||
if changed_local_path.is_none() {
|
||||
let mut value = setting_value.from_default(&self.default_settings, cx);
|
||||
setting_value.refine(value.as_mut(), &refinements, cx);
|
||||
for setting_value in self.setting_values.values_mut() {
|
||||
let value = setting_value.from_settings(&self.merged_settings, cx);
|
||||
setting_value.set_global_value(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Reload the local values for the setting.
|
||||
paths_stack.clear();
|
||||
project_settings_stack.clear();
|
||||
for ((root_id, directory_path), local_settings) in &self.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
|
||||
&& (root_id != prev_root_id || !directory_path.starts_with(prev_path))
|
||||
{
|
||||
paths_stack.pop();
|
||||
project_settings_stack.pop();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
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.is_some_and(|(changed_root_id, changed_local_path)| {
|
||||
*root_id != changed_root_id || !directory_path.starts_with(changed_local_path)
|
||||
}) {
|
||||
for ((root_id, directory_path), local_settings) in &self.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
|
||||
&& (root_id != prev_root_id || !directory_path.starts_with(prev_path))
|
||||
{
|
||||
paths_stack.pop();
|
||||
project_settings_stack.pop();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
let mut value = setting_value.from_default(&self.default_settings, cx);
|
||||
setting_value.refine(value.as_mut(), &refinements, cx);
|
||||
setting_value.refine(value.as_mut(), &project_settings_stack, cx);
|
||||
paths_stack.push(Some((*root_id, directory_path.as_ref())));
|
||||
let mut merged_local_settings = if let Some(deepest) = project_settings_stack.last() {
|
||||
(*deepest).clone()
|
||||
} else {
|
||||
self.merged_settings.as_ref().clone()
|
||||
};
|
||||
merged_local_settings.merge_from(Some(local_settings));
|
||||
|
||||
project_settings_stack.push(merged_local_settings);
|
||||
|
||||
// If a local settings file changed, then avoid recomputing local
|
||||
// settings for any path outside of that directory.
|
||||
if changed_local_path.is_some_and(|(changed_root_id, changed_local_path)| {
|
||||
*root_id != changed_root_id || !directory_path.starts_with(changed_local_path)
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for setting_value in self.setting_values.values_mut() {
|
||||
let value =
|
||||
setting_value.from_settings(&project_settings_stack.last().unwrap(), cx);
|
||||
setting_value.set_local_value(*root_id, directory_path.clone(), value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1001,15 +1002,8 @@ impl Debug for SettingsStore {
|
|||
}
|
||||
|
||||
impl<T: Settings> AnySettingValue for SettingValue<T> {
|
||||
fn from_default(&self, s: &SettingsContent, cx: &mut App) -> Box<dyn Any> {
|
||||
Box::new(T::from_defaults(s, cx)) as _
|
||||
}
|
||||
|
||||
fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent], cx: &mut App) {
|
||||
let value = value.downcast_mut::<T>().unwrap();
|
||||
for refinement in refinements {
|
||||
value.refine(refinement, cx)
|
||||
}
|
||||
fn from_settings(&self, s: &SettingsContent, cx: &mut App) -> Box<dyn Any> {
|
||||
Box::new(T::from_settings(s, cx)) as _
|
||||
}
|
||||
|
||||
fn setting_type_name(&self) -> &'static str {
|
||||
|
|
@ -1072,7 +1066,6 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
use unindent::Unindent;
|
||||
use util::MergeFrom;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct AutoUpdateSetting {
|
||||
|
|
@ -1080,19 +1073,11 @@ mod tests {
|
|||
}
|
||||
|
||||
impl Settings for AutoUpdateSetting {
|
||||
fn from_defaults(content: &SettingsContent, _: &mut App) -> Self {
|
||||
fn from_settings(content: &SettingsContent, _: &mut App) -> Self {
|
||||
AutoUpdateSetting {
|
||||
auto_update: content.auto_update.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _: &mut App) {
|
||||
if let Some(auto_update) = content.auto_update {
|
||||
self.auto_update = auto_update;
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(_: &VsCodeSettings, _: &mut SettingsContent) {}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
|
@ -1102,7 +1087,7 @@ mod tests {
|
|||
}
|
||||
|
||||
impl Settings for TitleBarSettings {
|
||||
fn from_defaults(content: &SettingsContent, _: &mut App) -> Self {
|
||||
fn from_settings(content: &SettingsContent, _: &mut App) -> Self {
|
||||
let content = content.title_bar.clone().unwrap();
|
||||
TitleBarSettings {
|
||||
show: content.show.unwrap(),
|
||||
|
|
@ -1110,13 +1095,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _: &mut App) {
|
||||
let Some(content) = content.title_bar.as_ref() else {
|
||||
return;
|
||||
};
|
||||
self.show.merge_from(&content.show)
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &VsCodeSettings, content: &mut SettingsContent) {
|
||||
let mut show = None;
|
||||
|
||||
|
|
@ -1138,7 +1116,7 @@ mod tests {
|
|||
}
|
||||
|
||||
impl Settings for DefaultLanguageSettings {
|
||||
fn from_defaults(content: &SettingsContent, _: &mut App) -> Self {
|
||||
fn from_settings(content: &SettingsContent, _: &mut App) -> Self {
|
||||
let content = &content.project.all_languages.defaults;
|
||||
DefaultLanguageSettings {
|
||||
tab_size: content.tab_size.unwrap(),
|
||||
|
|
@ -1146,13 +1124,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _: &mut App) {
|
||||
let content = &content.project.all_languages.defaults;
|
||||
self.tab_size.merge_from(&content.tab_size);
|
||||
self.preferred_line_length
|
||||
.merge_from(&content.preferred_line_length);
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &VsCodeSettings, content: &mut SettingsContent) {
|
||||
let content = &mut content.project.all_languages.defaults;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
[package]
|
||||
name = "settings_ui_macros"
|
||||
name = "settings_macros"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lib]
|
||||
path = "src/settings_ui_macros.rs"
|
||||
path = "src/settings_macros.rs"
|
||||
proc-macro = true
|
||||
|
||||
[lints]
|
||||
|
|
@ -16,8 +16,6 @@ workspace = true
|
|||
default = []
|
||||
|
||||
[dependencies]
|
||||
heck.workspace = true
|
||||
proc-macro2.workspace = true
|
||||
quote.workspace = true
|
||||
syn.workspace = true
|
||||
workspace-hack.workspace = true
|
||||
132
crates/settings_macros/src/settings_macros.rs
Normal file
132
crates/settings_macros/src/settings_macros.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
|
||||
|
||||
/// Derives the `MergeFrom` trait for a struct.
|
||||
///
|
||||
/// This macro automatically implements `MergeFrom` by calling `merge_from`
|
||||
/// on all fields in the struct. For `Option<T>` fields, it merges by taking
|
||||
/// the `other` value when `self` is `None`. For other types, it recursively
|
||||
/// calls `merge_from` on the field.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[derive(Clone, MergeFrom)]
|
||||
/// struct MySettings {
|
||||
/// field1: Option<String>,
|
||||
/// field2: SomeOtherSettings,
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(MergeFrom)]
|
||||
pub fn derive_merge_from(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = &input.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let merge_body = match &input.data {
|
||||
Data::Struct(data_struct) => match &data_struct.fields {
|
||||
Fields::Named(fields) => {
|
||||
let field_merges = fields.named.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
let field_type = &field.ty;
|
||||
|
||||
if is_option_type(field_type) {
|
||||
// For Option<T> fields, merge by taking the other value if self is None
|
||||
quote! {
|
||||
if let Some(other_value) = other.#field_name.as_ref() {
|
||||
if self.#field_name.is_none() {
|
||||
self.#field_name = Some(other_value.clone());
|
||||
} else if let Some(self_value) = self.#field_name.as_mut() {
|
||||
self_value.merge_from(Some(other_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For non-Option fields, recursively call merge_from
|
||||
quote! {
|
||||
self.#field_name.merge_from(Some(&other.#field_name));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
if let Some(other) = other {
|
||||
#(#field_merges)*
|
||||
}
|
||||
}
|
||||
}
|
||||
Fields::Unnamed(fields) => {
|
||||
let field_merges = fields.unnamed.iter().enumerate().map(|(i, field)| {
|
||||
let field_index = syn::Index::from(i);
|
||||
let field_type = &field.ty;
|
||||
|
||||
if is_option_type(field_type) {
|
||||
// For Option<T> fields, merge by taking the other value if self is None
|
||||
quote! {
|
||||
if let Some(other_value) = other.#field_index.as_ref() {
|
||||
if self.#field_index.is_none() {
|
||||
self.#field_index = Some(other_value.clone());
|
||||
} else if let Some(self_value) = self.#field_index.as_mut() {
|
||||
self_value.merge_from(Some(other_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For non-Option fields, recursively call merge_from
|
||||
quote! {
|
||||
self.#field_index.merge_from(Some(&other.#field_index));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
if let Some(other) = other {
|
||||
#(#field_merges)*
|
||||
}
|
||||
}
|
||||
}
|
||||
Fields::Unit => {
|
||||
quote! {
|
||||
// No fields to merge for unit structs
|
||||
}
|
||||
}
|
||||
},
|
||||
Data::Enum(_) => {
|
||||
quote! {
|
||||
if let Some(other) = other {
|
||||
*self = other.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
Data::Union(_) => {
|
||||
panic!("MergeFrom cannot be derived for unions");
|
||||
}
|
||||
};
|
||||
|
||||
let expanded = quote! {
|
||||
impl #impl_generics crate::merge_from::MergeFrom for #name #ty_generics #where_clause {
|
||||
fn merge_from(&mut self, other: ::core::option::Option<&Self>) {
|
||||
use crate::merge_from::MergeFrom as _;
|
||||
#merge_body
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
/// Check if a type is `Option<T>`
|
||||
fn is_option_type(ty: &Type) -> bool {
|
||||
match ty {
|
||||
Type::Path(type_path) => {
|
||||
if let Some(segment) = type_path.path.segments.last() {
|
||||
segment.ident == "Option"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,612 +0,0 @@
|
|||
use heck::{ToSnakeCase as _, ToTitleCase as _};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{ToTokens, quote};
|
||||
use syn::{Data, DeriveInput, LitStr, Token, parse_macro_input};
|
||||
|
||||
/// Derive macro for the `SettingsUi` marker trait.
|
||||
///
|
||||
/// This macro automatically implements the `SettingsUi` trait for the annotated type.
|
||||
/// The `SettingsUi` trait is a marker trait used to indicate that a type can be
|
||||
/// displayed in the settings UI.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use settings_ui_macros::SettingsUi;
|
||||
///
|
||||
/// #[derive(SettingsUi)]
|
||||
/// #[settings_ui(group = "Standard")]
|
||||
/// struct MySettings {
|
||||
/// enabled: bool,
|
||||
/// count: usize,
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(SettingsUi, attributes(settings_ui))]
|
||||
pub fn derive_settings_ui(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = &input.ident;
|
||||
|
||||
// Handle generic parameters if present
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let mut group_name = Option::<String>::None;
|
||||
let mut path_name = Option::<String>::None;
|
||||
|
||||
for attr in &input.attrs {
|
||||
if attr.path().is_ident("settings_ui") {
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("group") {
|
||||
if group_name.is_some() {
|
||||
return Err(meta.error("Only one 'group' path can be specified"));
|
||||
}
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit: LitStr = meta.input.parse()?;
|
||||
group_name = Some(lit.value());
|
||||
} else if meta.path.is_ident("path") {
|
||||
// todo(settings_ui) rely entirely on settings_key, remove path attribute
|
||||
if path_name.is_some() {
|
||||
return Err(meta.error("Only one 'path' can be specified, either with `path` in `settings_ui` or with `settings_key`"));
|
||||
}
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit: LitStr = meta.input.parse()?;
|
||||
path_name = Some(lit.value());
|
||||
} else if meta.path.is_ident("render") {
|
||||
// Just consume the tokens even if we don't use them here
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let _lit: LitStr = meta.input.parse()?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap_or_else(|e| panic!("in #[settings_ui] attribute: {}", e));
|
||||
} else if let Some(settings_key) = parse_setting_key_attr(attr) {
|
||||
// todo(settings_ui) either remove fallback key or handle it here
|
||||
if path_name.is_some() && settings_key.key.is_some() {
|
||||
panic!("Both 'path' and 'settings_key' are specified. Must specify only one");
|
||||
}
|
||||
path_name = settings_key.key;
|
||||
}
|
||||
}
|
||||
|
||||
let doc_str = parse_documentation_from_attrs(&input.attrs);
|
||||
|
||||
let ui_item_fn_body = generate_ui_item_body(group_name.as_ref(), &input);
|
||||
|
||||
// todo(settings_ui): make group name optional, repurpose group as tag indicating item is group, and have "title" tag for custom title
|
||||
let title = group_name.unwrap_or(input.ident.to_string().to_title_case());
|
||||
|
||||
let ui_entry_fn_body = map_ui_item_to_entry(
|
||||
path_name.as_deref(),
|
||||
&title,
|
||||
doc_str.as_deref(),
|
||||
quote! { Self },
|
||||
);
|
||||
|
||||
let expanded = quote! {
|
||||
impl #impl_generics settings::SettingsUi for #name #ty_generics #where_clause {
|
||||
fn settings_ui_item() -> settings::SettingsUiItem {
|
||||
#ui_item_fn_body
|
||||
}
|
||||
|
||||
fn settings_ui_entry() -> settings::SettingsUiEntry {
|
||||
#ui_entry_fn_body
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
proc_macro::TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
fn extract_type_from_option(ty: TokenStream) -> TokenStream {
|
||||
match option_inner_type(ty.clone()) {
|
||||
Some(inner_type) => inner_type,
|
||||
None => ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn option_inner_type(ty: TokenStream) -> Option<TokenStream> {
|
||||
let ty = syn::parse2::<syn::Type>(ty).ok()?;
|
||||
let syn::Type::Path(path) = ty else {
|
||||
return None;
|
||||
};
|
||||
let segment = path.path.segments.last()?;
|
||||
if segment.ident != "Option" {
|
||||
return None;
|
||||
}
|
||||
let syn::PathArguments::AngleBracketed(args) = &segment.arguments else {
|
||||
return None;
|
||||
};
|
||||
let arg = args.args.first()?;
|
||||
let syn::GenericArgument::Type(ty) = arg else {
|
||||
return None;
|
||||
};
|
||||
return Some(ty.to_token_stream());
|
||||
}
|
||||
|
||||
fn map_ui_item_to_entry(
|
||||
path: Option<&str>,
|
||||
title: &str,
|
||||
doc_str: Option<&str>,
|
||||
ty: TokenStream,
|
||||
) -> TokenStream {
|
||||
// todo(settings_ui): does quote! just work with options?
|
||||
let path = path.map_or_else(|| quote! {None}, |path| quote! {Some(#path)});
|
||||
let doc_str = doc_str.map_or_else(|| quote! {None}, |doc_str| quote! {Some(#doc_str)});
|
||||
let item = ui_item_from_type(ty);
|
||||
quote! {
|
||||
settings::SettingsUiEntry {
|
||||
title: #title,
|
||||
path: #path,
|
||||
item: #item,
|
||||
documentation: #doc_str,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ui_item_from_type(ty: TokenStream) -> TokenStream {
|
||||
let ty = extract_type_from_option(ty);
|
||||
return trait_method_call(ty, quote! {settings::SettingsUi}, quote! {settings_ui_item});
|
||||
}
|
||||
|
||||
fn trait_method_call(
|
||||
ty: TokenStream,
|
||||
trait_name: TokenStream,
|
||||
method_name: TokenStream,
|
||||
) -> TokenStream {
|
||||
// doing the <ty as settings::SettingsUi> makes the error message better:
|
||||
// -> "#ty Doesn't implement settings::SettingsUi" instead of "no item "settings_ui_item" for #ty"
|
||||
// and ensures safety against name conflicts
|
||||
//
|
||||
// todo(settings_ui): Turn `Vec<T>` into `Vec::<T>` here as well
|
||||
quote! {
|
||||
<#ty as #trait_name>::#method_name()
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_ui_item_body(group_name: Option<&String>, input: &syn::DeriveInput) -> TokenStream {
|
||||
match (group_name, &input.data) {
|
||||
(_, Data::Union(_)) => unimplemented!("Derive SettingsUi for Unions"),
|
||||
(None, Data::Struct(_)) => quote! {
|
||||
settings::SettingsUiItem::None
|
||||
},
|
||||
(Some(_), Data::Struct(data_struct)) => {
|
||||
let parent_serde_attrs = parse_serde_attributes(&input.attrs);
|
||||
item_group_from_fields(&data_struct.fields, &parent_serde_attrs)
|
||||
}
|
||||
(None, Data::Enum(data_enum)) => {
|
||||
let serde_attrs = parse_serde_attributes(&input.attrs);
|
||||
let render_as = parse_render_as(&input.attrs);
|
||||
let length = data_enum.variants.len();
|
||||
|
||||
let mut variants = Vec::with_capacity(length);
|
||||
let mut labels = Vec::with_capacity(length);
|
||||
|
||||
for variant in &data_enum.variants {
|
||||
// todo(settings_ui): Can #[serde(rename = )] be on enum variants?
|
||||
let ident = variant.ident.clone().to_string();
|
||||
let variant_name = serde_attrs.rename_all.apply(&ident);
|
||||
let title = variant_name.to_title_case();
|
||||
|
||||
variants.push(variant_name);
|
||||
labels.push(title);
|
||||
}
|
||||
|
||||
let is_not_union = data_enum.variants.iter().all(|v| v.fields.is_empty());
|
||||
if is_not_union {
|
||||
return match render_as {
|
||||
RenderAs::ToggleGroup if length > 6 => {
|
||||
panic!("Can't set toggle group with more than six entries");
|
||||
}
|
||||
RenderAs::ToggleGroup => {
|
||||
quote! {
|
||||
settings::SettingsUiItem::Single(settings::SettingsUiItemSingle::ToggleGroup{ variants: &[#(#variants),*], labels: &[#(#labels),*] })
|
||||
}
|
||||
}
|
||||
RenderAs::Default => {
|
||||
quote! {
|
||||
settings::SettingsUiItem::Single(settings::SettingsUiItemSingle::DropDown{ variants: &[#(#variants),*], labels: &[#(#labels),*] })
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
// else: Union!
|
||||
let enum_name = &input.ident;
|
||||
|
||||
let options = data_enum.variants.iter().map(|variant| {
|
||||
if variant.fields.is_empty() {
|
||||
return quote! {None};
|
||||
}
|
||||
let name = &variant.ident;
|
||||
let item = item_group_from_fields(&variant.fields, &serde_attrs);
|
||||
// todo(settings_ui): documentation
|
||||
return quote! {
|
||||
Some(settings::SettingsUiEntry {
|
||||
path: None,
|
||||
title: stringify!(#name),
|
||||
documentation: None,
|
||||
item: #item,
|
||||
})
|
||||
};
|
||||
});
|
||||
let defaults = data_enum.variants.iter().map(|variant| {
|
||||
let variant_name = &variant.ident;
|
||||
if variant.fields.is_empty() {
|
||||
quote! {
|
||||
serde_json::to_value(#enum_name::#variant_name).expect("Failed to serialize default value for #enum_name::#variant_name")
|
||||
}
|
||||
} else {
|
||||
let fields = variant.fields.iter().enumerate().map(|(index, field)| {
|
||||
let field_name = field.ident.as_ref().map_or_else(|| syn::Index::from(index).into_token_stream(), |ident| ident.to_token_stream());
|
||||
let field_type_is_option = option_inner_type(field.ty.to_token_stream()).is_some();
|
||||
let field_default = if field_type_is_option {
|
||||
quote! {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
};
|
||||
|
||||
quote!{
|
||||
#field_name: #field_default
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
serde_json::to_value(#enum_name::#variant_name {
|
||||
#(#fields),*
|
||||
}).expect("Failed to serialize default value for #enum_name::#variant_name")
|
||||
}
|
||||
}
|
||||
});
|
||||
// todo(settings_ui): Identify #[default] attr and use it for index, defaulting to 0
|
||||
let default_variant_index: usize = 0;
|
||||
let determine_option_fn = {
|
||||
let match_arms = data_enum
|
||||
.variants
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, variant)| {
|
||||
let variant_name = &variant.ident;
|
||||
quote! {
|
||||
Ok(#variant_name {..}) => #index
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
|value: &serde_json::Value, _cx: &gpui::App| -> usize {
|
||||
use #enum_name::*;
|
||||
match serde_json::from_value::<#enum_name>(value.clone()) {
|
||||
#(#match_arms),*,
|
||||
Err(_) => #default_variant_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// todo(settings_ui) should probably always use toggle group for unions, dropdown makes less sense
|
||||
return quote! {
|
||||
settings::SettingsUiItem::Union(settings::SettingsUiItemUnion {
|
||||
defaults: Box::new([#(#defaults),*]),
|
||||
labels: &[#(#labels),*],
|
||||
options: Box::new([#(#options),*]),
|
||||
determine_option: #determine_option_fn,
|
||||
})
|
||||
};
|
||||
// panic!("Unhandled");
|
||||
}
|
||||
// todo(settings_ui) discriminated unions
|
||||
(_, Data::Enum(_)) => quote! {
|
||||
settings::SettingsUiItem::None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn item_group_from_fields(fields: &syn::Fields, parent_serde_attrs: &SerdeOptions) -> TokenStream {
|
||||
let group_items = fields
|
||||
.iter()
|
||||
.filter(|field| {
|
||||
!field.attrs.iter().any(|attr| {
|
||||
let mut has_skip = false;
|
||||
if attr.path().is_ident("settings_ui") {
|
||||
let _ = attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("skip") {
|
||||
has_skip = true;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
has_skip
|
||||
})
|
||||
})
|
||||
.map(|field| {
|
||||
let field_serde_attrs = parse_serde_attributes(&field.attrs);
|
||||
let name = field.ident.as_ref().map(ToString::to_string);
|
||||
let title = name.as_ref().map_or_else(
|
||||
|| "todo(settings_ui): Titles for tuple fields".to_string(),
|
||||
|name| name.to_title_case(),
|
||||
);
|
||||
let doc_str = parse_documentation_from_attrs(&field.attrs);
|
||||
|
||||
(
|
||||
title,
|
||||
doc_str,
|
||||
name.filter(|_| !field_serde_attrs.flatten).map(|name| {
|
||||
parent_serde_attrs.apply_rename_to_field(&field_serde_attrs, &name)
|
||||
}),
|
||||
field.ty.to_token_stream(),
|
||||
)
|
||||
})
|
||||
// todo(settings_ui): Re-format field name as nice title, and support setting different title with attr
|
||||
.map(|(title, doc_str, path, ty)| {
|
||||
map_ui_item_to_entry(path.as_deref(), &title, doc_str.as_deref(), ty)
|
||||
});
|
||||
|
||||
quote! {
|
||||
settings::SettingsUiItem::Group(settings::SettingsUiItemGroup{ items: vec![#(#group_items),*] })
|
||||
}
|
||||
}
|
||||
|
||||
struct SerdeOptions {
|
||||
rename_all: SerdeRenameAll,
|
||||
rename: Option<String>,
|
||||
flatten: bool,
|
||||
untagged: bool,
|
||||
_alias: Option<String>, // todo(settings_ui)
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum SerdeRenameAll {
|
||||
Lowercase,
|
||||
SnakeCase,
|
||||
None,
|
||||
}
|
||||
|
||||
impl SerdeRenameAll {
|
||||
fn apply(&self, name: &str) -> String {
|
||||
match self {
|
||||
SerdeRenameAll::Lowercase => name.to_lowercase(),
|
||||
SerdeRenameAll::SnakeCase => name.to_snake_case(),
|
||||
SerdeRenameAll::None => name.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SerdeOptions {
|
||||
fn apply_rename_to_field(&self, field_options: &Self, name: &str) -> String {
|
||||
// field renames take precedence over struct rename all cases
|
||||
if let Some(rename) = &field_options.rename {
|
||||
return rename.clone();
|
||||
}
|
||||
return self.rename_all.apply(name);
|
||||
}
|
||||
}
|
||||
|
||||
enum RenderAs {
|
||||
ToggleGroup,
|
||||
Default,
|
||||
}
|
||||
|
||||
fn parse_render_as(attrs: &[syn::Attribute]) -> RenderAs {
|
||||
let mut render_as = RenderAs::Default;
|
||||
|
||||
for attr in attrs {
|
||||
if !attr.path().is_ident("settings_ui") {
|
||||
continue;
|
||||
}
|
||||
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("render") {
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit = meta.input.parse::<LitStr>()?.value();
|
||||
|
||||
if lit == "toggle_group" {
|
||||
render_as = RenderAs::ToggleGroup;
|
||||
} else {
|
||||
return Err(meta.error(format!("invalid `render` attribute: {}", lit)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
render_as
|
||||
}
|
||||
|
||||
fn parse_serde_attributes(attrs: &[syn::Attribute]) -> SerdeOptions {
|
||||
let mut options = SerdeOptions {
|
||||
rename_all: SerdeRenameAll::None,
|
||||
rename: None,
|
||||
flatten: false,
|
||||
untagged: false,
|
||||
_alias: None,
|
||||
};
|
||||
|
||||
for attr in attrs {
|
||||
if !attr.path().is_ident("serde") {
|
||||
continue;
|
||||
}
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("rename_all") {
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit = meta.input.parse::<LitStr>()?.value();
|
||||
|
||||
if options.rename_all != SerdeRenameAll::None {
|
||||
return Err(meta.error("duplicate `rename_all` attribute"));
|
||||
} else if lit == "lowercase" {
|
||||
options.rename_all = SerdeRenameAll::Lowercase;
|
||||
} else if lit == "snake_case" {
|
||||
options.rename_all = SerdeRenameAll::SnakeCase;
|
||||
} else {
|
||||
return Err(meta.error(format!("invalid `rename_all` attribute: {}", lit)));
|
||||
}
|
||||
// todo(settings_ui): Other options?
|
||||
} else if meta.path.is_ident("flatten") {
|
||||
options.flatten = true;
|
||||
} else if meta.path.is_ident("rename") {
|
||||
if options.rename.is_some() {
|
||||
return Err(meta.error("Can only have one rename attribute"));
|
||||
}
|
||||
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit = meta.input.parse::<LitStr>()?.value();
|
||||
options.rename = Some(lit);
|
||||
} else if meta.path.is_ident("untagged") {
|
||||
options.untagged = true;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
fn parse_documentation_from_attrs(attrs: &[syn::Attribute]) -> Option<String> {
|
||||
let mut doc_str = Option::<String>::None;
|
||||
for attr in attrs {
|
||||
if attr.path().is_ident("doc") {
|
||||
// /// ...
|
||||
// becomes
|
||||
// #[doc = "..."]
|
||||
use syn::{Expr::Lit, ExprLit, Lit::Str, Meta, MetaNameValue};
|
||||
if let Meta::NameValue(MetaNameValue {
|
||||
value:
|
||||
Lit(ExprLit {
|
||||
lit: Str(ref lit_str),
|
||||
..
|
||||
}),
|
||||
..
|
||||
}) = attr.meta
|
||||
{
|
||||
let doc = lit_str.value();
|
||||
let doc_str = doc_str.get_or_insert_default();
|
||||
doc_str.push_str(doc.trim());
|
||||
doc_str.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
return doc_str;
|
||||
}
|
||||
|
||||
struct SettingsKey {
|
||||
key: Option<String>,
|
||||
fallback_key: Option<String>,
|
||||
}
|
||||
|
||||
fn parse_setting_key_attr(attr: &syn::Attribute) -> Option<SettingsKey> {
|
||||
if !attr.path().is_ident("settings_key") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut settings_key = SettingsKey {
|
||||
key: None,
|
||||
fallback_key: None,
|
||||
};
|
||||
|
||||
let mut found_none = false;
|
||||
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("None") {
|
||||
found_none = true;
|
||||
} else if meta.path.is_ident("key") {
|
||||
if settings_key.key.is_some() {
|
||||
return Err(meta.error("Only one 'group' path can be specified"));
|
||||
}
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit: LitStr = meta.input.parse()?;
|
||||
settings_key.key = Some(lit.value());
|
||||
} else if meta.path.is_ident("fallback_key") {
|
||||
if found_none {
|
||||
return Err(meta.error("Cannot specify 'fallback_key' and 'None'"));
|
||||
}
|
||||
|
||||
if settings_key.fallback_key.is_some() {
|
||||
return Err(meta.error("Only one 'fallback_key' can be specified"));
|
||||
}
|
||||
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let lit: LitStr = meta.input.parse()?;
|
||||
settings_key.fallback_key = Some(lit.value());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap_or_else(|e| panic!("in #[settings_key] attribute: {}", e));
|
||||
|
||||
if found_none && settings_key.fallback_key.is_some() {
|
||||
panic!("in #[settings_key] attribute: Cannot specify 'None' and 'fallback_key'");
|
||||
}
|
||||
if found_none && settings_key.key.is_some() {
|
||||
panic!("in #[settings_key] attribute: Cannot specify 'None' and 'key'");
|
||||
}
|
||||
if !found_none && settings_key.key.is_none() {
|
||||
panic!("in #[settings_key] attribute: 'key' must be specified");
|
||||
}
|
||||
|
||||
return Some(settings_key);
|
||||
}
|
||||
|
||||
#[proc_macro_derive(SettingsKey, attributes(settings_key))]
|
||||
pub fn derive_settings_key(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = &input.ident;
|
||||
|
||||
// Handle generic parameters if present
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
let mut settings_key = Option::<SettingsKey>::None;
|
||||
|
||||
for attr in &input.attrs {
|
||||
let parsed_settings_key = parse_setting_key_attr(attr);
|
||||
if parsed_settings_key.is_some() && settings_key.is_some() {
|
||||
panic!("Duplicate #[settings_key] attribute");
|
||||
}
|
||||
settings_key = settings_key.or(parsed_settings_key);
|
||||
}
|
||||
|
||||
let Some(SettingsKey { key, fallback_key }) = settings_key else {
|
||||
panic!("Missing #[settings_key] attribute");
|
||||
};
|
||||
|
||||
let key = key.map_or_else(|| quote! {None}, |key| quote! {Some(#key)});
|
||||
let fallback_key = fallback_key.map_or_else(
|
||||
|| quote! {None},
|
||||
|fallback_key| quote! {Some(#fallback_key)},
|
||||
);
|
||||
|
||||
let expanded = quote! {
|
||||
impl #impl_generics settings::SettingsKey for #name #ty_generics #where_clause {
|
||||
const KEY: Option<&'static str> = #key;
|
||||
|
||||
const FALLBACK_KEY: Option<&'static str> = #fallback_key;
|
||||
};
|
||||
};
|
||||
|
||||
proc_macro::TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use syn::{Attribute, parse_quote};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_extract_key() {
|
||||
let input: Attribute = parse_quote!(
|
||||
#[settings_key(key = "my_key")]
|
||||
);
|
||||
let settings_key = parse_setting_key_attr(&input).unwrap();
|
||||
assert_eq!(settings_key.key, Some("my_key".to_string()));
|
||||
assert_eq!(settings_key.fallback_key, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_key() {
|
||||
let input: Attribute = parse_quote!(
|
||||
#[settings_key(None)]
|
||||
);
|
||||
let settings_key = parse_setting_key_attr(&input).unwrap();
|
||||
assert_eq!(settings_key.key, None);
|
||||
assert_eq!(settings_key.fallback_key, None);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ use settings::{
|
|||
};
|
||||
use task::Shell;
|
||||
use theme::FontFamilyName;
|
||||
use util::MergeFrom;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
pub struct Toolbar {
|
||||
|
|
@ -73,7 +72,7 @@ fn settings_shell_to_task_shell(shell: settings::Shell) -> Shell {
|
|||
}
|
||||
|
||||
impl settings::Settings for TerminalSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let content = content.terminal.clone().unwrap();
|
||||
TerminalSettings {
|
||||
shell: settings_shell_to_task_shell(content.shell.unwrap()),
|
||||
|
|
@ -108,80 +107,12 @@ impl settings::Settings for TerminalSettings {
|
|||
breadcrumbs: content.toolbar.unwrap().breadcrumbs.unwrap(),
|
||||
},
|
||||
scrollbar: ScrollbarSettings {
|
||||
show: content.scrollbar.unwrap().show.flatten(),
|
||||
show: content.scrollbar.unwrap().show,
|
||||
},
|
||||
minimum_contrast: content.minimum_contrast.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(content) = &content.terminal else {
|
||||
return;
|
||||
};
|
||||
self.shell
|
||||
.merge_from(&content.shell.clone().map(settings_shell_to_task_shell));
|
||||
self.working_directory
|
||||
.merge_from(&content.working_directory);
|
||||
if let Some(font_size) = content.font_size.map(px) {
|
||||
self.font_size = Some(font_size)
|
||||
}
|
||||
if let Some(font_family) = content.font_family.clone() {
|
||||
self.font_family = Some(font_family);
|
||||
}
|
||||
if let Some(fallbacks) = content.font_fallbacks.clone() {
|
||||
self.font_fallbacks = Some(FontFallbacks::from_fonts(
|
||||
fallbacks
|
||||
.into_iter()
|
||||
.map(|family| family.0.to_string())
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
if let Some(font_features) = content.font_features.clone() {
|
||||
self.font_features = Some(font_features)
|
||||
}
|
||||
if let Some(font_weight) = content.font_weight {
|
||||
self.font_weight = Some(FontWeight(font_weight));
|
||||
}
|
||||
self.line_height.merge_from(&content.line_height);
|
||||
if let Some(env) = &content.env {
|
||||
for (key, value) in env {
|
||||
self.env.insert(key.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
if let Some(cursor_shape) = content.cursor_shape {
|
||||
self.cursor_shape = Some(cursor_shape.into())
|
||||
}
|
||||
self.blinking.merge_from(&content.blinking);
|
||||
self.alternate_scroll.merge_from(&content.alternate_scroll);
|
||||
self.option_as_meta.merge_from(&content.option_as_meta);
|
||||
self.copy_on_select.merge_from(&content.copy_on_select);
|
||||
self.keep_selection_on_copy
|
||||
.merge_from(&content.keep_selection_on_copy);
|
||||
self.button.merge_from(&content.button);
|
||||
self.dock.merge_from(&content.dock);
|
||||
self.default_width
|
||||
.merge_from(&content.default_width.map(px));
|
||||
self.default_height
|
||||
.merge_from(&content.default_height.map(px));
|
||||
self.detect_venv.merge_from(&content.detect_venv);
|
||||
if let Some(max_scroll_history_lines) = content.max_scroll_history_lines {
|
||||
self.max_scroll_history_lines = Some(max_scroll_history_lines)
|
||||
}
|
||||
self.toolbar.breadcrumbs.merge_from(
|
||||
&content
|
||||
.toolbar
|
||||
.as_ref()
|
||||
.and_then(|toolbar| toolbar.breadcrumbs),
|
||||
);
|
||||
self.scrollbar.show.merge_from(
|
||||
&content
|
||||
.scrollbar
|
||||
.as_ref()
|
||||
.and_then(|scrollbar| scrollbar.show),
|
||||
);
|
||||
self.minimum_contrast.merge_from(&content.minimum_contrast);
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, content: &mut SettingsContent) {
|
||||
let mut default = TerminalSettingsContent::default();
|
||||
let current = content.terminal.as_mut().unwrap_or(&mut default);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ fs.workspace = true
|
|||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
indexmap.workspace = true
|
||||
inventory.workspace = true
|
||||
log.workspace = true
|
||||
palette = { workspace = true, default-features = false, features = ["std"] }
|
||||
parking_lot.workspace = true
|
||||
|
|
|
|||
|
|
@ -7,17 +7,16 @@ use crate::{
|
|||
use collections::HashMap;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use gpui::{
|
||||
App, Context, Font, FontFallbacks, FontStyle, FontWeight, Global, Pixels, SharedString,
|
||||
Subscription, Window, px,
|
||||
App, Context, Font, FontFallbacks, FontStyle, FontWeight, Global, Pixels, Subscription, Window,
|
||||
px,
|
||||
};
|
||||
use refineable::Refineable;
|
||||
use schemars::{JsonSchema, json_schema};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use settings::{FontFamilyName, IconThemeName, ThemeMode, ThemeName};
|
||||
use settings::{ParameterizedJsonSchema, Settings, SettingsContent};
|
||||
use settings::{Settings, SettingsContent};
|
||||
use std::sync::Arc;
|
||||
use util::schemars::replace_subschema;
|
||||
use util::{MergeFrom, ResultExt as _};
|
||||
use util::ResultExt as _;
|
||||
|
||||
const MIN_FONT_SIZE: Pixels = px(6.0);
|
||||
const MAX_FONT_SIZE: Pixels = px(100.0);
|
||||
|
|
@ -270,45 +269,6 @@ pub struct AgentFontSize(Pixels);
|
|||
|
||||
impl Global for AgentFontSize {}
|
||||
|
||||
inventory::submit! {
|
||||
ParameterizedJsonSchema {
|
||||
add_and_get_ref: |generator, _params, cx| {
|
||||
replace_subschema::<settings::ThemeName>(generator, || json_schema!({
|
||||
"type": "string",
|
||||
"enum": ThemeRegistry::global(cx).list_names(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inventory::submit! {
|
||||
ParameterizedJsonSchema {
|
||||
add_and_get_ref: |generator, _params, cx| {
|
||||
replace_subschema::<settings::IconThemeName>(generator, || json_schema!({
|
||||
"type": "string",
|
||||
"enum": ThemeRegistry::global(cx)
|
||||
.list_icon_themes()
|
||||
.into_iter()
|
||||
.map(|icon_theme| icon_theme.name)
|
||||
.collect::<Vec<SharedString>>(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inventory::submit! {
|
||||
ParameterizedJsonSchema {
|
||||
add_and_get_ref: |generator, params, _cx| {
|
||||
replace_subschema::<settings::FontFamilyName>(generator, || {
|
||||
json_schema!({
|
||||
"type": "string",
|
||||
"enum": params.font_names,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the selection of a theme, which can be either static or dynamic.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
|
|
@ -807,20 +767,20 @@ pub fn font_fallbacks_from_settings(
|
|||
}
|
||||
|
||||
impl settings::Settings for ThemeSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, cx: &mut App) -> Self {
|
||||
let content = &content.theme;
|
||||
// todo(settings_refactor). This should *not* require cx...
|
||||
let themes = ThemeRegistry::default_global(cx);
|
||||
let system_appearance = SystemAppearance::default_global(cx);
|
||||
let theme_selection: ThemeSelection = content.theme.clone().unwrap().into();
|
||||
let icon_theme_selection: IconThemeSelection = content.icon_theme.clone().unwrap().into();
|
||||
Self {
|
||||
ui_font_size: content.ui_font_size.unwrap().into(),
|
||||
let mut this = Self {
|
||||
ui_font_size: clamp_font_size(content.ui_font_size.unwrap().into()),
|
||||
ui_font: Font {
|
||||
family: content.ui_font_family.as_ref().unwrap().0.clone().into(),
|
||||
features: content.ui_font_features.clone().unwrap(),
|
||||
fallbacks: font_fallbacks_from_settings(content.ui_font_fallbacks.clone()),
|
||||
weight: content.ui_font_weight.map(FontWeight).unwrap(),
|
||||
weight: clamp_font_weight(content.ui_font_weight.unwrap()),
|
||||
style: Default::default(),
|
||||
},
|
||||
buffer_font: Font {
|
||||
|
|
@ -833,12 +793,12 @@ impl settings::Settings for ThemeSettings {
|
|||
.into(),
|
||||
features: content.buffer_font_features.clone().unwrap(),
|
||||
fallbacks: font_fallbacks_from_settings(content.buffer_font_fallbacks.clone()),
|
||||
weight: content.buffer_font_weight.map(FontWeight).unwrap(),
|
||||
weight: clamp_font_weight(content.buffer_font_weight.unwrap()),
|
||||
style: FontStyle::default(),
|
||||
},
|
||||
buffer_font_size: content.buffer_font_size.unwrap().into(),
|
||||
buffer_font_size: clamp_font_size(content.buffer_font_size.unwrap().into()),
|
||||
buffer_line_height: content.buffer_line_height.unwrap().into(),
|
||||
agent_font_size: content.agent_font_size.flatten().map(Into::into),
|
||||
agent_font_size: content.agent_font_size.map(Into::into),
|
||||
active_theme: themes
|
||||
.get(theme_selection.theme(*system_appearance))
|
||||
.or(themes.get(&zed_default_dark().name))
|
||||
|
|
@ -852,110 +812,10 @@ impl settings::Settings for ThemeSettings {
|
|||
.unwrap(),
|
||||
icon_theme_selection: Some(icon_theme_selection),
|
||||
ui_density: content.ui_density.unwrap_or_default().into(),
|
||||
unnecessary_code_fade: content.unnecessary_code_fade.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, cx: &mut App) {
|
||||
let value = &content.theme;
|
||||
|
||||
let themes = ThemeRegistry::default_global(cx);
|
||||
let system_appearance = SystemAppearance::default_global(cx);
|
||||
|
||||
self.ui_density
|
||||
.merge_from(&value.ui_density.map(Into::into));
|
||||
|
||||
if let Some(value) = value.buffer_font_family.clone() {
|
||||
self.buffer_font.family = value.0.into();
|
||||
}
|
||||
if let Some(value) = value.buffer_font_features.clone() {
|
||||
self.buffer_font.features = value;
|
||||
}
|
||||
if let Some(value) = value.buffer_font_fallbacks.clone() {
|
||||
self.buffer_font.fallbacks = font_fallbacks_from_settings(Some(value));
|
||||
}
|
||||
if let Some(value) = value.buffer_font_weight {
|
||||
self.buffer_font.weight = clamp_font_weight(value);
|
||||
}
|
||||
|
||||
if let Some(value) = value.ui_font_family.clone() {
|
||||
self.ui_font.family = value.0.into();
|
||||
}
|
||||
if let Some(value) = value.ui_font_features.clone() {
|
||||
self.ui_font.features = value;
|
||||
}
|
||||
if let Some(value) = value.ui_font_fallbacks.clone() {
|
||||
self.ui_font.fallbacks = font_fallbacks_from_settings(Some(value));
|
||||
}
|
||||
if let Some(value) = value.ui_font_weight {
|
||||
self.ui_font.weight = clamp_font_weight(value);
|
||||
}
|
||||
|
||||
if let Some(value) = &value.theme {
|
||||
self.theme_selection = Some(value.clone().into());
|
||||
|
||||
let theme_name = self
|
||||
.theme_selection
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.theme(*system_appearance);
|
||||
|
||||
match themes.get(theme_name) {
|
||||
Ok(theme) => {
|
||||
self.active_theme = theme;
|
||||
}
|
||||
Err(err @ ThemeNotFoundError(_)) => {
|
||||
if themes.extensions_loaded() {
|
||||
log::error!("{err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.experimental_theme_overrides
|
||||
.clone_from(&value.experimental_theme_overrides);
|
||||
self.theme_overrides.clone_from(&value.theme_overrides);
|
||||
|
||||
self.apply_theme_overrides();
|
||||
|
||||
if let Some(value) = &value.icon_theme {
|
||||
self.icon_theme_selection = Some(value.clone().into());
|
||||
|
||||
let icon_theme_name = self
|
||||
.icon_theme_selection
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.icon_theme(*system_appearance);
|
||||
|
||||
match themes.get_icon_theme(icon_theme_name) {
|
||||
Ok(icon_theme) => {
|
||||
self.active_icon_theme = icon_theme;
|
||||
}
|
||||
Err(err @ IconThemeNotFoundError(_)) => {
|
||||
if themes.extensions_loaded() {
|
||||
log::error!("{err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.ui_font_size
|
||||
.merge_from(&value.ui_font_size.map(Into::into).map(clamp_font_size));
|
||||
self.buffer_font_size
|
||||
.merge_from(&value.buffer_font_size.map(Into::into).map(clamp_font_size));
|
||||
self.agent_font_size.merge_from(
|
||||
&value
|
||||
.agent_font_size
|
||||
.map(|value| value.map(Into::into).map(clamp_font_size)),
|
||||
);
|
||||
|
||||
self.buffer_line_height
|
||||
.merge_from(&value.buffer_line_height.map(Into::into));
|
||||
|
||||
// Clamp the `unnecessary_code_fade` to ensure text can't disappear entirely.
|
||||
self.unnecessary_code_fade
|
||||
.merge_from(&value.unnecessary_code_fade);
|
||||
self.unnecessary_code_fade = self.unnecessary_code_fade.clamp(0.0, 0.9);
|
||||
unnecessary_code_fade: content.unnecessary_code_fade.unwrap().clamp(0.0, 0.9),
|
||||
};
|
||||
this.apply_theme_overrides();
|
||||
this
|
||||
}
|
||||
|
||||
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
pub use settings::TitleBarVisibility;
|
||||
use settings::{Settings, SettingsContent};
|
||||
use ui::App;
|
||||
use util::MergeFrom;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TitleBarSettings {
|
||||
|
|
@ -16,7 +15,7 @@ pub struct TitleBarSettings {
|
|||
}
|
||||
|
||||
impl Settings for TitleBarSettings {
|
||||
fn from_defaults(s: &SettingsContent, _: &mut App) -> Self {
|
||||
fn from_settings(s: &SettingsContent, _: &mut App) -> Self {
|
||||
let content = s.title_bar.clone().unwrap();
|
||||
TitleBarSettings {
|
||||
show: content.show.unwrap(),
|
||||
|
|
@ -29,24 +28,4 @@ impl Settings for TitleBarSettings {
|
|||
show_menus: content.show_menus.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, s: &SettingsContent, _: &mut App) {
|
||||
let Some(content) = &s.title_bar else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.show.merge_from(&content.show);
|
||||
self.show_branch_icon.merge_from(&content.show_branch_icon);
|
||||
self.show_onboarding_banner
|
||||
.merge_from(&content.show_onboarding_banner);
|
||||
self.show_user_picture
|
||||
.merge_from(&content.show_user_picture);
|
||||
self.show_branch_name.merge_from(&content.show_branch_name);
|
||||
self.show_project_items
|
||||
.merge_from(&content.show_project_items);
|
||||
self.show_sign_in.merge_from(&content.show_sign_in);
|
||||
self.show_menus.merge_from(&content.show_menus);
|
||||
}
|
||||
|
||||
fn import_from_vscode(_: &settings::VsCodeSettings, _: &mut SettingsContent) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@ pub fn replace_subschema<T: JsonSchema>(
|
|||
let schema_name = T::schema_name();
|
||||
let definitions = generator.definitions_mut();
|
||||
assert!(!definitions.contains_key(&format!("{schema_name}2")));
|
||||
if definitions.contains_key(schema_name.as_ref()) {
|
||||
definitions.insert(schema_name.to_string(), schema().to_value());
|
||||
}
|
||||
assert!(definitions.contains_key(schema_name.as_ref()));
|
||||
definitions.insert(schema_name.to_string(), schema().to_value());
|
||||
schemars::Schema::new_ref(format!("{DEFS_PATH}{schema_name}"))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1390,21 +1390,3 @@ Line 3"#
|
|||
assert_eq!(result[1], (10..15, "world")); // '🦀' is 4 bytes
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refine<T: Clone>(dest: &mut T, src: &Option<T>) {
|
||||
if let Some(src) = src {
|
||||
*dest = src.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MergeFrom: Sized + Clone {
|
||||
fn merge_from(&mut self, src: &Option<Self>);
|
||||
}
|
||||
|
||||
impl<T: Clone> MergeFrom for T {
|
||||
fn merge_from(&mut self, src: &Option<Self>) {
|
||||
if let Some(src) = src {
|
||||
*self = src.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ use std::{mem, ops::Range, sync::Arc};
|
|||
use surrounds::SurroundsType;
|
||||
use theme::ThemeSettings;
|
||||
use ui::{IntoElement, SharedString, px};
|
||||
use util::MergeFrom;
|
||||
use vim_mode_setting::HelixModeSetting;
|
||||
use vim_mode_setting::VimModeSetting;
|
||||
use workspace::{self, Pane, Workspace};
|
||||
|
|
@ -1845,7 +1844,7 @@ impl From<settings::ModeContent> for Mode {
|
|||
}
|
||||
|
||||
impl Settings for VimSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let vim = content.vim.clone().unwrap();
|
||||
Self {
|
||||
default_mode: vim.default_mode.unwrap().into(),
|
||||
|
|
@ -1857,29 +1856,4 @@ impl Settings for VimSettings {
|
|||
cursor_shape: vim.cursor_shape.unwrap().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(vim) = content.vim.as_ref() else {
|
||||
return;
|
||||
};
|
||||
self.default_mode
|
||||
.merge_from(&vim.default_mode.map(Into::into));
|
||||
self.toggle_relative_line_numbers
|
||||
.merge_from(&vim.toggle_relative_line_numbers);
|
||||
self.use_system_clipboard
|
||||
.merge_from(&vim.use_system_clipboard);
|
||||
self.use_smartcase_find.merge_from(&vim.use_smartcase_find);
|
||||
self.custom_digraphs.merge_from(&vim.custom_digraphs);
|
||||
self.highlight_on_yank_duration
|
||||
.merge_from(&vim.highlight_on_yank_duration);
|
||||
self.cursor_shape
|
||||
.merge_from(&vim.cursor_shape.map(Into::into));
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
_vscode: &settings::VsCodeSettings,
|
||||
_current: &mut settings::SettingsContent,
|
||||
) {
|
||||
// TODO: translate vim extension settings
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,16 +16,10 @@ pub fn init(cx: &mut App) {
|
|||
pub struct VimModeSetting(pub bool);
|
||||
|
||||
impl Settings for VimModeSetting {
|
||||
fn from_defaults(content: &SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &SettingsContent, _cx: &mut App) -> Self {
|
||||
Self(content.vim_mode.unwrap())
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
|
||||
if let Some(vim_mode) = content.vim_mode {
|
||||
self.0 = vim_mode;
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _content: &mut SettingsContent) {
|
||||
// TODO: could possibly check if any of the `vim.<foo>` keys are set?
|
||||
}
|
||||
|
|
@ -34,15 +28,9 @@ impl Settings for VimModeSetting {
|
|||
pub struct HelixModeSetting(pub bool);
|
||||
|
||||
impl Settings for HelixModeSetting {
|
||||
fn from_defaults(content: &SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &SettingsContent, _cx: &mut App) -> Self {
|
||||
Self(content.helix_mode.unwrap())
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &SettingsContent, _cx: &mut App) {
|
||||
if let Some(helix_mode) = content.helix_mode {
|
||||
self.0 = helix_mode;
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use std::{
|
|||
};
|
||||
use theme::Theme;
|
||||
use ui::{Color, Icon, IntoElement, Label, LabelCommon};
|
||||
use util::{MergeFrom as _, ResultExt};
|
||||
use util::ResultExt;
|
||||
|
||||
pub const LEADER_UPDATE_THROTTLE: Duration = Duration::from_millis(200);
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ pub struct PreviewTabsSettings {
|
|||
}
|
||||
|
||||
impl Settings for ItemSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let tabs = content.tabs.as_ref().unwrap();
|
||||
Self {
|
||||
git_status: tabs.git_status.unwrap(),
|
||||
|
|
@ -77,18 +77,6 @@ impl Settings for ItemSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(tabs) = content.tabs.as_ref() else {
|
||||
return;
|
||||
};
|
||||
self.git_status.merge_from(&tabs.git_status);
|
||||
self.close_position.merge_from(&tabs.close_position);
|
||||
self.activate_on_close.merge_from(&tabs.activate_on_close);
|
||||
self.file_icons.merge_from(&tabs.file_icons);
|
||||
self.show_diagnostics.merge_from(&tabs.show_diagnostics);
|
||||
self.show_close_button.merge_from(&tabs.show_close_button);
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
vscode: &settings::VsCodeSettings,
|
||||
current: &mut settings::SettingsContent,
|
||||
|
|
@ -125,7 +113,7 @@ impl Settings for ItemSettings {
|
|||
}
|
||||
|
||||
impl Settings for PreviewTabsSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let preview_tabs = content.preview_tabs.as_ref().unwrap();
|
||||
Self {
|
||||
enabled: preview_tabs.enabled.unwrap(),
|
||||
|
|
@ -136,18 +124,6 @@ impl Settings for PreviewTabsSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(preview_tabs) = content.preview_tabs.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.enabled.merge_from(&preview_tabs.enabled);
|
||||
self.enable_preview_from_file_finder
|
||||
.merge_from(&preview_tabs.enable_preview_from_file_finder);
|
||||
self.enable_preview_from_code_navigation
|
||||
.merge_from(&preview_tabs.enable_preview_from_code_navigation);
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
vscode: &settings::VsCodeSettings,
|
||||
current: &mut settings::SettingsContent,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ pub use settings::{
|
|||
BottomDockLayout, PaneSplitDirectionHorizontal, PaneSplitDirectionVertical,
|
||||
RestoreOnStartupBehavior,
|
||||
};
|
||||
use util::MergeFrom as _;
|
||||
|
||||
pub struct WorkspaceSettings {
|
||||
pub active_pane_modifiers: ActivePanelModifiers,
|
||||
|
|
@ -63,7 +62,7 @@ pub struct TabBarSettings {
|
|||
}
|
||||
|
||||
impl Settings for WorkspaceSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let workspace = &content.workspace;
|
||||
Self {
|
||||
active_pane_modifiers: ActivePanelModifiers {
|
||||
|
|
@ -111,65 +110,6 @@ impl Settings for WorkspaceSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let workspace = &content.workspace;
|
||||
if let Some(border_size) = workspace
|
||||
.active_pane_modifiers
|
||||
.and_then(|modifier| modifier.border_size)
|
||||
{
|
||||
self.active_pane_modifiers.border_size = Some(border_size);
|
||||
}
|
||||
|
||||
if let Some(inactive_opacity) = workspace
|
||||
.active_pane_modifiers
|
||||
.and_then(|modifier| modifier.inactive_opacity)
|
||||
{
|
||||
self.active_pane_modifiers.inactive_opacity = Some(inactive_opacity);
|
||||
}
|
||||
|
||||
self.bottom_dock_layout
|
||||
.merge_from(&workspace.bottom_dock_layout);
|
||||
self.pane_split_direction_horizontal
|
||||
.merge_from(&workspace.pane_split_direction_horizontal);
|
||||
self.pane_split_direction_vertical
|
||||
.merge_from(&workspace.pane_split_direction_vertical);
|
||||
self.centered_layout.merge_from(&workspace.centered_layout);
|
||||
self.confirm_quit.merge_from(&workspace.confirm_quit);
|
||||
self.show_call_status_icon
|
||||
.merge_from(&workspace.show_call_status_icon);
|
||||
self.autosave.merge_from(&workspace.autosave);
|
||||
self.restore_on_startup
|
||||
.merge_from(&workspace.restore_on_startup);
|
||||
self.restore_on_file_reopen
|
||||
.merge_from(&workspace.restore_on_file_reopen);
|
||||
self.drop_target_size
|
||||
.merge_from(&workspace.drop_target_size);
|
||||
self.use_system_path_prompts
|
||||
.merge_from(&workspace.use_system_path_prompts);
|
||||
self.use_system_prompts
|
||||
.merge_from(&workspace.use_system_prompts);
|
||||
self.command_aliases
|
||||
.extend(workspace.command_aliases.clone());
|
||||
if let Some(max_tabs) = workspace.max_tabs {
|
||||
self.max_tabs = Some(max_tabs);
|
||||
}
|
||||
self.when_closing_with_no_tabs
|
||||
.merge_from(&workspace.when_closing_with_no_tabs);
|
||||
self.on_last_window_closed
|
||||
.merge_from(&workspace.on_last_window_closed);
|
||||
self.resize_all_panels_in_dock.merge_from(
|
||||
&workspace
|
||||
.resize_all_panels_in_dock
|
||||
.as_ref()
|
||||
.map(|resize| resize.clone().into_iter().map(Into::into).collect()),
|
||||
);
|
||||
self.close_on_file_delete
|
||||
.merge_from(&workspace.close_on_file_delete);
|
||||
self.use_system_window_tabs
|
||||
.merge_from(&workspace.use_system_window_tabs);
|
||||
self.zoomed_padding.merge_from(&workspace.zoomed_padding);
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
vscode: &settings::VsCodeSettings,
|
||||
current: &mut settings::SettingsContent,
|
||||
|
|
@ -257,7 +197,7 @@ impl Settings for WorkspaceSettings {
|
|||
}
|
||||
|
||||
impl Settings for TabBarSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
let tab_bar = content.tab_bar.clone().unwrap();
|
||||
TabBarSettings {
|
||||
show: tab_bar.show.unwrap(),
|
||||
|
|
@ -266,17 +206,6 @@ impl Settings for TabBarSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
|
||||
let Some(tab_bar) = &content.tab_bar else {
|
||||
return;
|
||||
};
|
||||
self.show.merge_from(&tab_bar.show);
|
||||
self.show_nav_history_buttons
|
||||
.merge_from(&tab_bar.show_nav_history_buttons);
|
||||
self.show_tab_bar_buttons
|
||||
.merge_from(&tab_bar.show_tab_bar_buttons);
|
||||
}
|
||||
|
||||
fn import_from_vscode(
|
||||
vscode: &settings::VsCodeSettings,
|
||||
current: &mut settings::SettingsContent,
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ impl WorktreeSettings {
|
|||
}
|
||||
|
||||
impl Settings for WorktreeSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
|
||||
fn from_settings(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 private_files = worktree.private_files.unwrap().0;
|
||||
let parsed_file_scan_inclusions: Vec<String> = file_scan_inclusions
|
||||
.iter()
|
||||
.flat_map(|glob| {
|
||||
|
|
@ -49,54 +49,16 @@ impl Settings for WorktreeSettings {
|
|||
Self {
|
||||
project_name: None,
|
||||
file_scan_exclusions: path_matchers(file_scan_exclusions, "file_scan_exclusions")
|
||||
.unwrap(),
|
||||
.log_err()
|
||||
.unwrap_or_default(),
|
||||
file_scan_inclusions: path_matchers(
|
||||
parsed_file_scan_inclusions,
|
||||
"file_scan_inclusions",
|
||||
)
|
||||
.unwrap(),
|
||||
private_files: path_matchers(private_files, "private_files").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
private_files: path_matchers(private_files, "private_files")
|
||||
.log_err()
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,17 +24,11 @@ pub struct ZlogSettings {
|
|||
}
|
||||
|
||||
impl Settings for ZlogSettings {
|
||||
fn from_defaults(content: &settings::SettingsContent, _: &mut App) -> Self {
|
||||
fn from_settings(content: &settings::SettingsContent, _: &mut App) -> Self {
|
||||
ZlogSettings {
|
||||
scopes: content.log.clone().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) {
|
||||
if let Some(log) = &content.log {
|
||||
self.scopes.extend(log.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn import_from_vscode(_: &settings::VsCodeSettings, _: &mut settings::SettingsContent) {}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue