Detailed changes
@@ -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",
@@ -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" }
@@ -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
@@ -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"
@@ -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"
@@ -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"
@@ -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"] }
@@ -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"] }
@@ -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]
@@ -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 }
@@ -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();
@@ -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())
@@ -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();
@@ -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())
@@ -25,7 +25,10 @@ pub fn marked_display_snapshot(
) -> (DisplaySnapshot, Vec<DisplayPoint>) {
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())
@@ -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" }
+workspace = { path = "../workspace" }
@@ -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"
@@ -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"
@@ -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
@@ -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(),
)
@@ -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<str>,
+ font_features: Features,
font_ids: Vec<FontId>,
}
@@ -58,17 +59,21 @@ impl FontCache {
.map(|family| family.name.clone())
}
- pub fn load_family(&self, names: &[&str]) -> Result<FamilyId> {
+ pub fn load_family(&self, names: &[&str], features: &Features) -> Result<FamilyId> {
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);
}
}
@@ -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<bool>,
+ pub case: Option<bool>,
+ pub cpsp: Option<bool>,
+ pub frac: Option<bool>,
+ pub liga: Option<bool>,
+ pub onum: Option<bool>,
+ pub ordn: Option<bool>,
+ pub pnum: Option<bool>,
+ pub ss01: Option<bool>,
+ pub ss02: Option<bool>,
+ pub ss03: Option<bool>,
+ pub ss04: Option<bool>,
+ pub ss05: Option<bool>,
+ pub ss06: Option<bool>,
+ pub ss07: Option<bool>,
+ pub ss08: Option<bool>,
+ pub ss09: Option<bool>,
+ pub ss10: Option<bool>,
+ pub ss11: Option<bool>,
+ pub ss12: Option<bool>,
+ pub ss13: Option<bool>,
+ pub ss14: Option<bool>,
+ pub ss15: Option<bool>,
+ pub ss16: Option<bool>,
+ pub ss17: Option<bool>,
+ pub ss18: Option<bool>,
+ pub ss19: Option<bool>,
+ pub ss20: Option<bool>,
+ pub subs: Option<bool>,
+ pub sups: Option<bool>,
+ pub swsh: Option<bool>,
+ pub titl: Option<bool>,
+ pub tnum: Option<bool>,
+ pub zero: Option<bool>,
+}
+
#[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<WeightJson>,
size: f32,
#[serde(default)]
@@ -107,12 +148,13 @@ impl TextStyle {
font_family_name: impl Into<Arc<str>>,
font_size: f32,
font_properties: Properties,
+ font_features: Features,
underline: Underline,
color: Color,
font_cache: &FontCache,
) -> Result<Self> {
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();
@@ -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<Vec<u8>>]) -> anyhow::Result<()>;
- fn load_family(&self, name: &str) -> anyhow::Result<Vec<FontId>>;
+ fn load_family(&self, name: &str, features: &FontFeatures) -> anyhow::Result<Vec<FontId>>;
fn select_font(
&self,
font_ids: &[FontId],
@@ -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<Vec<FontId>> {
- self.0.write().load_family(name)
+ fn load_family(&self, name: &str, features: &Features) -> anyhow::Result<Vec<FontId>> {
+ self.0.write().load_family(name, features)
}
fn select_font(&self, font_ids: &[FontId], properties: &Properties) -> anyhow::Result<FontId> {
@@ -126,7 +128,7 @@ impl FontSystemState {
Ok(())
}
- fn load_family(&mut self, name: &str) -> anyhow::Result<Vec<FontId>> {
+ fn load_family(&mut self, name: &str, features: &Features) -> anyhow::Result<Vec<FontId>> {
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(),
@@ -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<bool>,
+ 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;
+}
@@ -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,
@@ -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"] }
@@ -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"] }
@@ -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]
@@ -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"
@@ -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" }
@@ -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"
@@ -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"
@@ -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"
@@ -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"
@@ -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"
@@ -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"
@@ -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<WorkingDirectory>,
pub font_size: Option<f32>,
pub font_family: Option<String>,
+ pub font_features: Option<fonts::Features>,
pub env: Option<HashMap<String, String>>,
pub blinking: Option<TerminalBlink>,
pub alternate_scroll: Option<AlternateScroll>,
@@ -332,6 +335,8 @@ pub struct SettingsFileContent {
#[serde(default)]
pub buffer_font_size: Option<f32>,
#[serde(default)]
+ pub buffer_font_features: Option<fonts::Features>,
+ #[serde(default)]
pub active_pane_magnification: Option<f32>,
#[serde(default)]
pub cursor_blink: Option<bool>,
@@ -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.,
@@ -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"
@@ -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"] }
@@ -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
@@ -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"
@@ -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<Theme> = 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> = theme.into();
self.themes.lock().insert(name.to_string(), theme.clone());
Ok(theme)
}
@@ -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"] }
@@ -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"] }
+db = { path = "../db", features = ["test-support"] }
@@ -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"
@@ -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",
]
@@ -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]
@@ -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(