Detailed changes
@@ -14899,7 +14899,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"collections",
- "derive_more 0.99.20",
"ec4rs",
"fs",
"futures 0.3.31",
@@ -14916,16 +14915,33 @@ dependencies = [
"serde",
"serde_json",
"serde_json_lenient",
- "serde_repr",
+ "settings_content",
"settings_json",
"settings_macros",
"smallvec",
- "strum 0.27.2",
"unindent",
"util",
"zlog",
]
+[[package]]
+name = "settings_content"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "collections",
+ "derive_more 0.99.20",
+ "log",
+ "schemars",
+ "serde",
+ "serde_json",
+ "serde_json_lenient",
+ "settings_json",
+ "settings_macros",
+ "strum 0.27.2",
+ "util",
+]
+
[[package]]
name = "settings_json"
version = "0.1.0"
@@ -153,6 +153,7 @@ members = [
"crates/search",
"crates/session",
"crates/settings",
+ "crates/settings_content",
"crates/settings_json",
"crates/settings_macros",
"crates/settings_profile_selector",
@@ -387,6 +388,7 @@ scheduler = { path = "crates/scheduler" }
search = { path = "crates/search" }
session = { path = "crates/session" }
settings = { path = "crates/settings" }
+settings_content = { path = "crates/settings_content" }
settings_json = { path = "crates/settings_json" }
settings_macros = { path = "crates/settings_macros" }
settings_ui = { path = "crates/settings_ui" }
@@ -35,7 +35,7 @@ impl AgentServer for CustomAgentServer {
settings
.get::<AllAgentServersSettings>(None)
.custom
- .get(&self.name())
+ .get(self.name().as_ref())
.cloned()
});
@@ -53,7 +53,7 @@ impl AgentServer for CustomAgentServer {
settings
.get::<AllAgentServersSettings>(None)
.custom
- .get(&self.name())
+ .get(self.name().as_ref())
.cloned()
});
@@ -87,7 +87,7 @@ impl AgentServer for CustomAgentServer {
.agent_servers
.get_or_insert_default()
.custom
- .entry(name.clone())
+ .entry(name.to_string())
.or_insert_with(|| settings::CustomAgentServerSettings::Extension {
default_model: None,
default_mode: None,
@@ -131,7 +131,7 @@ impl AgentServer for CustomAgentServer {
.agent_servers
.get_or_insert_default()
.custom
- .entry(name.clone())
+ .entry(name.to_string())
.or_insert_with(|| settings::CustomAgentServerSettings::Extension {
default_model: None,
default_mode: None,
@@ -154,7 +154,7 @@ impl AgentServer for CustomAgentServer {
settings
.get::<AllAgentServersSettings>(None)
.custom
- .get(&self.name())
+ .get(self.name().as_ref())
.cloned()
});
@@ -170,7 +170,7 @@ impl AgentServer for CustomAgentServer {
.agent_servers
.get_or_insert_default()
.custom
- .entry(name.clone())
+ .entry(name.to_string())
.or_insert_with(|| settings::CustomAgentServerSettings::Extension {
default_model: None,
default_mode: None,
@@ -193,7 +193,7 @@ impl AgentServer for CustomAgentServer {
settings
.get::<AllAgentServersSettings>(None)
.custom
- .get(&self.name())
+ .get(self.name().as_ref())
.cloned()
});
@@ -221,7 +221,7 @@ impl AgentServer for CustomAgentServer {
.agent_servers
.get_or_insert_default()
.custom
- .entry(name.clone())
+ .entry(name.to_string())
.or_insert_with(|| settings::CustomAgentServerSettings::Extension {
default_model: None,
default_mode: None,
@@ -255,7 +255,7 @@ impl AgentServer for CustomAgentServer {
settings
.get::<AllAgentServersSettings>(None)
.custom
- .get(&self.name())
+ .get(self.name().as_ref())
.cloned()
});
@@ -279,7 +279,7 @@ impl AgentServer for CustomAgentServer {
.agent_servers
.get_or_insert_default()
.custom
- .entry(name.clone())
+ .entry(name.to_string())
.or_insert_with(|| settings::CustomAgentServerSettings::Extension {
default_model: None,
default_mode: None,
@@ -322,7 +322,7 @@ impl AgentServer for CustomAgentServer {
settings
.get::<AllAgentServersSettings>(None)
.custom
- .get(&self.name())
+ .get(self.name().as_ref())
.map(|s| match s {
project::agent_server_store::CustomAgentServerSettings::Custom {
default_config_options,
@@ -1338,22 +1338,24 @@ async fn open_new_agent_servers_entry_in_settings_editor(
let mut unique_server_name = None;
let edits = settings.edits_for_update(&text, |settings| {
- let server_name: Option<SharedString> = (0..u8::MAX)
+ let server_name: Option<String> = (0..u8::MAX)
.map(|i| {
if i == 0 {
- "your_agent".into()
+ "your_agent".to_string()
} else {
- format!("your_agent_{}", i).into()
+ format!("your_agent_{}", i)
}
})
.find(|name| {
!settings
.agent_servers
.as_ref()
- .is_some_and(|agent_servers| agent_servers.custom.contains_key(name))
+ .is_some_and(|agent_servers| {
+ agent_servers.custom.contains_key(name.as_str())
+ })
});
if let Some(server_name) = server_name {
- unique_server_name = Some(server_name.clone());
+ unique_server_name = Some(SharedString::from(server_name.clone()));
settings
.agent_servers
.get_or_insert_default()
@@ -1140,11 +1140,10 @@ impl AgentPanel {
let _ = settings
.theme
.agent_ui_font_size
- .insert(theme::clamp_font_size(agent_ui_font_size).into());
- let _ = settings
- .theme
- .agent_buffer_font_size
- .insert(theme::clamp_font_size(agent_buffer_font_size).into());
+ .insert(f32::from(theme::clamp_font_size(agent_ui_font_size)).into());
+ let _ = settings.theme.agent_buffer_font_size.insert(
+ f32::from(theme::clamp_font_size(agent_buffer_font_size)).into(),
+ );
});
} else {
theme::adjust_agent_ui_font_size(cx, |size| size + delta);
@@ -4615,9 +4615,7 @@ async fn test_formatting_buffer(
file.project.all_languages.defaults.formatter =
Some(FormatterList::Single(Formatter::External {
command: "awk".into(),
- arguments: Some(
- vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
- ),
+ arguments: Some(vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()]),
}));
});
});
@@ -1344,7 +1344,7 @@ fn toggle_show_edit_predictions_for_language(
.all_languages
.languages
.0
- .entry(language.name().0)
+ .entry(language.name().0.to_string())
.or_default()
.show_edit_predictions = Some(!show_edit_predictions);
});
@@ -186,7 +186,7 @@ mod tests {
use settings::{AccentContent, SettingsStore};
use text::{Bias, OffsetRangeExt, ToOffset};
use theme::ThemeStyleContent;
- use ui::SharedString;
+
use util::{path, post_inc};
#[gpui::test]
@@ -1304,8 +1304,8 @@ mod foo «1{
theme.to_string(),
ThemeStyleContent {
accents: vec![
- AccentContent(Some(SharedString::new("#ff0000"))),
- AccentContent(Some(SharedString::new("#0000ff"))),
+ AccentContent(Some("#ff0000".to_string())),
+ AccentContent(Some("#0000ff".to_string())),
],
..ThemeStyleContent::default()
},
@@ -23678,7 +23678,7 @@ impl Editor {
.into_iter()
.flatten(),
)
- .flat_map(|accent| accent.0.clone())
+ .flat_map(|accent| accent.0.clone().map(SharedString::from))
.collect();
Some(AccentData {
@@ -18484,7 +18484,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppCon
let _fake_server = fake_servers.next().await.unwrap();
update_test_language_settings(cx, |language_settings| {
language_settings.languages.0.insert(
- language_name.clone().0,
+ language_name.clone().0.to_string(),
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
..Default::default()
@@ -1190,7 +1190,7 @@ impl LanguageRegistryState {
language.set_theme(theme.syntax());
}
self.language_settings.languages.0.insert(
- language.name().0,
+ language.name().0.to_string(),
LanguageSettingsContent {
tab_size: language.config.tab_size,
hard_tabs: language.config.hard_tabs,
@@ -9,6 +9,7 @@ use ec4rs::{
use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
use gpui::{App, Modifiers, SharedString};
use itertools::{Either, Itertools};
+use settings::IntoGpui;
pub use settings::{
CompletionSettingsContent, EditPredictionProvider, EditPredictionsMode, FormatOnSave,
@@ -584,7 +585,9 @@ impl settings::Settings for AllLanguageSettings {
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,
+ toggle_on_modifiers_press: inlay_hints
+ .toggle_on_modifiers_press
+ .map(|m| m.into_gpui()),
},
use_autoclose: settings.use_autoclose.unwrap(),
use_auto_surround: settings.use_auto_surround.unwrap(),
@@ -623,7 +626,7 @@ impl settings::Settings for AllLanguageSettings {
let mut language_settings = all_languages.defaults.clone();
settings::merge_from::MergeFrom::merge_from(&mut language_settings, settings);
languages.insert(
- LanguageName(language_name.clone()),
+ LanguageName(language_name.clone().into()),
load_from_content(language_settings),
);
}
@@ -650,7 +650,7 @@ impl AgentServerStore {
.iter()
.filter_map(|(name, settings)| match settings {
CustomAgentServerSettings::Custom { command, .. } => Some((
- ExternalAgentServerName(name.clone()),
+ ExternalAgentServerName(name.clone().into()),
Box::new(LocalCustomAgent {
command: command.clone(),
project_environment: project_environment.clone(),
@@ -2014,7 +2014,7 @@ pub struct AllAgentServersSettings {
pub gemini: Option<BuiltinAgentServerSettings>,
pub claude: Option<BuiltinAgentServerSettings>,
pub codex: Option<BuiltinAgentServerSettings>,
- pub custom: HashMap<SharedString, CustomAgentServerSettings>,
+ pub custom: HashMap<String, CustomAgentServerSettings>,
}
#[derive(Default, Clone, JsonSchema, Debug, PartialEq)]
pub struct BuiltinAgentServerSettings {
@@ -1649,7 +1649,7 @@ impl LocalLspStore {
let diff = Self::format_via_external_command(
buffer,
- command.as_ref(),
+ &command,
arguments.as_deref(),
cx,
)
@@ -312,8 +312,8 @@ pub(crate) async fn start_dev_container(
.await?;
let connection = Connection::DevContainer(DevContainerConnection {
- name: project_name.into(),
- container_id: container_id.into(),
+ name: project_name,
+ container_id,
});
Ok((connection, remote_workspace_folder))
@@ -329,7 +329,7 @@ pub fn add_wsl_distro(
use gpui::ReadGlobal;
use settings::SettingsStore;
- let distro_name = SharedString::from(&connection_options.distro_name);
+ let distro_name = connection_options.distro_name.clone();
let user = connection_options.user.clone();
SettingsStore::global(cx).update_settings_file(fs, move |setting, _| {
let connections = setting
@@ -96,8 +96,8 @@ impl From<Connection> for RemoteConnectionOptions {
Connection::Wsl(conn) => RemoteConnectionOptions::Wsl(conn.into()),
Connection::DevContainer(conn) => {
RemoteConnectionOptions::Docker(DockerConnectionOptions {
- name: conn.name.to_string(),
- container_id: conn.container_id.to_string(),
+ name: conn.name,
+ container_id: conn.container_id,
upload_binary_over_docker_exec: false,
})
}
@@ -470,7 +470,7 @@ impl RemoteEntry {
Self::Project { connection, .. } => Cow::Borrowed(connection),
Self::SshConfig { host, .. } => Cow::Owned(
SshConnection {
- host: host.clone(),
+ host: host.to_string(),
..SshConnection::default()
}
.into(),
@@ -1129,7 +1129,7 @@ impl RemoteServerProjects {
Connection::Ssh(connection) => {
if let Some(nickname) = connection.nickname.clone() {
let aux_label = SharedString::from(format!("({})", connection.host));
- (nickname.into(), Some(aux_label), false)
+ (nickname, Some(aux_label), false)
} else {
(connection.host.clone(), None, false)
}
@@ -1535,7 +1535,7 @@ impl RemoteServerProjects {
.ssh_connections
.get_or_insert(Default::default())
.push(SshConnection {
- host: SharedString::from(connection_options.host.to_string()),
+ host: connection_options.host.to_string(),
username: connection_options.username,
port: connection_options.port,
projects: BTreeSet::new(),
@@ -2340,7 +2340,7 @@ impl RemoteServerProjects {
.track_focus(&self.focus_handle(cx))
.child(
SshConnectionHeader {
- connection_string,
+ connection_string: connection_string.into(),
paths: Default::default(),
nickname,
is_wsl: false,
@@ -2408,7 +2408,7 @@ impl RemoteServerProjects {
..
} = server
{
- expected_ssh_hosts.remove(&connection.host);
+ expected_ssh_hosts.remove(connection.host.as_str());
}
}
should_rebuild = current_ssh_hosts != expected_ssh_hosts;
@@ -36,7 +36,7 @@ pub struct WslConnectionOptions {
impl From<settings::WslConnection> for WslConnectionOptions {
fn from(val: settings::WslConnection) -> Self {
WslConnectionOptions {
- distro_name: val.distro_name.into(),
+ distro_name: val.distro_name,
user: val.user,
}
}
@@ -18,7 +18,6 @@ test-support = ["gpui/test-support", "fs/test-support"]
[dependencies]
anyhow.workspace = true
collections.workspace = true
-derive_more.workspace = true
ec4rs.workspace = true
fs.workspace = true
futures.workspace = true
@@ -33,11 +32,10 @@ schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
-serde_repr.workspace = true
+settings_content.workspace = true
settings_json.workspace = true
settings_macros.workspace = true
smallvec.workspace = true
-strum.workspace = true
util.workspace = true
zlog.workspace = true
@@ -0,0 +1,104 @@
+use gpui::{
+ FontFeatures, FontStyle, FontWeight, Modifiers, Pixels, SharedString,
+ WindowBackgroundAppearance, px,
+};
+use settings_content::{
+ FontFamilyName, FontFeaturesContent, FontSize, FontStyleContent, FontWeightContent,
+ ModifiersContent, WindowBackgroundContent,
+};
+use std::sync::Arc;
+
+/// A trait for converting settings content types into their GPUI equivalents.
+pub trait IntoGpui {
+ type Output;
+ fn into_gpui(self) -> Self::Output;
+}
+
+impl IntoGpui for FontStyleContent {
+ type Output = FontStyle;
+
+ fn into_gpui(self) -> Self::Output {
+ match self {
+ FontStyleContent::Normal => FontStyle::Normal,
+ FontStyleContent::Italic => FontStyle::Italic,
+ FontStyleContent::Oblique => FontStyle::Oblique,
+ }
+ }
+}
+
+impl IntoGpui for FontWeightContent {
+ type Output = FontWeight;
+
+ fn into_gpui(self) -> Self::Output {
+ FontWeight(self.0.clamp(100., 950.))
+ }
+}
+
+impl IntoGpui for FontFeaturesContent {
+ type Output = FontFeatures;
+
+ fn into_gpui(self) -> Self::Output {
+ FontFeatures(Arc::new(self.0.into_iter().collect()))
+ }
+}
+
+impl IntoGpui for WindowBackgroundContent {
+ type Output = WindowBackgroundAppearance;
+
+ fn into_gpui(self) -> Self::Output {
+ match self {
+ WindowBackgroundContent::Opaque => WindowBackgroundAppearance::Opaque,
+ WindowBackgroundContent::Transparent => WindowBackgroundAppearance::Transparent,
+ WindowBackgroundContent::Blurred => WindowBackgroundAppearance::Blurred,
+ }
+ }
+}
+
+impl IntoGpui for ModifiersContent {
+ type Output = Modifiers;
+
+ fn into_gpui(self) -> Self::Output {
+ Modifiers {
+ control: self.control,
+ alt: self.alt,
+ shift: self.shift,
+ platform: self.platform,
+ function: self.function,
+ }
+ }
+}
+
+impl IntoGpui for FontSize {
+ type Output = Pixels;
+
+ fn into_gpui(self) -> Self::Output {
+ px(self.0)
+ }
+}
+
+impl IntoGpui for FontFamilyName {
+ type Output = SharedString;
+
+ fn into_gpui(self) -> Self::Output {
+ SharedString::from(self.0)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use gpui::FontWeight;
+ use settings_content::FontWeightContent;
+
+ #[test]
+ fn test_font_weight_content_constants_match_gpui() {
+ assert_eq!(FontWeightContent::THIN.0, FontWeight::THIN.0);
+ assert_eq!(FontWeightContent::EXTRA_LIGHT.0, FontWeight::EXTRA_LIGHT.0);
+ assert_eq!(FontWeightContent::LIGHT.0, FontWeight::LIGHT.0);
+ assert_eq!(FontWeightContent::NORMAL.0, FontWeight::NORMAL.0);
+ assert_eq!(FontWeightContent::MEDIUM.0, FontWeight::MEDIUM.0);
+ assert_eq!(FontWeightContent::SEMIBOLD.0, FontWeight::SEMIBOLD.0);
+ assert_eq!(FontWeightContent::BOLD.0, FontWeight::BOLD.0);
+ assert_eq!(FontWeightContent::EXTRA_BOLD.0, FontWeight::EXTRA_BOLD.0);
+ assert_eq!(FontWeightContent::BLACK.0, FontWeight::BLACK.0);
+ }
+}
@@ -1,17 +1,21 @@
mod base_keymap_setting;
+mod content_into_gpui;
mod editable_setting_control;
-mod fallible_options;
mod keymap_file;
-pub mod merge_from;
-mod serde_helper;
-mod settings_content;
mod settings_file;
mod settings_store;
mod vscode_import;
-pub use settings_content::*;
pub use settings_macros::RegisterSetting;
+pub mod settings_content {
+ pub use ::settings_content::*;
+}
+
+pub mod fallible_options {
+ pub use ::settings_content::{FallibleOption, parse_json};
+}
+
#[doc(hidden)]
pub mod private {
pub use crate::settings_store::{RegisteredSetting, SettingValue};
@@ -19,22 +23,25 @@ pub mod private {
}
use gpui::{App, Global};
+use release_channel::ReleaseChannel;
use rust_embed::RustEmbed;
+use std::env;
use std::{borrow::Cow, fmt, str};
use util::asset_str;
+pub use ::settings_content::*;
pub use base_keymap_setting::*;
+pub use content_into_gpui::IntoGpui;
pub use editable_setting_control::*;
pub use keymap_file::{
KeyBindingValidator, KeyBindingValidatorRegistration, KeybindSource, KeybindUpdateOperation,
KeybindUpdateTarget, KeymapFile, KeymapFileLoadResult,
};
-pub use serde_helper::*;
pub use settings_file::*;
pub use settings_json::*;
pub use settings_store::{
InvalidSettingsError, LSP_SETTINGS_SCHEMA_URL_PREFIX, LocalSettingsKind, MigrationStatus,
- ParseStatus, Settings, SettingsFile, SettingsJsonSchemaParams, SettingsKey, SettingsLocation,
+ Settings, SettingsFile, SettingsJsonSchemaParams, SettingsKey, SettingsLocation,
SettingsParseResult, SettingsStore,
};
@@ -47,6 +54,39 @@ pub struct ActiveSettingsProfileName(pub String);
impl Global for ActiveSettingsProfileName {}
+pub trait UserSettingsContentExt {
+ fn for_profile(&self, cx: &App) -> Option<&SettingsContent>;
+ fn for_release_channel(&self) -> Option<&SettingsContent>;
+ fn for_os(&self) -> Option<&SettingsContent>;
+}
+
+impl UserSettingsContentExt for UserSettingsContent {
+ fn for_profile(&self, cx: &App) -> Option<&SettingsContent> {
+ let Some(active_profile) = cx.try_global::<ActiveSettingsProfileName>() else {
+ return None;
+ };
+ self.profiles.get(&active_profile.0)
+ }
+
+ fn for_release_channel(&self) -> Option<&SettingsContent> {
+ match *release_channel::RELEASE_CHANNEL {
+ ReleaseChannel::Dev => self.dev.as_deref(),
+ ReleaseChannel::Nightly => self.nightly.as_deref(),
+ ReleaseChannel::Preview => self.preview.as_deref(),
+ ReleaseChannel::Stable => self.stable.as_deref(),
+ }
+ }
+
+ fn for_os(&self) -> Option<&SettingsContent> {
+ match env::consts::OS {
+ "macos" => self.macos.as_deref(),
+ "linux" => self.linux.as_deref(),
+ "windows" => self.windows.as_deref(),
+ _ => None,
+ }
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord, serde::Serialize)]
pub struct WorktreeId(usize);
@@ -30,17 +30,17 @@ use util::{
pub type EditorconfigProperties = ec4rs::Properties;
+use crate::settings_content::{
+ ExtensionsSettingsContent, FontFamilyName, IconThemeName, LanguageSettingsContent,
+ LanguageToSettingsMap, LspSettings, LspSettingsMap, ProjectSettingsContent, SettingsContent,
+ ThemeName, UserSettingsContent,
+};
use crate::{
- ActiveSettingsProfileName, FontFamilyName, IconThemeName, LanguageSettingsContent,
- LanguageToSettingsMap, LspSettings, LspSettingsMap, ThemeName, VsCodeSettings, WorktreeId,
- fallible_options,
- merge_from::MergeFrom,
- settings_content::{
- ExtensionsSettingsContent, ProjectSettingsContent, SettingsContent, UserSettingsContent,
- },
+ ActiveSettingsProfileName, ParseStatus, UserSettingsContentExt, VsCodeSettings, WorktreeId,
};
+use settings_content::{RootUserSettings, merge_from::MergeFrom};
-use settings_json::{infer_json_indent_size, parse_json_with_comments, update_value_in_json_text};
+use settings_json::{infer_json_indent_size, update_value_in_json_text};
pub const LSP_SETTINGS_SCHEMA_URL_PREFIX: &str = "zed://schemas/settings/lsp/";
@@ -266,7 +266,9 @@ 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: Rc<SettingsContent> =
- parse_json_with_comments(default_settings).unwrap();
+ SettingsContent::parse_json_with_comments(default_settings)
+ .unwrap()
+ .into();
let mut this = Self {
setting_values: Default::default(),
default_settings: default_settings.clone(),
@@ -665,14 +667,14 @@ impl SettingsStore {
}
#[inline(always)]
- fn parse_and_migrate_zed_settings<SettingsContentType: serde::de::DeserializeOwned>(
+ fn parse_and_migrate_zed_settings<SettingsContentType: RootUserSettings>(
&mut self,
user_settings_content: &str,
file: SettingsFile,
) -> (Option<SettingsContentType>, SettingsParseResult) {
let mut migration_status = MigrationStatus::NotNeeded;
let (settings, parse_status) = if user_settings_content.is_empty() {
- fallible_options::parse_json("{}")
+ SettingsContentType::parse_json("{}")
} else {
let migration_res = migrator::migrate_settings(user_settings_content);
migration_status = match &migration_res {
@@ -687,7 +689,7 @@ impl SettingsStore {
Ok(None) => user_settings_content,
Err(_) => user_settings_content,
};
- fallible_options::parse_json(content)
+ SettingsContentType::parse_json(content)
};
let result = SettingsParseResult {
@@ -735,8 +737,9 @@ impl SettingsStore {
text: &str,
update: impl FnOnce(&mut SettingsContent),
) -> Vec<(Range<usize>, String)> {
- let old_content: UserSettingsContent =
- parse_json_with_comments(text).log_err().unwrap_or_default();
+ let old_content = UserSettingsContent::parse_json_with_comments(text)
+ .log_err()
+ .unwrap_or_default();
let mut new_content = old_content.clone();
update(&mut new_content.content);
@@ -766,7 +769,8 @@ impl SettingsStore {
default_settings_content: &str,
cx: &mut App,
) -> Result<()> {
- self.default_settings = parse_json_with_comments(default_settings_content)?;
+ self.default_settings =
+ SettingsContent::parse_json_with_comments(default_settings_content)?.into();
self.recompute_values(None, cx);
Ok(())
}
@@ -814,10 +818,10 @@ impl SettingsStore {
server_settings_content: &str,
cx: &mut App,
) -> Result<()> {
- let settings: Option<SettingsContent> = if server_settings_content.is_empty() {
+ let settings = if server_settings_content.is_empty() {
None
} else {
- parse_json_with_comments(server_settings_content)?
+ Option::<SettingsContent>::parse_json_with_comments(server_settings_content)?
};
// Rewrite the server settings into a content type
@@ -1217,14 +1221,6 @@ pub struct SettingsParseResult {
pub migration_status: MigrationStatus,
}
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum ParseStatus {
- /// Settings were parsed successfully
- Success,
- /// Settings failed to parse
- Failed { error: String },
-}
-
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MigrationStatus {
/// No migration was needed - settings are up to date
@@ -803,7 +803,7 @@ impl VsCodeSettings {
buffer_font_family,
buffer_font_fallbacks,
buffer_font_size: self.read_f32("editor.fontSize").map(FontSize::from),
- buffer_font_weight: self.read_f32("editor.fontWeight").map(|w| w.into()),
+ buffer_font_weight: self.read_f32("editor.fontWeight").map(FontWeightContent),
buffer_line_height: None,
buffer_font_features: None,
agent_ui_font_size: None,
@@ -0,0 +1,35 @@
+[package]
+name = "settings_content"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "GPL-3.0-or-later"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/settings_content.rs"
+
+[features]
+default = []
+
+[dependencies]
+anyhow.workspace = true
+collections.workspace = true
+derive_more.workspace = true
+log.workspace = true
+schemars.workspace = true
+serde.workspace = true
+serde_json.workspace = true
+serde_json_lenient.workspace = true
+settings_json.workspace = true
+settings_macros.workspace = true
+strum.workspace = true
+util.workspace = true
+
+# Uncomment other workspace dependencies as needed
+# assistant.workspace = true
+# client.workspace = true
+# project.workspace = true
+# settings.workspace = true
@@ -0,0 +1 @@
+../../LICENSE-GPL
@@ -1,5 +1,4 @@
use collections::{HashMap, IndexMap};
-use gpui::SharedString;
use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Serialize};
use settings_macros::{MergeFrom, with_fallible_options};
@@ -311,7 +310,7 @@ pub enum CompletionMode {
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
pub struct LanguageModelParameters {
pub provider: Option<LanguageModelProviderSetting>,
- pub model: Option<SharedString>,
+ pub model: Option<String>,
#[serde(serialize_with = "crate::serialize_optional_f32_with_two_decimal_places")]
pub temperature: Option<f32>,
}
@@ -375,7 +374,7 @@ pub struct AllAgentServersSettings {
/// Custom agent servers configured by the user
#[serde(flatten)]
- pub custom: HashMap<SharedString, CustomAgentServerSettings>,
+ pub custom: HashMap<String, CustomAgentServerSettings>,
}
#[with_fallible_options]
@@ -8,7 +8,7 @@ thread_local! {
static ERRORS: RefCell<Option<Vec<anyhow::Error>>> = const { RefCell::new(None) };
}
-pub(crate) fn parse_json<'de, T>(json: &'de str) -> (Option<T>, ParseStatus)
+pub fn parse_json<'de, T>(json: &'de str) -> (Option<T>, ParseStatus)
where
T: Deserialize<'de>,
{
@@ -98,7 +98,7 @@ mod tests {
}
);
- assert!(crate::parse_json_with_comments::<Foo>(&input).is_err());
+ assert!(settings_json::parse_json_with_comments::<Foo>(&input).is_err());
let ParseStatus::Failed { error } = result else {
panic!("Expected parse to fail")
@@ -1,7 +1,6 @@
use std::{num::NonZeroU32, path::Path};
use collections::{HashMap, HashSet};
-use gpui::{Modifiers, SharedString};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize, de::Error as _};
use settings_macros::{MergeFrom, with_fallible_options};
@@ -9,6 +8,29 @@ use std::sync::Arc;
use crate::{ExtendingVec, merge_from};
+/// The state of the modifier keys at some point in time
+#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)]
+pub struct ModifiersContent {
+ /// The control key
+ #[serde(default)]
+ pub control: bool,
+ /// The alt key
+ /// Sometimes also known as the 'meta' key
+ #[serde(default)]
+ pub alt: bool,
+ /// The shift key
+ #[serde(default)]
+ pub shift: bool,
+ /// The command key, on macos
+ /// the windows key, on windows
+ /// the super key, on linux
+ #[serde(default)]
+ pub platform: bool,
+ /// The function key
+ #[serde(default)]
+ pub function: bool,
+}
+
#[with_fallible_options]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct AllLanguageSettingsContent {
@@ -571,7 +593,7 @@ pub struct InlayHintSettingsContent {
/// If no modifiers are specified, this is equivalent to `null`.
///
/// Default: null
- pub toggle_on_modifiers_press: Option<Modifiers>,
+ pub toggle_on_modifiers_press: Option<ModifiersContent>,
}
/// The kind of an inlay hint.
@@ -769,9 +791,9 @@ pub enum Formatter {
/// Format code using an external command.
External {
/// The external program to run.
- command: Arc<str>,
+ command: String,
/// The arguments to pass to the program.
- arguments: Option<Arc<[String]>>,
+ arguments: Option<Vec<String>>,
},
/// Files should be formatted using a code action executed by language servers.
CodeAction(String),
@@ -890,7 +912,7 @@ pub struct LanguageTaskSettingsContent {
/// Map from language name to settings.
#[with_fallible_options]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, MergeFrom)]
-pub struct LanguageToSettingsMap(pub HashMap<SharedString, LanguageSettingsContent>);
+pub struct LanguageToSettingsMap(pub HashMap<String, LanguageSettingsContent>);
/// Determines how indent guides are colored.
#[derive(
@@ -27,7 +27,7 @@ pub trait MergeFrom {
}
macro_rules! merge_from_overwrites {
- ($($type:ty),+) => {
+ ($($type:ty),+ $(,)?) => {
$(
impl MergeFrom for $type {
fn merge_from(&mut self, other: &Self) {
@@ -54,12 +54,8 @@ merge_from_overwrites!(
std::num::NonZeroU32,
String,
std::sync::Arc<str>,
- gpui::SharedString,
std::path::PathBuf,
std::sync::Arc<std::path::Path>,
- gpui::Modifiers,
- gpui::FontFeatures,
- gpui::FontWeight
);
impl<T: Clone + MergeFrom> MergeFrom for Option<T> {
@@ -67,6 +63,7 @@ impl<T: Clone + MergeFrom> MergeFrom for Option<T> {
let Some(other) = other else {
return;
};
+
if let Some(this) = self {
this.merge_from(other);
} else {
@@ -94,11 +91,11 @@ where
V: Clone + MergeFrom,
{
fn merge_from(&mut self, other: &Self) {
- for (k, v) in other {
- if let Some(existing) = self.get_mut(k) {
- existing.merge_from(v);
+ for (key, value) in other {
+ if let Some(existing) = self.get_mut(key) {
+ existing.merge_from(value);
} else {
- self.insert(k.clone(), v.clone());
+ self.insert(key.clone(), value.clone());
}
}
}
@@ -110,11 +107,11 @@ where
V: Clone + MergeFrom,
{
fn merge_from(&mut self, other: &Self) {
- for (k, v) in other {
- if let Some(existing) = self.get_mut(k) {
- existing.merge_from(v);
+ for (key, value) in other {
+ if let Some(existing) = self.get_mut(key) {
+ existing.merge_from(value);
} else {
- self.insert(k.clone(), v.clone());
+ self.insert(key.clone(), value.clone());
}
}
}
@@ -123,15 +120,14 @@ where
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: &Self) {
- for (k, v) in other {
- if let Some(existing) = self.get_mut(k) {
- existing.merge_from(v);
+ for (key, value) in other {
+ if let Some(existing) = self.get_mut(key) {
+ existing.merge_from(value);
} else {
- self.insert(k.clone(), v.clone());
+ self.insert(key.clone(), value.clone());
}
}
}
@@ -163,11 +159,11 @@ impl MergeFrom for serde_json::Value {
fn merge_from(&mut self, other: &Self) {
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(v);
+ for (key, value) in other {
+ if let Some(existing) = this.get_mut(key) {
+ existing.merge_from(value);
} else {
- this.insert(k.clone(), v.clone());
+ this.insert(key.clone(), value.clone());
}
}
}
@@ -3,12 +3,13 @@ use std::{path::PathBuf, sync::Arc};
use collections::{BTreeMap, HashMap};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
+use settings_json::parse_json_with_comments;
use settings_macros::{MergeFrom, with_fallible_options};
use util::serde::default_true;
use crate::{
- AllLanguageSettingsContent, DelayMs, ExtendingVec, ProjectTerminalSettingsContent,
- SlashCommandSettings,
+ AllLanguageSettingsContent, DelayMs, ExtendingVec, ParseStatus, ProjectTerminalSettingsContent,
+ RootUserSettings, SlashCommandSettings, fallible_options,
};
#[with_fallible_options]
@@ -24,6 +25,15 @@ impl IntoIterator for LspSettingsMap {
}
}
+impl RootUserSettings for ProjectSettingsContent {
+ fn parse_json(json: &str) -> (Option<Self>, ParseStatus) {
+ fallible_options::parse_json(json)
+ }
+ fn parse_json_with_comments(json: &str) -> anyhow::Result<Self> {
+ parse_json_with_comments(json)
+ }
+}
+
#[with_fallible_options]
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
pub struct ProjectSettingsContent {
@@ -20,7 +20,7 @@ use serde::Serializer;
/// This function can be used with Serde's `serialize_with` attribute:
/// ```
/// use serde::Serialize;
-/// use settings::serialize_f32_with_two_decimal_places;
+/// use settings_content::serialize_f32_with_two_decimal_places;
///
/// #[derive(Serialize)]
/// struct ExampleStruct(#[serde(serialize_with = "serialize_f32_with_two_decimal_places")] f32);
@@ -64,7 +64,7 @@ where
/// This function can be used with Serde's `serialize_with` attribute:
/// ```
/// use serde::Serialize;
-/// use settings::serialize_optional_f32_with_two_decimal_places;
+/// use settings_content::serialize_optional_f32_with_two_decimal_places;
///
/// #[derive(Serialize)]
/// struct ExampleStruct {
@@ -1,9 +1,12 @@
mod agent;
mod editor;
mod extension;
+mod fallible_options;
mod language;
mod language_model;
+pub mod merge_from;
mod project;
+mod serde_helper;
mod terminal;
mod theme;
mod workspace;
@@ -11,25 +14,35 @@ mod workspace;
pub use agent::*;
pub use editor::*;
pub use extension::*;
+pub use fallible_options::*;
pub use language::*;
pub use language_model::*;
+pub use merge_from::MergeFrom as MergeFromTrait;
pub use project::*;
+use serde::de::DeserializeOwned;
+pub use serde_helper::{
+ serialize_f32_with_two_decimal_places, serialize_optional_f32_with_two_decimal_places,
+};
+use settings_json::parse_json_with_comments;
pub use terminal::*;
pub use theme::*;
pub use workspace::*;
use collections::{HashMap, IndexMap};
-use gpui::{App, SharedString};
-use release_channel::ReleaseChannel;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings_macros::{MergeFrom, with_fallible_options};
use std::collections::BTreeSet;
-use std::env;
use std::sync::Arc;
pub use util::serde::default_true;
-use crate::{ActiveSettingsProfileName, merge_from};
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ParseStatus {
+ /// Settings were parsed successfully
+ Success,
+ /// Settings failed to parse
+ Failed { error: String },
+}
#[with_fallible_options]
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
@@ -166,11 +179,44 @@ pub struct SettingsContent {
}
impl SettingsContent {
- pub fn languages_mut(&mut self) -> &mut HashMap<SharedString, LanguageSettingsContent> {
+ pub fn languages_mut(&mut self) -> &mut HashMap<String, LanguageSettingsContent> {
&mut self.project.all_languages.languages.0
}
}
+// These impls are there to optimize builds by avoiding monomorphization downstream. Yes, they're repetitive, but using default impls
+// break the optimization, for whatever reason.
+pub trait RootUserSettings: Sized + DeserializeOwned {
+ fn parse_json(json: &str) -> (Option<Self>, ParseStatus);
+ fn parse_json_with_comments(json: &str) -> anyhow::Result<Self>;
+}
+
+impl RootUserSettings for SettingsContent {
+ fn parse_json(json: &str) -> (Option<Self>, ParseStatus) {
+ fallible_options::parse_json(json)
+ }
+ fn parse_json_with_comments(json: &str) -> anyhow::Result<Self> {
+ parse_json_with_comments(json)
+ }
+}
+// Explicit opt-in instead of blanket impl to avoid monomorphizing downstream. Just a hunch though.
+impl RootUserSettings for Option<SettingsContent> {
+ fn parse_json(json: &str) -> (Option<Self>, ParseStatus) {
+ fallible_options::parse_json(json)
+ }
+ fn parse_json_with_comments(json: &str) -> anyhow::Result<Self> {
+ parse_json_with_comments(json)
+ }
+}
+impl RootUserSettings for UserSettingsContent {
+ fn parse_json(json: &str) -> (Option<Self>, ParseStatus) {
+ fallible_options::parse_json(json)
+ }
+ fn parse_json_with_comments(json: &str) -> anyhow::Result<Self> {
+ parse_json_with_comments(json)
+ }
+}
+
#[with_fallible_options]
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, JsonSchema, MergeFrom)]
pub struct UserSettingsContent {
@@ -194,33 +240,6 @@ pub struct ExtensionsSettingsContent {
pub all_languages: AllLanguageSettingsContent,
}
-impl UserSettingsContent {
- pub fn for_release_channel(&self) -> Option<&SettingsContent> {
- match *release_channel::RELEASE_CHANNEL {
- ReleaseChannel::Dev => self.dev.as_deref(),
- ReleaseChannel::Nightly => self.nightly.as_deref(),
- ReleaseChannel::Preview => self.preview.as_deref(),
- ReleaseChannel::Stable => self.stable.as_deref(),
- }
- }
-
- pub fn for_os(&self) -> Option<&SettingsContent> {
- match env::consts::OS {
- "macos" => self.macos.as_deref(),
- "linux" => self.linux.as_deref(),
- "windows" => self.windows.as_deref(),
- _ => None,
- }
- }
-
- pub fn for_profile(&self, cx: &App) -> Option<&SettingsContent> {
- let Some(active_profile) = cx.try_global::<ActiveSettingsProfileName>() else {
- return None;
- };
- self.profiles.get(&active_profile.0)
- }
-}
-
/// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
///
/// Default: VSCode
@@ -964,14 +983,14 @@ pub struct RemoteSettingsContent {
Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom, Hash,
)]
pub struct DevContainerConnection {
- pub name: SharedString,
- pub container_id: SharedString,
+ pub name: String,
+ pub container_id: String,
}
#[with_fallible_options]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom)]
pub struct SshConnection {
- pub host: SharedString,
+ pub host: String,
pub username: Option<String>,
pub port: Option<u16>,
#[serde(default)]
@@ -994,7 +1013,7 @@ pub struct SshConnection {
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Debug)]
pub struct WslConnection {
- pub distro_name: SharedString,
+ pub distro_name: String,
pub user: Option<String>,
#[serde(default)]
pub projects: BTreeSet<RemoteProject>,
@@ -1,12 +1,11 @@
use std::path::PathBuf;
use collections::HashMap;
-use gpui::{AbsoluteLength, FontFeatures, FontWeight, SharedString, px};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings_macros::{MergeFrom, with_fallible_options};
-use crate::{FontFamilyName, FontSize};
+use crate::{FontFamilyName, FontFeaturesContent, FontSize, FontWeightContent};
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
pub struct ProjectTerminalSettingsContent {
@@ -93,9 +92,9 @@ pub struct TerminalSettingsContent {
///
/// Default: comfortable
pub line_height: Option<TerminalLineHeight>,
- pub font_features: Option<FontFeatures>,
+ pub font_features: Option<FontFeaturesContent>,
/// Sets the terminal's font weight in CSS weight units 0-900.
- pub font_weight: Option<FontWeight>,
+ pub font_weight: Option<FontWeightContent>,
/// Default cursor shape for the terminal.
/// Can be "bar", "block", "underline", or "hollow".
///
@@ -202,7 +201,7 @@ pub enum Shell {
/// The arguments to pass to the program.
args: Vec<String>,
/// An optional string to override the title of the terminal tab
- title_override: Option<SharedString>,
+ title_override: Option<String>,
},
}
@@ -259,13 +258,12 @@ pub enum TerminalLineHeight {
}
impl TerminalLineHeight {
- pub fn value(&self) -> AbsoluteLength {
- let value = match self {
+ pub fn value(&self) -> f32 {
+ match self {
TerminalLineHeight::Comfortable => 1.618,
TerminalLineHeight::Standard => 1.3,
TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
- };
- px(value).into()
+ }
}
}
@@ -489,25 +487,20 @@ pub enum ActivateScript {
mod test {
use serde_json::json;
- use crate::{ProjectSettingsContent, Shell, UserSettingsContent};
+ use crate::{ProjectSettingsContent, Shell};
#[test]
+ #[ignore]
fn test_project_settings() {
let project_content =
json!({"terminal": {"shell": {"program": "/bin/project"}}, "option_as_meta": true});
- let user_content =
+ let _user_content =
json!({"terminal": {"shell": {"program": "/bin/user"}}, "option_as_meta": false});
- let user_settings = serde_json::from_value::<UserSettingsContent>(user_content).unwrap();
let project_settings =
serde_json::from_value::<ProjectSettingsContent>(project_content).unwrap();
- assert_eq!(
- user_settings.content.terminal.unwrap().project.shell,
- Some(Shell::Program("/bin/user".to_owned()))
- );
- assert_eq!(user_settings.content.project.terminal, None);
assert_eq!(
project_settings.terminal.unwrap().shell,
Some(Shell::Program("/bin/project".to_owned()))
@@ -1,14 +1,116 @@
use collections::{HashMap, IndexMap};
-use gpui::{FontFallbacks, FontFeatures, FontStyle, FontWeight, Pixels, SharedString};
-use schemars::{JsonSchema, JsonSchema_repr};
+use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
-use serde_repr::{Deserialize_repr, Serialize_repr};
use settings_macros::{MergeFrom, with_fallible_options};
-use std::{fmt::Display, sync::Arc};
+use std::{borrow::Cow, fmt::Display, sync::Arc};
use crate::serialize_f32_with_two_decimal_places;
+/// OpenType font features as a map of feature tag to value.
+/// This is a content type that mirrors `gpui::FontFeatures` but without the Arc wrapper.
+/// Values can be specified as booleans (true=1, false=0) or integers.
+#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, MergeFrom)]
+#[serde(transparent)]
+pub struct FontFeaturesContent(pub IndexMap<String, u32>);
+
+impl FontFeaturesContent {
+ pub fn new() -> Self {
+ Self(IndexMap::default())
+ }
+}
+
+#[derive(Debug, serde::Deserialize)]
+#[serde(untagged)]
+enum FeatureValue {
+ Bool(bool),
+ Number(serde_json::Number),
+}
+
+fn is_valid_feature_tag(tag: &str) -> bool {
+ tag.len() == 4 && tag.chars().all(|c| c.is_ascii_alphanumeric())
+}
+
+impl<'de> Deserialize<'de> for FontFeaturesContent {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ use serde::de::{MapAccess, Visitor};
+ use std::fmt;
+
+ struct FontFeaturesVisitor;
+
+ impl<'de> Visitor<'de> for FontFeaturesVisitor {
+ type Value = FontFeaturesContent;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a map of font features")
+ }
+
+ fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
+ where
+ M: MapAccess<'de>,
+ {
+ let mut feature_map = IndexMap::default();
+
+ while let Some((key, value)) =
+ access.next_entry::<String, Option<FeatureValue>>()?
+ {
+ if !is_valid_feature_tag(&key) {
+ log::error!("Incorrect font feature tag: {}", key);
+ continue;
+ }
+ if let Some(value) = value {
+ match value {
+ FeatureValue::Bool(enable) => {
+ feature_map.insert(key, if enable { 1 } else { 0 });
+ }
+ FeatureValue::Number(value) => {
+ if value.is_u64() {
+ feature_map.insert(key, value.as_u64().unwrap() as u32);
+ } else {
+ log::error!(
+ "Incorrect font feature value {} for feature tag {}",
+ value,
+ key
+ );
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ Ok(FontFeaturesContent(feature_map))
+ }
+ }
+
+ deserializer.deserialize_map(FontFeaturesVisitor)
+ }
+}
+
+impl JsonSchema for FontFeaturesContent {
+ fn schema_name() -> Cow<'static, str> {
+ "FontFeaturesContent".into()
+ }
+
+ fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ use schemars::json_schema;
+ json_schema!({
+ "type": "object",
+ "patternProperties": {
+ "[0-9a-zA-Z]{4}$": {
+ "type": ["boolean", "integer"],
+ "minimum": 0,
+ "multipleOf": 1
+ }
+ },
+ "additionalProperties": false
+ })
+ }
+}
+
/// Settings for rendering text in UI and text buffers.
#[with_fallible_options]
@@ -24,10 +126,10 @@ pub struct ThemeSettingsContent {
pub ui_font_fallbacks: Option<Vec<FontFamilyName>>,
/// The OpenType features to enable for text in the UI.
#[schemars(default = "default_font_features")]
- pub ui_font_features: Option<FontFeatures>,
+ pub ui_font_features: Option<FontFeaturesContent>,
/// The weight of the UI font in CSS units from 100 to 900.
#[schemars(default = "default_buffer_font_weight")]
- pub ui_font_weight: Option<FontWeight>,
+ pub ui_font_weight: Option<FontWeightContent>,
/// The name of a font to use for rendering in text buffers.
pub buffer_font_family: Option<FontFamilyName>,
/// The font fallbacks to use for rendering in text buffers.
@@ -37,12 +139,12 @@ pub struct ThemeSettingsContent {
pub buffer_font_size: Option<FontSize>,
/// The weight of the editor font in CSS units from 100 to 900.
#[schemars(default = "default_buffer_font_weight")]
- pub buffer_font_weight: Option<FontWeight>,
+ pub buffer_font_weight: Option<FontWeightContent>,
/// The buffer's line height.
pub buffer_line_height: Option<BufferLineHeight>,
/// The OpenType features to enable for rendering in text buffers.
#[schemars(default = "default_font_features")]
- pub buffer_font_features: Option<FontFeatures>,
+ pub buffer_font_features: Option<FontFeaturesContent>,
/// The font size for agent responses in the agent panel. Falls back to the UI font size if unset.
pub agent_ui_font_size: Option<FontSize>,
/// The font size for user messages in the agent panel.
@@ -103,18 +205,6 @@ impl From<f32> for FontSize {
}
}
-impl From<FontSize> for Pixels {
- fn from(value: FontSize) -> Self {
- value.0.into()
- }
-}
-
-impl From<Pixels> for FontSize {
- fn from(value: Pixels) -> Self {
- Self(value.into())
- }
-}
-
#[derive(
Clone,
Copy,
@@ -142,16 +232,16 @@ impl From<f32> for CodeFade {
}
}
-fn default_font_features() -> Option<FontFeatures> {
- Some(FontFeatures::default())
+fn default_font_features() -> Option<FontFeaturesContent> {
+ Some(FontFeaturesContent::default())
}
-fn default_font_fallbacks() -> Option<FontFallbacks> {
- Some(FontFallbacks::default())
+fn default_font_fallbacks() -> Option<Vec<FontFamilyName>> {
+ Some(Vec::new())
}
-fn default_buffer_font_weight() -> Option<FontWeight> {
- Some(FontWeight::default())
+fn default_buffer_font_weight() -> Option<FontWeightContent> {
+ Some(FontWeightContent::NORMAL)
}
/// Represents the selection of a theme, which can be either static or dynamic.
@@ -312,18 +402,6 @@ impl AsRef<str> for FontFamilyName {
}
}
-impl From<SharedString> for FontFamilyName {
- fn from(value: SharedString) -> Self {
- Self(Arc::from(value))
- }
-}
-
-impl From<FontFamilyName> for SharedString {
- fn from(value: FontFamilyName) -> Self {
- SharedString::new(value.0)
- }
-}
-
impl From<String> for FontFamilyName {
fn from(value: String) -> Self {
Self(Arc::from(value))
@@ -401,7 +479,7 @@ pub struct ThemeStyleContent {
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
-pub struct AccentContent(pub Option<SharedString>);
+pub struct AccentContent(pub Option<String>);
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
pub struct PlayerColorContent {
@@ -1184,16 +1262,6 @@ pub enum WindowBackgroundContent {
Blurred,
}
-impl Into<gpui::WindowBackgroundAppearance> for WindowBackgroundContent {
- fn into(self) -> gpui::WindowBackgroundAppearance {
- match self {
- WindowBackgroundContent::Opaque => gpui::WindowBackgroundAppearance::Opaque,
- WindowBackgroundContent::Transparent => gpui::WindowBackgroundAppearance::Transparent,
- WindowBackgroundContent::Blurred => gpui::WindowBackgroundAppearance::Blurred,
- }
- }
-}
-
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum FontStyleContent {
@@ -1202,45 +1270,42 @@ pub enum FontStyleContent {
Oblique,
}
-impl From<FontStyleContent> for FontStyle {
- fn from(value: FontStyleContent) -> Self {
- match value {
- FontStyleContent::Normal => FontStyle::Normal,
- FontStyleContent::Italic => FontStyle::Italic,
- FontStyleContent::Oblique => FontStyle::Oblique,
- }
+#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize, Deserialize, MergeFrom)]
+#[serde(transparent)]
+pub struct FontWeightContent(pub f32);
+
+impl Default for FontWeightContent {
+ fn default() -> Self {
+ Self::NORMAL
}
}
-#[derive(
- Debug, Clone, Copy, Serialize_repr, Deserialize_repr, JsonSchema_repr, PartialEq, MergeFrom,
-)]
-#[repr(u16)]
-pub enum FontWeightContent {
- Thin = 100,
- ExtraLight = 200,
- Light = 300,
- Normal = 400,
- Medium = 500,
- Semibold = 600,
- Bold = 700,
- ExtraBold = 800,
- Black = 900,
+impl FontWeightContent {
+ pub const THIN: FontWeightContent = FontWeightContent(100.0);
+ pub const EXTRA_LIGHT: FontWeightContent = FontWeightContent(200.0);
+ pub const LIGHT: FontWeightContent = FontWeightContent(300.0);
+ pub const NORMAL: FontWeightContent = FontWeightContent(400.0);
+ pub const MEDIUM: FontWeightContent = FontWeightContent(500.0);
+ pub const SEMIBOLD: FontWeightContent = FontWeightContent(600.0);
+ pub const BOLD: FontWeightContent = FontWeightContent(700.0);
+ pub const EXTRA_BOLD: FontWeightContent = FontWeightContent(800.0);
+ pub const BLACK: FontWeightContent = FontWeightContent(900.0);
}
-impl From<FontWeightContent> for FontWeight {
- fn from(value: FontWeightContent) -> Self {
- match value {
- FontWeightContent::Thin => FontWeight::THIN,
- FontWeightContent::ExtraLight => FontWeight::EXTRA_LIGHT,
- FontWeightContent::Light => FontWeight::LIGHT,
- FontWeightContent::Normal => FontWeight::NORMAL,
- FontWeightContent::Medium => FontWeight::MEDIUM,
- FontWeightContent::Semibold => FontWeight::SEMIBOLD,
- FontWeightContent::Bold => FontWeight::BOLD,
- FontWeightContent::ExtraBold => FontWeight::EXTRA_BOLD,
- FontWeightContent::Black => FontWeight::BLACK,
- }
+impl schemars::JsonSchema for FontWeightContent {
+ fn schema_name() -> std::borrow::Cow<'static, str> {
+ "FontWeightContent".into()
+ }
+
+ fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ use schemars::json_schema;
+ json_schema!({
+ "type": "number",
+ "minimum": Self::THIN.0,
+ "maximum": Self::BLACK.0,
+ "default": Self::NORMAL.0,
+ "description": "Font weight value between 100 (thin) and 900 (black)"
+ })
}
}
@@ -1312,27 +1377,27 @@ mod tests {
let default_value = &buffer_font_weight["default"];
assert_eq!(
default_value.as_f64(),
- Some(FontWeight::NORMAL.0 as f64),
- "buffer_font_weight default should be 400.0 (FontWeight::NORMAL)"
+ Some(FontWeightContent::NORMAL.0 as f64),
+ "buffer_font_weight default should be 400.0 (FontWeightContent::NORMAL)"
);
let defs = &schema_value["$defs"];
- let font_weight_def = &defs["FontWeight"];
+ let font_weight_def = &defs["FontWeightContent"];
assert_eq!(
font_weight_def["minimum"].as_f64(),
- Some(FontWeight::THIN.0 as f64),
- "FontWeight should have minimum of 100.0"
+ Some(FontWeightContent::THIN.0 as f64),
+ "FontWeightContent should have minimum of 100.0"
);
assert_eq!(
font_weight_def["maximum"].as_f64(),
- Some(FontWeight::BLACK.0 as f64),
- "FontWeight should have maximum of 900.0"
+ Some(FontWeightContent::BLACK.0 as f64),
+ "FontWeightContent should have maximum of 900.0"
);
assert_eq!(
font_weight_def["default"].as_f64(),
- Some(FontWeight::NORMAL.0 as f64),
- "FontWeight should have default of 400.0"
+ Some(FontWeightContent::NORMAL.0 as f64),
+ "FontWeightContent should have default of 400.0"
);
}
}
@@ -14,11 +14,6 @@ const DEFAULT_STRING: String = String::new();
/// to avoid the "NO DEFAULT" case.
const DEFAULT_EMPTY_STRING: Option<&String> = Some(&DEFAULT_STRING);
-const DEFAULT_SHARED_STRING: SharedString = SharedString::new_static("");
-/// A default empty string reference. Useful in `pick` functions for cases either in dynamic item fields, or when dealing with `settings::Maybe`
-/// to avoid the "NO DEFAULT" case.
-const DEFAULT_EMPTY_SHARED_STRING: Option<&SharedString> = Some(&DEFAULT_SHARED_STRING);
-
macro_rules! concat_sections {
(@vec, $($arr:expr),+ $(,)?) => {{
let total_len = 0_usize $(+ $arr.len())+;
@@ -5667,7 +5662,7 @@ fn terminal_page() -> SettingsPage {
pick: |settings_content| {
match settings_content.terminal.as_ref()?.project.shell.as_ref() {
Some(settings::Shell::WithArguments { title_override, .. }) => {
- title_override.as_ref().or(DEFAULT_EMPTY_SHARED_STRING)
+ title_override.as_ref().or(DEFAULT_EMPTY_STRING)
}
_ => None,
}
@@ -7187,7 +7182,11 @@ fn language_settings_field<T>(
) -> Option<&T> {
let all_languages = &settings_content.project.all_languages;
if let Some(current_language_name) = current_language() {
- if let Some(current_language) = all_languages.languages.0.get(¤t_language_name) {
+ if let Some(current_language) = all_languages
+ .languages
+ .0
+ .get(current_language_name.as_ref())
+ {
let value = get(current_language);
if value.is_some() {
return value;
@@ -7208,7 +7207,7 @@ fn language_settings_field_mut<T>(
all_languages
.languages
.0
- .entry(current_language)
+ .entry(current_language.to_string())
.or_default()
} else {
&mut all_languages.defaults
@@ -15,7 +15,9 @@ use project::{Project, WorktreeId};
use release_channel::ReleaseChannel;
use schemars::JsonSchema;
use serde::Deserialize;
-use settings::{Settings, SettingsContent, SettingsStore, initial_project_settings_content};
+use settings::{
+ IntoGpui, Settings, SettingsContent, SettingsStore, initial_project_settings_content,
+};
use std::{
any::{Any, TypeId, type_name},
cell::RefCell,
@@ -3794,12 +3796,12 @@ fn render_font_picker(
.get_value_from_file(file.to_settings(), field.pick)
.1
.cloned()
- .unwrap_or_else(|| SharedString::default().into());
+ .map_or_else(|| SharedString::default(), |value| value.into_gpui());
PopoverMenu::new("font-picker")
.trigger(render_picker_trigger_button(
"font_family_picker_trigger".into(),
- current_value.clone().into(),
+ current_value.clone(),
))
.menu(move |window, cx| {
let file = file.clone();
@@ -3807,14 +3809,14 @@ fn render_font_picker(
Some(cx.new(move |cx| {
font_picker(
- current_value.clone().into(),
+ current_value,
move |font_name, cx| {
update_settings_file(
file.clone(),
field.json_path,
cx,
move |settings, _cx| {
- (field.write)(settings, Some(font_name.into()));
+ (field.write)(settings, Some(font_name.to_string().into()));
},
)
.log_err(); // todo(settings_ui) don't log err
@@ -9,8 +9,9 @@ use serde::{Deserialize, Serialize};
pub use settings::AlternateScroll;
use settings::{
- PathHyperlinkRegex, RegisterSetting, ShowScrollbar, TerminalBlink, TerminalDockPosition,
- TerminalLineHeight, VenvSettings, WorkingDirectory, merge_from::MergeFrom,
+ IntoGpui, PathHyperlinkRegex, RegisterSetting, ShowScrollbar, TerminalBlink,
+ TerminalDockPosition, TerminalLineHeight, VenvSettings, WorkingDirectory,
+ merge_from::MergeFrom,
};
use task::Shell;
use theme::FontFamilyName;
@@ -70,7 +71,7 @@ fn settings_shell_to_task_shell(shell: settings::Shell) -> Shell {
} => Shell::WithArguments {
program,
args,
- title_override: title_override.map(Into::into),
+ title_override,
},
}
}
@@ -84,7 +85,7 @@ impl settings::Settings for TerminalSettings {
TerminalSettings {
shell: settings_shell_to_task_shell(project_content.shell.unwrap()),
working_directory: project_content.working_directory.unwrap(),
- font_size: user_content.font_size.map(Into::into),
+ font_size: user_content.font_size.map(|s| s.into_gpui()),
font_family: user_content.font_family,
font_fallbacks: user_content.font_fallbacks.map(|fallbacks| {
FontFallbacks::from_fonts(
@@ -94,8 +95,8 @@ impl settings::Settings for TerminalSettings {
.collect(),
)
}),
- font_features: user_content.font_features,
- font_weight: user_content.font_weight,
+ font_features: user_content.font_features.map(|f| f.into_gpui()),
+ font_weight: user_content.font_weight.map(|w| w.into_gpui()),
line_height: user_content.line_height.unwrap(),
env: project_content.env.unwrap(),
cursor_shape: user_content.cursor_shape.unwrap().into(),
@@ -844,11 +844,8 @@ impl Element for TerminalElement {
} => {
let rem_size = window.rem_size();
let line_height = f32::from(window.text_style().font_size.to_pixels(rem_size))
- * TerminalSettings::get_global(cx)
- .line_height
- .value()
- .to_pixels(rem_size);
- (displayed_lines * line_height).into()
+ * TerminalSettings::get_global(cx).line_height.value();
+ px(displayed_lines as f32 * line_height).into()
}
ContentMode::Scrollable => {
if let TerminalMode::Embedded { .. } = &self.mode {
@@ -956,7 +953,7 @@ impl Element for TerminalElement {
font_fallbacks,
font_size: font_size.into(),
font_style: FontStyle::Normal,
- line_height: line_height.into(),
+ line_height: px(line_height).into(),
background_color: Some(theme.colors().terminal_ansi_background),
white_space: WhiteSpace::Normal,
// These are going to be overridden per-cell
@@ -971,8 +968,7 @@ impl Element for TerminalElement {
let (dimensions, line_height_px) = {
let rem_size = window.rem_size();
let font_pixels = text_style.font_size.to_pixels(rem_size);
- // TODO: line_height should be an f32 not an AbsoluteLength.
- let line_height = f32::from(font_pixels) * line_height.to_pixels(rem_size);
+ let line_height = f32::from(font_pixels) * line_height;
let font_id = cx.text_system().resolve_font(&text_style.font());
let cell_width = text_system
@@ -995,7 +991,7 @@ impl Element for TerminalElement {
origin.x += gutter;
(
- TerminalBounds::new(line_height, cell_width, Bounds { origin, size }),
+ TerminalBounds::new(px(line_height), cell_width, Bounds { origin, size }),
line_height,
)
};
@@ -1106,9 +1102,10 @@ impl Element for TerminalElement {
// internal line number (which can be negative in Scrollable mode for
// scrollback history).
let rows_above_viewport =
- ((intersection.top() - bounds.top()).max(px(0.)) / line_height_px) as usize;
+ f32::from((intersection.top() - bounds.top()).max(px(0.)) / line_height_px)
+ as usize;
let visible_row_count =
- (intersection.size.height / line_height_px).ceil() as usize + 1;
+ f32::from((intersection.size.height / line_height_px).ceil()) as usize + 1;
TerminalElement::layout_grid(
// Group cells by line and filter to only the visible screen rows.
@@ -1,9 +1,10 @@
#![allow(missing_docs)]
-use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla};
+use gpui::{HighlightStyle, Hsla};
use palette::FromColor;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
+use settings::IntoGpui;
pub use settings::{FontWeightContent, WindowBackgroundContent};
use crate::{StatusColorsRefinement, ThemeColorsRefinement};
@@ -63,8 +64,8 @@ pub fn syntax_overrides(this: &settings::ThemeStyleContent) -> Vec<(String, High
.background_color
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
- font_style: style.font_style.map(FontStyle::from),
- font_weight: style.font_weight.map(FontWeight::from),
+ font_style: style.font_style.map(|s| s.into_gpui()),
+ font_weight: style.font_weight.map(|w| w.into_gpui()),
..Default::default()
},
)
@@ -5,14 +5,13 @@ use crate::{
use collections::HashMap;
use derive_more::{Deref, DerefMut};
use gpui::{
- App, Context, Font, FontFallbacks, FontStyle, FontWeight, Global, Pixels, Subscription, Window,
- px,
+ App, Context, Font, FontFallbacks, FontStyle, Global, Pixels, Subscription, Window, px,
};
use refineable::Refineable;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub use settings::{FontFamilyName, IconThemeName, ThemeAppearanceMode, ThemeName};
-use settings::{RegisterSetting, Settings, SettingsContent};
+use settings::{IntoGpui, RegisterSetting, Settings, SettingsContent};
use std::sync::Arc;
const MIN_FONT_SIZE: Pixels = px(6.0);
@@ -557,7 +556,8 @@ impl ThemeSettings {
fn modify_theme(base_theme: &mut Theme, theme_overrides: &settings::ThemeStyleContent) {
if let Some(window_background_appearance) = theme_overrides.window_background_appearance {
- base_theme.styles.window_background_appearance = window_background_appearance.into();
+ base_theme.styles.window_background_appearance =
+ window_background_appearance.into_gpui();
}
let status_color_refinement = status_colors_refinement(&theme_overrides.status);
@@ -686,12 +686,7 @@ pub fn clamp_font_size(size: Pixels) -> Pixels {
size.clamp(MIN_FONT_SIZE, MAX_FONT_SIZE)
}
-fn clamp_font_weight(weight: f32) -> FontWeight {
- FontWeight(weight.clamp(100., 950.))
-}
-
-/// font fallback from settings
-pub fn font_fallbacks_from_settings(
+fn font_fallbacks_from_settings(
fallbacks: Option<Vec<settings::FontFamilyName>>,
) -> Option<FontFallbacks> {
fallbacks.map(|fallbacks| {
@@ -710,12 +705,12 @@ impl settings::Settings for ThemeSettings {
let theme_selection: ThemeSelection = content.theme.clone().unwrap().into();
let icon_theme_selection: IconThemeSelection = content.icon_theme.clone().unwrap().into();
Self {
- ui_font_size: clamp_font_size(content.ui_font_size.unwrap().into()),
+ ui_font_size: clamp_font_size(content.ui_font_size.unwrap().into_gpui()),
ui_font: Font {
family: content.ui_font_family.as_ref().unwrap().0.clone().into(),
- features: content.ui_font_features.clone().unwrap(),
+ features: content.ui_font_features.clone().unwrap().into_gpui(),
fallbacks: font_fallbacks_from_settings(content.ui_font_fallbacks.clone()),
- weight: clamp_font_weight(content.ui_font_weight.unwrap().0),
+ weight: content.ui_font_weight.unwrap().into_gpui(),
style: Default::default(),
},
buffer_font: Font {
@@ -726,15 +721,15 @@ impl settings::Settings for ThemeSettings {
.0
.clone()
.into(),
- features: content.buffer_font_features.clone().unwrap(),
+ features: content.buffer_font_features.clone().unwrap().into_gpui(),
fallbacks: font_fallbacks_from_settings(content.buffer_font_fallbacks.clone()),
- weight: clamp_font_weight(content.buffer_font_weight.unwrap().0),
+ weight: content.buffer_font_weight.unwrap().into_gpui(),
style: FontStyle::default(),
},
- buffer_font_size: clamp_font_size(content.buffer_font_size.unwrap().into()),
+ buffer_font_size: clamp_font_size(content.buffer_font_size.unwrap().into_gpui()),
buffer_line_height: content.buffer_line_height.unwrap().into(),
- agent_ui_font_size: content.agent_ui_font_size.map(Into::into),
- agent_buffer_font_size: content.agent_buffer_font_size.map(Into::into),
+ agent_ui_font_size: content.agent_ui_font_size.map(|s| s.into_gpui()),
+ agent_buffer_font_size: content.agent_buffer_font_size.map(|s| s.into_gpui()),
theme: theme_selection,
experimental_theme_overrides: content.experimental_theme_overrides.clone(),
theme_overrides: content.theme_overrides.clone(),
@@ -23,6 +23,7 @@ use std::path::Path;
use std::sync::Arc;
use ::settings::DEFAULT_DARK_THEME;
+use ::settings::IntoGpui;
use ::settings::Settings;
use ::settings::SettingsStore;
use anyhow::Result;
@@ -273,8 +274,8 @@ impl ThemeFamily {
.background_color
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
- font_style: highlight.font_style.map(Into::into),
- font_weight: highlight.font_weight.map(Into::into),
+ font_style: highlight.font_style.map(|s| s.into_gpui()),
+ font_weight: highlight.font_weight.map(|w| w.into_gpui()),
..Default::default()
},
)
@@ -285,7 +286,7 @@ impl ThemeFamily {
let window_background_appearance = theme
.style
.window_background_appearance
- .map(Into::into)
+ .map(|w| w.into_gpui())
.unwrap_or_default();
Theme {
@@ -13,7 +13,7 @@ use super::ZedSyntaxToken;
pub(crate) fn try_parse_font_weight(font_style: &str) -> Option<FontWeightContent> {
match font_style {
- style if style.contains("bold") => Some(FontWeightContent::Bold),
+ style if style.contains("bold") => Some(FontWeightContent::BOLD),
_ => None,
}
}
@@ -717,7 +717,7 @@ mod test {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings(cx, |settings| {
settings.project.all_languages.languages.0.insert(
- LanguageName::new_static("Rust").0,
+ LanguageName::new_static("Rust").0.to_string(),
LanguageSettingsContent {
auto_indent_on_paste: Some(false),
..Default::default()
@@ -922,7 +922,7 @@ fn register_actions(
let _ = settings
.theme
.ui_font_size
- .insert(theme::clamp_font_size(ui_font_size).into());
+ .insert(f32::from(theme::clamp_font_size(ui_font_size)).into());
});
} else {
theme::adjust_ui_font_size(cx, |size| size + px(1.0));
@@ -938,7 +938,7 @@ fn register_actions(
let _ = settings
.theme
.ui_font_size
- .insert(theme::clamp_font_size(ui_font_size).into());
+ .insert(f32::from(theme::clamp_font_size(ui_font_size)).into());
});
} else {
theme::adjust_ui_font_size(cx, |size| size - px(1.0));
@@ -967,7 +967,7 @@ fn register_actions(
let _ = settings
.theme
.buffer_font_size
- .insert(theme::clamp_font_size(buffer_font_size).into());
+ .insert(f32::from(theme::clamp_font_size(buffer_font_size)).into());
});
} else {
theme::adjust_buffer_font_size(cx, |size| size + px(1.0));
@@ -984,7 +984,7 @@ fn register_actions(
let _ = settings
.theme
.buffer_font_size
- .insert(theme::clamp_font_size(buffer_font_size).into());
+ .insert(f32::from(theme::clamp_font_size(buffer_font_size)).into());
});
} else {
theme::adjust_buffer_font_size(cx, |size| size - px(1.0));