diff --git a/Cargo.lock b/Cargo.lock index b111ab7190..ca99be0218 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -518,6 +518,7 @@ dependencies = [ "menu", "project", "serde", + "serde_derive", "serde_json", "settings", "smol", @@ -1097,6 +1098,7 @@ dependencies = [ "ipc-channel", "plist", "serde", + "serde_derive", ] [[package]] @@ -1119,6 +1121,7 @@ dependencies = [ "rand 0.8.5", "rpc", "serde", + "serde_derive", "settings", "smol", "sum_tree", @@ -1228,6 +1231,7 @@ dependencies = [ "sea-orm", "sea-query", "serde", + "serde_derive", "serde_json", "settings", "sha-1 0.9.8", @@ -1269,6 +1273,7 @@ dependencies = [ "postage", "project", "serde", + "serde_derive", "settings", "theme", "util", @@ -1739,6 +1744,7 @@ dependencies = [ "log", "parking_lot 0.11.2", "serde", + "serde_derive", "smol", "sqlez", "sqlez_macros", @@ -1947,6 +1953,7 @@ dependencies = [ "rand 0.8.5", "rpc", "serde", + "serde_derive", "settings", "smallvec", "smol", @@ -2100,6 +2107,7 @@ dependencies = [ "project", "search", "serde", + "serde_derive", "settings", "sysinfo", "theme", @@ -2296,6 +2304,7 @@ dependencies = [ "regex", "rope", "serde", + "serde_derive", "serde_json", "smol", "tempfile", @@ -2664,8 +2673,10 @@ dependencies = [ "postage", "rand 0.8.5", "resvg", + "schemars", "seahash", "serde", + "serde_derive", "serde_json", "simplelog", "smallvec", @@ -3275,6 +3286,7 @@ dependencies = [ "regex", "rpc", "serde", + "serde_derive", "serde_json", "settings", "similar", @@ -3470,6 +3482,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "serde", + "serde_derive", "serde_json", "sha2 0.10.6", "simplelog", @@ -3490,6 +3503,7 @@ dependencies = [ "prost-types 0.8.0", "reqwest", "serde", + "serde_derive", "sha2 0.10.6", ] @@ -3530,6 +3544,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "serde", + "serde_derive", "serde_json", "smol", "unindent", @@ -4449,6 +4464,7 @@ dependencies = [ "bincode", "plugin_macros", "serde", + "serde_derive", ] [[package]] @@ -4459,6 +4475,7 @@ dependencies = [ "proc-macro2", "quote", "serde", + "serde_derive", "syn", ] @@ -4470,6 +4487,7 @@ dependencies = [ "bincode", "pollster", "serde", + "serde_derive", "serde_json", "smol", "wasi-common", @@ -4627,6 +4645,7 @@ dependencies = [ "regex", "rpc", "serde", + "serde_derive", "serde_json", "settings", "sha2 0.10.6", @@ -5249,6 +5268,7 @@ dependencies = [ "rand 0.8.5", "rsa", "serde", + "serde_derive", "smol", "smol-timeout", "tempdir", @@ -5672,6 +5692,7 @@ dependencies = [ "postage", "project", "serde", + "serde_derive", "serde_json", "settings", "smallvec", @@ -5860,6 +5881,7 @@ dependencies = [ "postage", "schemars", "serde", + "serde_derive", "serde_json", "serde_path_to_error", "sqlez", @@ -6480,6 +6502,7 @@ dependencies = [ "procinfo", "rand 0.8.5", "serde", + "serde_derive", "settings", "shellexpand", "smallvec", @@ -6511,6 +6534,7 @@ dependencies = [ "project", "rand 0.8.5", "serde", + "serde_derive", "settings", "shellexpand", "smallvec", @@ -6570,6 +6594,7 @@ dependencies = [ "indexmap", "parking_lot 0.11.2", "serde", + "serde_derive", "serde_json", "serde_path_to_error", "toml", @@ -7563,6 +7588,7 @@ dependencies = [ "project", "search", "serde", + "serde_derive", "serde_json", "settings", "tokio", @@ -8346,6 +8372,7 @@ dependencies = [ "postage", "project", "serde", + "serde_derive", "serde_json", "settings", "smallvec", @@ -8472,6 +8499,7 @@ dependencies = [ "rust-embed", "search", "serde", + "serde_derive", "serde_json", "serde_path_to_error", "settings", diff --git a/Cargo.toml b/Cargo.toml index ab8bcd6de8..fd713d4242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ resolver = "2" [workspace.dependencies] serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } rand = { version = "0.8" } diff --git a/assets/settings/default.json b/assets/settings/default.json index add664f956..4a45449f2d 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -3,6 +3,11 @@ "theme": "One Dark", // The name of a font to use for rendering text in the editor "buffer_font_family": "Zed Mono", + // The OpenType features to enable for text in the editor. + "buffer_font_features": { + // Disable ligatures: + // "calt": false + }, // The default font size for text in the editor "buffer_font_size": 15, // The factor to grow the active pane by. Defaults to 1.0 diff --git a/crates/auto_update/Cargo.toml b/crates/auto_update/Cargo.toml index 5f672e759f..8edb1957af 100644 --- a/crates/auto_update/Cargo.toml +++ b/crates/auto_update/Cargo.toml @@ -23,6 +23,7 @@ isahc = "1.7" lazy_static = "1.4" log = "0.4" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order"] } smol = "1.2.5" tempdir = "0.3.7" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index f2bab22ea7..bf2e583d2c 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -18,6 +18,7 @@ clap = { version = "3.1", features = ["derive"] } dirs = "3.0" ipc-channel = "0.16" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9" diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 347424d34e..6ff66b8ecc 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -35,7 +35,8 @@ time = { version = "0.3", features = ["serde", "serde-well-known"] } tiny_http = "0.8" uuid = { version = "1.1.2", features = ["v4"] } url = "2.2" -serde = { version = "*", features = ["derive"] } +serde = { version = "*", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } settings = { path = "../settings" } tempfile = "3" diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 873ff8aa44..c25fc28769 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -42,6 +42,7 @@ scrypt = "0.7" sea-orm = { git = "https://github.com/zed-industries/sea-orm", rev = "18f4c691085712ad014a51792af75a9044bacee6", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls"] } sea-query = "0.27" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = "1.0" sha-1 = "0.9" sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] } diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 2afeb8ad8a..9b5ca9c1ea 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -44,6 +44,7 @@ futures = "0.3" log = "0.4" postage = { version = "0.4.1", features = ["futures-traits"] } serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } [dev-dependencies] call = { path = "../call", features = ["test-support"] } diff --git a/crates/db/Cargo.toml b/crates/db/Cargo.toml index 496e61b811..16ec37019a 100644 --- a/crates/db/Cargo.toml +++ b/crates/db/Cargo.toml @@ -24,6 +24,7 @@ lazy_static = "1.4.0" log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" serde = { version = "1.0", features = ["derive"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } smol = "1.2" [dev-dependencies] diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 6cb7ef32ec..6c52ec70e9 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -54,6 +54,7 @@ parking_lot = "0.11" postage = { version = "0.4", features = ["futures-traits"] } rand = { version = "0.8.3", optional = true } serde = { workspace = true } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } smallvec = { version = "1.6", features = ["union"] } smol = "1.2" tree-sitter-rust = { version = "*", optional = true } diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 500f3626e3..32810b4450 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -785,7 +785,9 @@ pub mod tests { let mut tab_size = rng.gen_range(1..=4); let buffer_start_excerpt_header_height = rng.gen_range(1..=5); let excerpt_header_height = rng.gen_range(1..=5); - let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let family_id = font_cache + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); @@ -1042,7 +1044,9 @@ pub mod tests { let font_cache = cx.font_cache(); - let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let family_id = font_cache + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); @@ -1131,7 +1135,10 @@ pub mod tests { cx.set_global(Settings::test(cx)); let text = sample_text(6, 6, 'a'); let buffer = MultiBuffer::build_simple(&text, cx); - let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let family_id = cx + .font_cache() + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = cx .font_cache() .select_font(family_id, &Default::default()) @@ -1214,7 +1221,9 @@ pub mod tests { let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let font_cache = cx.font_cache(); - let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let family_id = font_cache + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); @@ -1302,7 +1311,9 @@ pub mod tests { let font_cache = cx.font_cache(); - let family_id = font_cache.load_family(&["Courier"]).unwrap(); + let family_id = font_cache + .load_family(&["Courier"], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); @@ -1374,7 +1385,9 @@ pub mod tests { let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx)); let font_cache = cx.font_cache(); - let family_id = font_cache.load_family(&["Courier"]).unwrap(); + let family_id = font_cache + .load_family(&["Courier"], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); @@ -1490,7 +1503,9 @@ pub mod tests { let text = "✅\t\tα\nβ\t\n🏀β\t\tγ"; let buffer = MultiBuffer::build_simple(text, cx); let font_cache = cx.font_cache(); - let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let family_id = font_cache + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); @@ -1548,7 +1563,9 @@ pub mod tests { cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx); let font_cache = cx.font_cache(); - let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let family_id = font_cache + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 797f41c52a..fffe20bb21 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -1015,7 +1015,10 @@ mod tests { fn test_basic_blocks(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); - let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let family_id = cx + .font_cache() + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = cx .font_cache() .select_font(family_id, &Default::default()) @@ -1185,7 +1188,10 @@ mod tests { fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); - let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let family_id = cx + .font_cache() + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = cx .font_cache() .select_font(family_id, &Default::default()) @@ -1241,7 +1247,10 @@ mod tests { Some(rng.gen_range(0.0..=100.0)) }; let tab_size = 1.try_into().unwrap(); - let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let family_id = cx + .font_cache() + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = cx .font_cache() .select_font(family_id, &Default::default()) diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index 6c34fa4797..0d5fb878e5 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -1053,7 +1053,9 @@ mod tests { Some(rng.gen_range(0.0..=1000.0)) }; let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap(); - let family_id = font_cache.load_family(&["Helvetica"]).unwrap(); + let family_id = font_cache + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(family_id, &Default::default()) .unwrap(); diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 0ede6186da..3e5f896564 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -587,7 +587,10 @@ mod tests { #[gpui::test] fn test_move_up_and_down_with_excerpts(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); - let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let family_id = cx + .font_cache() + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = cx .font_cache() .select_font(family_id, &Default::default()) diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 48652c44b7..9d0c1c7f1a 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -25,7 +25,10 @@ pub fn marked_display_snapshot( ) -> (DisplaySnapshot, Vec) { let (unmarked_text, markers) = marked_text_offsets(text); - let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap(); + let family_id = cx + .font_cache() + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = cx .font_cache() .select_font(family_id, &Default::default()) diff --git a/crates/feedback/Cargo.toml b/crates/feedback/Cargo.toml index b224f91a2f..51f070dd1d 100644 --- a/crates/feedback/Cargo.toml +++ b/crates/feedback/Cargo.toml @@ -25,10 +25,11 @@ postage = { version = "0.4", features = ["futures-traits"] } project = { path = "../project" } search = { path = "../search" } serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } settings = { path = "../settings" } sysinfo = "0.27.1" theme = { path = "../theme" } tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" } urlencoding = "2.1.2" util = { path = "../util" } -workspace = { path = "../workspace" } \ No newline at end of file +workspace = { path = "../workspace" } diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index cd6c8f969c..66708943f9 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -24,6 +24,7 @@ smol = "1.2.5" regex = "1.5" git2 = { version = "0.15", default-features = false } serde = { workspace = true } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { workspace = true } log = { version = "0.4.16", features = ["kv_unstable_serde"] } libc = "0.2" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 7be254be4d..65d36753e7 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -39,8 +39,10 @@ pathfinder_geometry = "0.5" postage = { version = "0.4.1", features = ["futures-traits"] } rand = "0.8.3" resvg = "0.14" +schemars = "0.8" seahash = "4.1" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = "1.0" smallvec = { version = "1.6", features = ["union"] } smol = "1.2" diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 4d84fefab6..e16ac440fd 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -56,7 +56,10 @@ impl gpui::Element for TextElement { cx: &mut gpui::PaintContext, ) -> Self::PaintState { let font_size = 12.; - let family = cx.font_cache.load_family(&["SF Pro Display"]).unwrap(); + let family = cx + .font_cache + .load_family(&["SF Pro Display"], &Default::default()) + .unwrap(); let normal = RunStyle { font_id: cx .font_cache diff --git a/crates/gpui/src/elements/label.rs b/crates/gpui/src/elements/label.rs index b7ac1938e3..aae00b07a8 100644 --- a/crates/gpui/src/elements/label.rs +++ b/crates/gpui/src/elements/label.rs @@ -216,6 +216,7 @@ mod tests { 12., Default::default(), Default::default(), + Default::default(), Color::black(), cx.font_cache(), ) @@ -225,6 +226,7 @@ mod tests { 12., *FontProperties::new().weight(Weight::BOLD), Default::default(), + Default::default(), Color::new(255, 0, 0, 255), cx.font_cache(), ) diff --git a/crates/gpui/src/font_cache.rs b/crates/gpui/src/font_cache.rs index 3631cbe724..4388ad4bcb 100644 --- a/crates/gpui/src/font_cache.rs +++ b/crates/gpui/src/font_cache.rs @@ -1,5 +1,5 @@ use crate::{ - fonts::{FontId, Metrics, Properties}, + fonts::{Features, FontId, Metrics, Properties}, geometry::vector::{vec2f, Vector2F}, platform, text_layout::LineWrapper, @@ -18,6 +18,7 @@ pub struct FamilyId(usize); struct Family { name: Arc, + font_features: Features, font_ids: Vec, } @@ -58,17 +59,21 @@ impl FontCache { .map(|family| family.name.clone()) } - pub fn load_family(&self, names: &[&str]) -> Result { + pub fn load_family(&self, names: &[&str], features: &Features) -> Result { for name in names { let state = self.0.upgradable_read(); - if let Some(ix) = state.families.iter().position(|f| f.name.as_ref() == *name) { + if let Some(ix) = state + .families + .iter() + .position(|f| f.name.as_ref() == *name && f.font_features == *features) + { return Ok(FamilyId(ix)); } let mut state = RwLockUpgradableReadGuard::upgrade(state); - if let Ok(font_ids) = state.fonts.load_family(name) { + if let Ok(font_ids) = state.fonts.load_family(name, features) { if font_ids.is_empty() { continue; } @@ -82,6 +87,7 @@ impl FontCache { state.families.push(Family { name: Arc::from(*name), + font_features: features.clone(), font_ids, }); return Ok(family_id); @@ -254,7 +260,15 @@ mod tests { fn test_select_font() { let platform = test::platform(); let fonts = FontCache::new(platform.fonts()); - let arial = fonts.load_family(&["Arial"]).unwrap(); + let arial = fonts + .load_family( + &["Arial"], + &Features { + calt: Some(false), + ..Default::default() + }, + ) + .unwrap(); let arial_regular = fonts.select_font(arial, &Properties::new()).unwrap(); let arial_italic = fonts .select_font(arial, Properties::new().style(Style::Italic)) @@ -265,5 +279,16 @@ mod tests { assert_ne!(arial_regular, arial_italic); assert_ne!(arial_regular, arial_bold); assert_ne!(arial_italic, arial_bold); + + let arial_with_calt = fonts + .load_family( + &["Arial"], + &Features { + calt: Some(true), + ..Default::default() + }, + ) + .unwrap(); + assert_ne!(arial_with_calt, arial); } } diff --git a/crates/gpui/src/fonts.rs b/crates/gpui/src/fonts.rs index 5c49ddcf61..e0f037acb8 100644 --- a/crates/gpui/src/fonts.rs +++ b/crates/gpui/src/fonts.rs @@ -11,7 +11,8 @@ pub use font_kit::{ properties::{Properties, Stretch, Style, Weight}, }; use ordered_float::OrderedFloat; -use serde::{de, Deserialize}; +use schemars::JsonSchema; +use serde::{de, Deserialize, Serialize}; use serde_json::Value; use std::{cell::RefCell, sync::Arc}; @@ -20,6 +21,44 @@ pub struct FontId(pub usize); pub type GlyphId = u32; +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct Features { + pub calt: Option, + pub case: Option, + pub cpsp: Option, + pub frac: Option, + pub liga: Option, + pub onum: Option, + pub ordn: Option, + pub pnum: Option, + pub ss01: Option, + pub ss02: Option, + pub ss03: Option, + pub ss04: Option, + pub ss05: Option, + pub ss06: Option, + pub ss07: Option, + pub ss08: Option, + pub ss09: Option, + pub ss10: Option, + pub ss11: Option, + pub ss12: Option, + pub ss13: Option, + pub ss14: Option, + pub ss15: Option, + pub ss16: Option, + pub ss17: Option, + pub ss18: Option, + pub ss19: Option, + pub ss20: Option, + pub subs: Option, + pub sups: Option, + pub swsh: Option, + pub titl: Option, + pub tnum: Option, + pub zero: Option, +} + #[derive(Clone, Debug)] pub struct TextStyle { pub color: Color, @@ -71,6 +110,8 @@ thread_local! { struct TextStyleJson { color: Color, family: String, + #[serde(default)] + features: Features, weight: Option, size: f32, #[serde(default)] @@ -107,12 +148,13 @@ impl TextStyle { font_family_name: impl Into>, font_size: f32, font_properties: Properties, + font_features: Features, underline: Underline, color: Color, font_cache: &FontCache, ) -> Result { let font_family_name = font_family_name.into(); - let font_family_id = font_cache.load_family(&[&font_family_name])?; + let font_family_id = font_cache.load_family(&[&font_family_name], &font_features)?; let font_id = font_cache.select_font(font_family_id, &font_properties)?; Ok(Self { color, @@ -175,6 +217,7 @@ impl TextStyle { json.family, json.size, font_properties, + json.features, underline_from_json(json.underline), json.color, font_cache, @@ -253,7 +296,9 @@ impl Default for TextStyle { .expect("TextStyle::default can only be called within a call to with_font_cache"); let font_family_name = Arc::from("Courier"); - let font_family_id = font_cache.load_family(&[&font_family_name]).unwrap(); + let font_family_id = font_cache + .load_family(&[&font_family_name], &Default::default()) + .unwrap(); let font_id = font_cache .select_font(font_family_id, &Default::default()) .unwrap(); diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 4c1ba49690..538b46ee77 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -9,7 +9,10 @@ pub mod current { use crate::{ executor, - fonts::{FontId, GlyphId, Metrics as FontMetrics, Properties as FontProperties}, + fonts::{ + Features as FontFeatures, FontId, GlyphId, Metrics as FontMetrics, + Properties as FontProperties, + }, geometry::{ rect::{RectF, RectI}, vector::Vector2F, @@ -335,7 +338,7 @@ pub enum RasterizationOptions { pub trait FontSystem: Send + Sync { fn add_fonts(&self, fonts: &[Arc>]) -> anyhow::Result<()>; - fn load_family(&self, name: &str) -> anyhow::Result>; + fn load_family(&self, name: &str, features: &FontFeatures) -> anyhow::Result>; fn select_font( &self, font_ids: &[FontId], diff --git a/crates/gpui/src/platform/mac/fonts.rs b/crates/gpui/src/platform/mac/fonts.rs index f39d548049..de1661b339 100644 --- a/crates/gpui/src/platform/mac/fonts.rs +++ b/crates/gpui/src/platform/mac/fonts.rs @@ -1,5 +1,7 @@ +mod open_type; + use crate::{ - fonts::{FontId, GlyphId, Metrics, Properties}, + fonts::{Features, FontId, GlyphId, Metrics, Properties}, geometry::{ rect::{RectF, RectI}, transform2d::Transform2F, @@ -64,8 +66,8 @@ impl platform::FontSystem for FontSystem { self.0.write().add_fonts(fonts) } - fn load_family(&self, name: &str) -> anyhow::Result> { - self.0.write().load_family(name) + fn load_family(&self, name: &str, features: &Features) -> anyhow::Result> { + self.0.write().load_family(name, features) } fn select_font(&self, font_ids: &[FontId], properties: &Properties) -> anyhow::Result { @@ -126,7 +128,7 @@ impl FontSystemState { Ok(()) } - fn load_family(&mut self, name: &str) -> anyhow::Result> { + fn load_family(&mut self, name: &str, features: &Features) -> anyhow::Result> { let mut font_ids = Vec::new(); let family = self @@ -134,7 +136,8 @@ impl FontSystemState { .select_family_by_name(name) .or_else(|_| self.system_source.select_family_by_name(name))?; for font in family.fonts() { - let font = font.load()?; + let mut font = font.load()?; + open_type::apply_features(&mut font, features); let font_id = FontId(self.fonts.len()); font_ids.push(font_id); let postscript_name = font.postscript_name().unwrap(); @@ -503,7 +506,7 @@ mod tests { fn test_layout_str(_: &mut MutableAppContext) { // This is failing intermittently on CI and we don't have time to figure it out let fonts = FontSystem::new(); - let menlo = fonts.load_family("Menlo").unwrap(); + let menlo = fonts.load_family("Menlo", &Default::default()).unwrap(); let menlo_regular = RunStyle { font_id: fonts.select_font(&menlo, &Properties::new()).unwrap(), color: Default::default(), @@ -544,13 +547,13 @@ mod tests { #[test] fn test_glyph_offsets() -> anyhow::Result<()> { let fonts = FontSystem::new(); - let zapfino = fonts.load_family("Zapfino")?; + let zapfino = fonts.load_family("Zapfino", &Default::default())?; let zapfino_regular = RunStyle { font_id: fonts.select_font(&zapfino, &Properties::new())?, color: Default::default(), underline: Default::default(), }; - let menlo = fonts.load_family("Menlo")?; + let menlo = fonts.load_family("Menlo", &Default::default())?; let menlo_regular = RunStyle { font_id: fonts.select_font(&menlo, &Properties::new())?, color: Default::default(), @@ -584,7 +587,7 @@ mod tests { use std::{fs::File, io::BufWriter, path::Path}; let fonts = FontSystem::new(); - let font_ids = fonts.load_family("Fira Code").unwrap(); + let font_ids = fonts.load_family("Fira Code", &Default::default()).unwrap(); let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap(); let glyph_id = fonts.glyph_for_char(font_id, 'G').unwrap(); @@ -618,7 +621,7 @@ mod tests { #[test] fn test_wrap_line() { let fonts = FontSystem::new(); - let font_ids = fonts.load_family("Helvetica").unwrap(); + let font_ids = fonts.load_family("Helvetica", &Default::default()).unwrap(); let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap(); let line = "one two three four five\n"; @@ -636,7 +639,7 @@ mod tests { #[test] fn test_layout_line_bom_char() { let fonts = FontSystem::new(); - let font_ids = fonts.load_family("Helvetica").unwrap(); + let font_ids = fonts.load_family("Helvetica", &Default::default()).unwrap(); let style = RunStyle { font_id: fonts.select_font(&font_ids, &Default::default()).unwrap(), color: Default::default(), diff --git a/crates/gpui/src/platform/mac/fonts/open_type.rs b/crates/gpui/src/platform/mac/fonts/open_type.rs new file mode 100644 index 0000000000..8ee10fdb41 --- /dev/null +++ b/crates/gpui/src/platform/mac/fonts/open_type.rs @@ -0,0 +1,395 @@ +#![allow(unused, non_upper_case_globals)] + +use std::ptr; + +use crate::fonts::Features; +use cocoa::appkit::CGFloat; +use core_foundation::{base::TCFType, number::CFNumber}; +use core_graphics::geometry::CGAffineTransform; +use core_text::{ + font::{CTFont, CTFontRef}, + font_descriptor::{ + CTFontDescriptor, CTFontDescriptorCreateCopyWithFeature, CTFontDescriptorRef, + }, +}; +use font_kit::font::Font; + +const kCaseSensitiveLayoutOffSelector: i32 = 1; +const kCaseSensitiveLayoutOnSelector: i32 = 0; +const kCaseSensitiveLayoutType: i32 = 33; +const kCaseSensitiveSpacingOffSelector: i32 = 3; +const kCaseSensitiveSpacingOnSelector: i32 = 2; +const kCharacterAlternativesType: i32 = 17; +const kCommonLigaturesOffSelector: i32 = 3; +const kCommonLigaturesOnSelector: i32 = 2; +const kContextualAlternatesOffSelector: i32 = 1; +const kContextualAlternatesOnSelector: i32 = 0; +const kContextualAlternatesType: i32 = 36; +const kContextualLigaturesOffSelector: i32 = 19; +const kContextualLigaturesOnSelector: i32 = 18; +const kContextualSwashAlternatesOffSelector: i32 = 5; +const kContextualSwashAlternatesOnSelector: i32 = 4; +const kDefaultLowerCaseSelector: i32 = 0; +const kDefaultUpperCaseSelector: i32 = 0; +const kDiagonalFractionsSelector: i32 = 2; +const kFractionsType: i32 = 11; +const kHistoricalLigaturesOffSelector: i32 = 21; +const kHistoricalLigaturesOnSelector: i32 = 20; +const kHojoCharactersSelector: i32 = 12; +const kInferiorsSelector: i32 = 2; +const kJIS2004CharactersSelector: i32 = 11; +const kLigaturesType: i32 = 1; +const kLowerCasePetiteCapsSelector: i32 = 2; +const kLowerCaseSmallCapsSelector: i32 = 1; +const kLowerCaseType: i32 = 37; +const kLowerCaseNumbersSelector: i32 = 0; +const kMathematicalGreekOffSelector: i32 = 11; +const kMathematicalGreekOnSelector: i32 = 10; +const kMonospacedNumbersSelector: i32 = 0; +const kNLCCharactersSelector: i32 = 13; +const kNoFractionsSelector: i32 = 0; +const kNormalPositionSelector: i32 = 0; +const kNoStyleOptionsSelector: i32 = 0; +const kNumberCaseType: i32 = 21; +const kNumberSpacingType: i32 = 6; +const kOrdinalsSelector: i32 = 3; +const kProportionalNumbersSelector: i32 = 1; +const kQuarterWidthTextSelector: i32 = 4; +const kScientificInferiorsSelector: i32 = 4; +const kSlashedZeroOffSelector: i32 = 5; +const kSlashedZeroOnSelector: i32 = 4; +const kStyleOptionsType: i32 = 19; +const kStylisticAltEighteenOffSelector: i32 = 37; +const kStylisticAltEighteenOnSelector: i32 = 36; +const kStylisticAltEightOffSelector: i32 = 17; +const kStylisticAltEightOnSelector: i32 = 16; +const kStylisticAltElevenOffSelector: i32 = 23; +const kStylisticAltElevenOnSelector: i32 = 22; +const kStylisticAlternativesType: i32 = 35; +const kStylisticAltFifteenOffSelector: i32 = 31; +const kStylisticAltFifteenOnSelector: i32 = 30; +const kStylisticAltFiveOffSelector: i32 = 11; +const kStylisticAltFiveOnSelector: i32 = 10; +const kStylisticAltFourOffSelector: i32 = 9; +const kStylisticAltFourOnSelector: i32 = 8; +const kStylisticAltFourteenOffSelector: i32 = 29; +const kStylisticAltFourteenOnSelector: i32 = 28; +const kStylisticAltNineOffSelector: i32 = 19; +const kStylisticAltNineOnSelector: i32 = 18; +const kStylisticAltNineteenOffSelector: i32 = 39; +const kStylisticAltNineteenOnSelector: i32 = 38; +const kStylisticAltOneOffSelector: i32 = 3; +const kStylisticAltOneOnSelector: i32 = 2; +const kStylisticAltSevenOffSelector: i32 = 15; +const kStylisticAltSevenOnSelector: i32 = 14; +const kStylisticAltSeventeenOffSelector: i32 = 35; +const kStylisticAltSeventeenOnSelector: i32 = 34; +const kStylisticAltSixOffSelector: i32 = 13; +const kStylisticAltSixOnSelector: i32 = 12; +const kStylisticAltSixteenOffSelector: i32 = 33; +const kStylisticAltSixteenOnSelector: i32 = 32; +const kStylisticAltTenOffSelector: i32 = 21; +const kStylisticAltTenOnSelector: i32 = 20; +const kStylisticAltThirteenOffSelector: i32 = 27; +const kStylisticAltThirteenOnSelector: i32 = 26; +const kStylisticAltThreeOffSelector: i32 = 7; +const kStylisticAltThreeOnSelector: i32 = 6; +const kStylisticAltTwelveOffSelector: i32 = 25; +const kStylisticAltTwelveOnSelector: i32 = 24; +const kStylisticAltTwentyOffSelector: i32 = 41; +const kStylisticAltTwentyOnSelector: i32 = 40; +const kStylisticAltTwoOffSelector: i32 = 5; +const kStylisticAltTwoOnSelector: i32 = 4; +const kSuperiorsSelector: i32 = 1; +const kSwashAlternatesOffSelector: i32 = 3; +const kSwashAlternatesOnSelector: i32 = 2; +const kTitlingCapsSelector: i32 = 4; +const kTypographicExtrasType: i32 = 14; +const kVerticalFractionsSelector: i32 = 1; +const kVerticalPositionType: i32 = 10; + +pub fn apply_features(font: &mut Font, features: &Features) { + // See https://chromium.googlesource.com/chromium/src/+/66.0.3359.158/third_party/harfbuzz-ng/src/hb-coretext.cc + // for a reference implementation. + toggle_open_type_feature( + font, + features.calt, + kContextualAlternatesType, + kContextualAlternatesOnSelector, + kContextualAlternatesOffSelector, + ); + toggle_open_type_feature( + font, + features.case, + kCaseSensitiveLayoutType, + kCaseSensitiveLayoutOnSelector, + kCaseSensitiveLayoutOffSelector, + ); + toggle_open_type_feature( + font, + features.cpsp, + kCaseSensitiveLayoutType, + kCaseSensitiveSpacingOnSelector, + kCaseSensitiveSpacingOffSelector, + ); + toggle_open_type_feature( + font, + features.frac, + kFractionsType, + kDiagonalFractionsSelector, + kNoFractionsSelector, + ); + toggle_open_type_feature( + font, + features.liga, + kLigaturesType, + kCommonLigaturesOnSelector, + kCommonLigaturesOffSelector, + ); + toggle_open_type_feature( + font, + features.onum, + kNumberCaseType, + kLowerCaseNumbersSelector, + 2, + ); + toggle_open_type_feature( + font, + features.ordn, + kVerticalPositionType, + kOrdinalsSelector, + kNormalPositionSelector, + ); + toggle_open_type_feature( + font, + features.pnum, + kNumberSpacingType, + kProportionalNumbersSelector, + 4, + ); + toggle_open_type_feature( + font, + features.ss01, + kStylisticAlternativesType, + kStylisticAltOneOnSelector, + kStylisticAltOneOffSelector, + ); + toggle_open_type_feature( + font, + features.ss02, + kStylisticAlternativesType, + kStylisticAltTwoOnSelector, + kStylisticAltTwoOffSelector, + ); + toggle_open_type_feature( + font, + features.ss03, + kStylisticAlternativesType, + kStylisticAltThreeOnSelector, + kStylisticAltThreeOffSelector, + ); + toggle_open_type_feature( + font, + features.ss04, + kStylisticAlternativesType, + kStylisticAltFourOnSelector, + kStylisticAltFourOffSelector, + ); + toggle_open_type_feature( + font, + features.ss05, + kStylisticAlternativesType, + kStylisticAltFiveOnSelector, + kStylisticAltFiveOffSelector, + ); + toggle_open_type_feature( + font, + features.ss06, + kStylisticAlternativesType, + kStylisticAltSixOnSelector, + kStylisticAltSixOffSelector, + ); + toggle_open_type_feature( + font, + features.ss07, + kStylisticAlternativesType, + kStylisticAltSevenOnSelector, + kStylisticAltSevenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss08, + kStylisticAlternativesType, + kStylisticAltEightOnSelector, + kStylisticAltEightOffSelector, + ); + toggle_open_type_feature( + font, + features.ss09, + kStylisticAlternativesType, + kStylisticAltNineOnSelector, + kStylisticAltNineOffSelector, + ); + toggle_open_type_feature( + font, + features.ss10, + kStylisticAlternativesType, + kStylisticAltTenOnSelector, + kStylisticAltTenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss11, + kStylisticAlternativesType, + kStylisticAltElevenOnSelector, + kStylisticAltElevenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss12, + kStylisticAlternativesType, + kStylisticAltTwelveOnSelector, + kStylisticAltTwelveOffSelector, + ); + toggle_open_type_feature( + font, + features.ss13, + kStylisticAlternativesType, + kStylisticAltThirteenOnSelector, + kStylisticAltThirteenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss14, + kStylisticAlternativesType, + kStylisticAltFourteenOnSelector, + kStylisticAltFourteenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss15, + kStylisticAlternativesType, + kStylisticAltFifteenOnSelector, + kStylisticAltFifteenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss16, + kStylisticAlternativesType, + kStylisticAltSixteenOnSelector, + kStylisticAltSixteenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss17, + kStylisticAlternativesType, + kStylisticAltSeventeenOnSelector, + kStylisticAltSeventeenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss18, + kStylisticAlternativesType, + kStylisticAltEighteenOnSelector, + kStylisticAltEighteenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss19, + kStylisticAlternativesType, + kStylisticAltNineteenOnSelector, + kStylisticAltNineteenOffSelector, + ); + toggle_open_type_feature( + font, + features.ss20, + kStylisticAlternativesType, + kStylisticAltTwentyOnSelector, + kStylisticAltTwentyOffSelector, + ); + toggle_open_type_feature( + font, + features.subs, + kVerticalPositionType, + kInferiorsSelector, + kNormalPositionSelector, + ); + toggle_open_type_feature( + font, + features.sups, + kVerticalPositionType, + kSuperiorsSelector, + kNormalPositionSelector, + ); + toggle_open_type_feature( + font, + features.swsh, + kContextualAlternatesType, + kSwashAlternatesOnSelector, + kSwashAlternatesOffSelector, + ); + toggle_open_type_feature( + font, + features.titl, + kStyleOptionsType, + kTitlingCapsSelector, + kNoStyleOptionsSelector, + ); + toggle_open_type_feature( + font, + features.tnum, + kNumberSpacingType, + kMonospacedNumbersSelector, + 4, + ); + toggle_open_type_feature( + font, + features.zero, + kTypographicExtrasType, + kSlashedZeroOnSelector, + kSlashedZeroOffSelector, + ); +} + +fn toggle_open_type_feature( + font: &mut Font, + enabled: Option, + type_identifier: i32, + on_selector_identifier: i32, + off_selector_identifier: i32, +) { + if let Some(enabled) = enabled { + let native_font = font.native_font(); + unsafe { + let selector_identifier = if enabled { + on_selector_identifier + } else { + off_selector_identifier + }; + let new_descriptor = CTFontDescriptorCreateCopyWithFeature( + native_font.copy_descriptor().as_concrete_TypeRef(), + CFNumber::from(type_identifier).as_concrete_TypeRef(), + CFNumber::from(selector_identifier).as_concrete_TypeRef(), + ); + let new_descriptor = CTFontDescriptor::wrap_under_create_rule(new_descriptor); + let new_font = CTFontCreateCopyWithAttributes( + font.native_font().as_concrete_TypeRef(), + 0.0, + ptr::null(), + new_descriptor.as_concrete_TypeRef(), + ); + let new_font = CTFont::wrap_under_create_rule(new_font); + *font = Font::from_native_font(new_font); + } + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTFontCreateCopyWithAttributes( + font: CTFontRef, + size: CGFloat, + matrix: *const CGAffineTransform, + attributes: CTFontDescriptorRef, + ) -> CTFontRef; +} diff --git a/crates/gpui/src/text_layout.rs b/crates/gpui/src/text_layout.rs index 8fd10a6018..72ea0c8919 100644 --- a/crates/gpui/src/text_layout.rs +++ b/crates/gpui/src/text_layout.rs @@ -663,7 +663,9 @@ mod tests { fn test_wrap_line(cx: &mut crate::MutableAppContext) { let font_cache = cx.font_cache().clone(); let font_system = cx.platform().fonts(); - let family = font_cache.load_family(&["Courier"]).unwrap(); + let family = font_cache + .load_family(&["Courier"], &Default::default()) + .unwrap(); let font_id = font_cache.select_font(family, &Default::default()).unwrap(); let mut wrapper = LineWrapper::new(font_id, 16., font_system); @@ -725,7 +727,9 @@ mod tests { let font_system = cx.platform().fonts(); let text_layout_cache = TextLayoutCache::new(font_system.clone()); - let family = font_cache.load_family(&["Helvetica"]).unwrap(); + let family = font_cache + .load_family(&["Helvetica"], &Default::default()) + .unwrap(); let font_id = font_cache.select_font(family, &Default::default()).unwrap(); let normal = RunStyle { font_id, diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 3e6561e471..26e2d8c53b 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -47,6 +47,7 @@ postage = { version = "0.4.1", features = ["futures-traits"] } rand = { version = "0.8.3", optional = true } regex = "1.5" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1", features = ["preserve_order"] } similar = "1.3" smallvec = { version = "1.6", features = ["union"] } diff --git a/crates/live_kit_client/Cargo.toml b/crates/live_kit_client/Cargo.toml index 0145737508..55645732b8 100644 --- a/crates/live_kit_client/Cargo.toml +++ b/crates/live_kit_client/Cargo.toml @@ -14,7 +14,7 @@ name = "test_app" [features] test-support = [ - "async-trait", + "async-trait", "collections/test-support", "gpui/test-support", "lazy_static", @@ -63,9 +63,11 @@ lazy_static = "1.4" objc = "0.2" parking_lot = "0.11.1" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } sha2 = "0.10" simplelog = "0.9" [build-dependencies] serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/crates/live_kit_server/Cargo.toml b/crates/live_kit_server/Cargo.toml index 17ee3cd62e..319a026456 100644 --- a/crates/live_kit_server/Cargo.toml +++ b/crates/live_kit_server/Cargo.toml @@ -20,6 +20,7 @@ prost = "0.8" prost-types = "0.8" reqwest = "0.11" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } sha2 = "0.10" [build-dependencies] diff --git a/crates/lsp/Cargo.toml b/crates/lsp/Cargo.toml index eb6e02aac9..f37b1c9a45 100644 --- a/crates/lsp/Cargo.toml +++ b/crates/lsp/Cargo.toml @@ -23,6 +23,7 @@ lsp-types = "0.91" parking_lot = "0.11" postage = { version = "0.4.1", features = ["futures-traits"] } serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["raw_value"] } smol = "1.2" diff --git a/crates/plugin/Cargo.toml b/crates/plugin/Cargo.toml index 7bf5510465..6b86b19fc8 100644 --- a/crates/plugin/Cargo.toml +++ b/crates/plugin/Cargo.toml @@ -6,5 +6,6 @@ publish = false [dependencies] serde = "1.0" +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } bincode = "1.3" plugin_macros = { path = "../plugin_macros" } diff --git a/crates/plugin_macros/Cargo.toml b/crates/plugin_macros/Cargo.toml index 32bfc6a01a..e661485373 100644 --- a/crates/plugin_macros/Cargo.toml +++ b/crates/plugin_macros/Cargo.toml @@ -12,4 +12,5 @@ syn = { version = "1.0", features = ["full", "extra-traits"] } quote = "1.0" proc-macro2 = "1.0" serde = "1.0" +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } bincode = "1.3" diff --git a/crates/plugin_runtime/Cargo.toml b/crates/plugin_runtime/Cargo.toml index b5cfb9514f..13efa10dc2 100644 --- a/crates/plugin_runtime/Cargo.toml +++ b/crates/plugin_runtime/Cargo.toml @@ -10,6 +10,7 @@ wasmtime-wasi = "0.38" wasi-common = "0.38" anyhow = { version = "1.0", features = ["std"] } serde = "1.0" +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = "1.0" bincode = "1.3" pollster = "0.2.5" diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 68dc57f614..89ed5563ab 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -49,6 +49,7 @@ pulldown-cmark = { version = "0.9.1", default-features = false } rand = "0.8.3" regex = "1.5" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order"] } sha2 = "0.10" similar = "1.3" diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index 25c2ce1ca7..ff71a2493e 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -27,6 +27,7 @@ prost = "0.8" rand = "0.8" rsa = "0.4" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } smol-timeout = "0.6" tracing = { version = "0.1.34", features = ["log"] } zstd = "0.11" diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index f36865a89b..85215c15b2 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -24,6 +24,7 @@ futures = "0.3" log = { version = "0.4.16", features = ["kv_unstable_serde"] } postage = { version = "0.4.1", features = ["futures-traits"] } serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } smallvec = { version = "1.6", features = ["union"] } smol = "1.2" diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index c1ec2a6210..1711bb335b 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -25,6 +25,7 @@ json_comments = "0.2" postage = { version = "0.4.1", features = ["futures-traits"] } schemars = "0.8" serde = { workspace = true } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { workspace = true } serde_path_to_error = "0.1.4" toml = "0.5" diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 3dc0769a4b..4566776a34 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -5,7 +5,7 @@ pub mod watched_json; use anyhow::{bail, Result}; use gpui::{ font_cache::{FamilyId, FontCache}, - AssetSource, + fonts, AssetSource, }; use schemars::{ gen::{SchemaGenerator, SchemaSettings}, @@ -28,6 +28,8 @@ pub use watched_json::watch_files; #[derive(Clone)] pub struct Settings { + pub buffer_font_family_name: String, + pub buffer_font_features: fonts::Features, pub buffer_font_family: FamilyId, pub default_buffer_font_size: f32, pub buffer_font_size: f32, @@ -225,6 +227,7 @@ pub struct TerminalSettings { pub working_directory: Option, pub font_size: Option, pub font_family: Option, + pub font_features: Option, pub env: Option>, pub blinking: Option, pub alternate_scroll: Option, @@ -332,6 +335,8 @@ pub struct SettingsFileContent { #[serde(default)] pub buffer_font_size: Option, #[serde(default)] + pub buffer_font_features: Option, + #[serde(default)] pub active_pane_magnification: Option, #[serde(default)] pub cursor_blink: Option, @@ -396,10 +401,16 @@ impl Settings { ) .unwrap(); + let buffer_font_features = defaults.buffer_font_features.unwrap(); Self { buffer_font_family: font_cache - .load_family(&[defaults.buffer_font_family.as_ref().unwrap()]) + .load_family( + &[defaults.buffer_font_family.as_ref().unwrap()], + &buffer_font_features, + ) .unwrap(), + buffer_font_family_name: defaults.buffer_font_family.unwrap(), + buffer_font_features, buffer_font_size: defaults.buffer_font_size.unwrap(), active_pane_magnification: defaults.active_pane_magnification.unwrap(), default_buffer_font_size: defaults.buffer_font_size.unwrap(), @@ -451,11 +462,24 @@ impl Settings { theme_registry: &ThemeRegistry, font_cache: &FontCache, ) { - if let Some(value) = &data.buffer_font_family { - if let Some(id) = font_cache.load_family(&[value]).log_err() { + let mut family_changed = false; + if let Some(value) = data.buffer_font_family { + self.buffer_font_family_name = value; + family_changed = true; + } + if let Some(value) = data.buffer_font_features { + self.buffer_font_features = value; + family_changed = true; + } + if family_changed { + if let Some(id) = font_cache + .load_family(&[&self.buffer_font_family_name], &self.buffer_font_features) + .log_err() + { self.buffer_font_family = id; } } + if let Some(value) = &data.theme { if let Some(theme) = theme_registry.get(value).log_err() { self.theme = theme; @@ -480,11 +504,6 @@ impl Settings { merge(&mut self.default_dock_anchor, data.default_dock_anchor); merge(&mut self.base_keymap, data.base_keymap); - // Ensure terminal font is loaded, so we can request it in terminal_element layout - if let Some(terminal_font) = &data.terminal.font_family { - font_cache.load_family(&[terminal_font]).log_err(); - } - self.editor_overrides = data.editor; self.git_overrides = data.git.unwrap_or_default(); self.journal_overrides = data.journal; @@ -616,7 +635,12 @@ impl Settings { #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &gpui::AppContext) -> Settings { Settings { - buffer_font_family: cx.font_cache().load_family(&["Monaco"]).unwrap(), + buffer_font_family_name: "Monaco".to_string(), + buffer_font_features: Default::default(), + buffer_font_family: cx + .font_cache() + .load_family(&["Monaco"], &Default::default()) + .unwrap(), buffer_font_size: 14., active_pane_magnification: 1., default_buffer_font_size: 14., diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index c6f33a0fc7..56a8a3c452 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -30,6 +30,7 @@ anyhow = "1" thiserror = "1.0" lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } [dev-dependencies] rand = "0.8.5" diff --git a/crates/terminal_view/Cargo.toml b/crates/terminal_view/Cargo.toml index f12e4be03b..1e5b9d6070 100644 --- a/crates/terminal_view/Cargo.toml +++ b/crates/terminal_view/Cargo.toml @@ -34,6 +34,7 @@ anyhow = "1" thiserror = "1.0" lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 5d03d6304e..d530a98582 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -505,13 +505,22 @@ impl TerminalElement { ///Configures a text style from the current settings. pub fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle { - // Pull the font family from settings properly overriding - let family_id = settings + let font_family_name = settings .terminal_overrides .font_family .as_ref() .or(settings.terminal_defaults.font_family.as_ref()) - .and_then(|family_name| font_cache.load_family(&[family_name]).log_err()) + .unwrap_or(&settings.buffer_font_family_name); + let font_features = settings + .terminal_overrides + .font_features + .as_ref() + .or(settings.terminal_defaults.font_features.as_ref()) + .unwrap_or(&settings.buffer_font_features); + + let family_id = font_cache + .load_family(&[font_family_name], &font_features) + .log_err() .unwrap_or(settings.buffer_font_family); let font_size = settings diff --git a/crates/theme/Cargo.toml b/crates/theme/Cargo.toml index 1e9883860f..a0ef4ad9f8 100644 --- a/crates/theme/Cargo.toml +++ b/crates/theme/Cargo.toml @@ -14,6 +14,7 @@ anyhow = "1.0.38" indexmap = "1.6.2" parking_lot = "0.11.1" serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order"] } serde_path_to_error = "0.1.4" toml = "0.5" diff --git a/crates/theme/src/theme_registry.rs b/crates/theme/src/theme_registry.rs index d47625289b..a82ede59f7 100644 --- a/crates/theme/src/theme_registry.rs +++ b/crates/theme/src/theme_registry.rs @@ -2,6 +2,7 @@ use crate::{Theme, ThemeMeta}; use anyhow::{Context, Result}; use gpui::{fonts, AssetSource, FontCache}; use parking_lot::Mutex; +use serde::Deserialize; use serde_json::Value; use std::{collections::HashMap, sync::Arc}; @@ -56,12 +57,16 @@ impl ThemeRegistry { .with_context(|| format!("failed to load theme file {}", asset_path))?; // Allocate into the heap directly, the Theme struct is too large to fit in the stack. - let mut theme: Arc = fonts::with_font_cache(self.font_cache.clone(), || { - serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_slice(&theme_json)) + let mut theme = fonts::with_font_cache(self.font_cache.clone(), || { + let mut theme = Box::new(Theme::default()); + let mut deserializer = serde_json::Deserializer::from_slice(&theme_json); + let result = Theme::deserialize_in_place(&mut deserializer, &mut theme); + result.map(|_| theme) })?; // Reset name to be the file path, so that we can use it to access the stored themes - Arc::get_mut(&mut theme).unwrap().meta.name = name.into(); + theme.meta.name = name.into(); + let theme: Arc = theme.into(); self.themes.lock().insert(name.to_string(), theme.clone()); Ok(theme) } diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index bd94e48b9e..dd79d56d8f 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -13,6 +13,7 @@ neovim = ["nvim-rs", "async-compat", "async-trait", "tokio"] [dependencies] serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } itertools = "0.10" log = { version = "0.4.16", features = ["kv_unstable_serde"] } diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index a143e1a240..06b20684c6 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -45,6 +45,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" postage = { version = "0.4.1", features = ["futures-traits"] } serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order"] } smallvec = { version = "1.6", features = ["union"] } indoc = "1.0.4" @@ -57,4 +58,4 @@ gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } fs = { path = "../fs", features = ["test-support"] } -db = { path = "../db", features = ["test-support"] } \ No newline at end of file +db = { path = "../db", features = ["test-support"] } diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 6f0227d748..6a2422f87c 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -88,6 +88,7 @@ regex = "1.5" rsa = "0.4" rust-embed = { version = "6.3", features = ["include-exclude"] } serde = { version = "1.0", features = ["derive", "rc"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = { version = "1.0", features = ["preserve_order"] } serde_path_to_error = "0.1.4" simplelog = "0.9" diff --git a/plugins/Cargo.lock b/plugins/Cargo.lock index 3a302d301b..1e51dd8085 100644 --- a/plugins/Cargo.lock +++ b/plugins/Cargo.lock @@ -23,6 +23,7 @@ version = "0.1.0" dependencies = [ "plugin", "serde", + "serde_derive", "serde_json", ] @@ -33,6 +34,7 @@ dependencies = [ "bincode", "plugin_macros", "serde", + "serde_derive", ] [[package]] @@ -43,6 +45,7 @@ dependencies = [ "proc-macro2", "quote", "serde", + "serde_derive", "syn", ] diff --git a/plugins/json_language/Cargo.toml b/plugins/json_language/Cargo.toml index 7e01b9793f..effbf2ed8a 100644 --- a/plugins/json_language/Cargo.toml +++ b/plugins/json_language/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] plugin = { path = "../../crates/plugin" } serde = { version = "1.0", features = ["derive"] } +serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_json = "1.0" [lib] diff --git a/styles/src/styleTree/components.ts b/styles/src/styleTree/components.ts index 3f69df981e..aa185a0cf7 100644 --- a/styles/src/styleTree/components.ts +++ b/styles/src/styleTree/components.ts @@ -97,7 +97,79 @@ export interface TextProperties { size?: keyof typeof fontSizes weight?: FontWeight underline?: boolean - color?: string + color?: string, + features?: FontFeatures, +} + +interface FontFeatures { + /** Contextual Alternates: Applies a second substitution feature based on a match of a character pattern within a context of surrounding patterns */ + calt?: boolean; + /** Case-Sensitive Forms: Shifts various punctuation marks up to a position that works better with all-capital sequences */ + case?: boolean; + /** Capital Spacing: Adjusts inter-glyph spacing for all-capital text */ + cpsp?: boolean; + /** Fractions: Replaces figures separated by a slash with diagonal fractions */ + frac?: boolean; + /** Standard Ligatures: Replaces a sequence of glyphs with a single glyph which is preferred for typographic purposes */ + liga?: boolean; + /** Oldstyle Figures: Changes selected figures from the default or lining style to oldstyle form. */ + onum?: boolean; + /** Ordinals: Replaces default alphabetic glyphs with the corresponding ordinal forms for use after figures */ + ordn?: boolean; + /** Proportional Figures: Replaces figure glyphs set on uniform (tabular) widths with corresponding glyphs set on proportional widths */ + pnum?: boolean; + /** Stylistic set 01 */ + ss01?: boolean; + /** Stylistic set 02 */ + ss02?: boolean; + /** Stylistic set 03 */ + ss03?: boolean; + /** Stylistic set 04 */ + ss04?: boolean; + /** Stylistic set 05 */ + ss05?: boolean; + /** Stylistic set 06 */ + ss06?: boolean; + /** Stylistic set 07 */ + ss07?: boolean; + /** Stylistic set 08 */ + ss08?: boolean; + /** Stylistic set 09 */ + ss09?: boolean; + /** Stylistic set 10 */ + ss10?: boolean; + /** Stylistic set 11 */ + ss11?: boolean; + /** Stylistic set 12 */ + ss12?: boolean; + /** Stylistic set 13 */ + ss13?: boolean; + /** Stylistic set 14 */ + ss14?: boolean; + /** Stylistic set 15 */ + ss15?: boolean; + /** Stylistic set 16 */ + ss16?: boolean; + /** Stylistic set 17 */ + ss17?: boolean; + /** Stylistic set 18 */ + ss18?: boolean; + /** Stylistic set 19 */ + ss19?: boolean; + /** Stylistic set 20 */ + ss20?: boolean; + /** Subscript: Replaces default glyphs with subscript glyphs */ + subs?: boolean; + /** Superscript: Replaces default glyphs with superscript glyphs */ + sups?: boolean; + /** Swash: Replaces default glyphs with swash glyphs for stylistic purposes */ + swsh?: boolean; + /** Titling: Replaces default glyphs with titling glyphs for use in large-size settings */ + titl?: boolean; + /** Tabular Figures: Replaces figure glyphs set on proportional widths with corresponding glyphs set on uniform (tabular) widths */ + tnum?: boolean; + /** Slashed Zero: Replaces default zero with a slashed zero for better distinction between "0" and "O" */ + zero?: boolean; } export function text(