feat(font): add run iterator, shared grid set and config integration
Completes the font engine port: - shaper/run.rs ports run.zig: the RunIterator over terminal rows that splits text runs at style/face boundaries, selection and cursor, handles graphemes and ligatures; Shaper::shape_run shapes a run. - shared_grid_set.rs ports SharedGridSet.zig: grids keyed by a config derived key, handed out as Arc<SharedGrid>. - Font-related types now have one canonical definition in ghostty-config (Variation, Descriptor, CodepointMap, metric modifiers, freetype load flags, synthetic style, shaping break) which the font crate re-exports. MetricModifier::apply_i32 now saturates to -i32::MAX like Zig. - The 23 terminal-driven harfbuzz shaper tests are ported, printing via Screen until Terminal lands. Test Plan: - cargo test -p ghostty-font (157 passed, 3 ignored - also skipped in Zig); Devanagari/Chakma/Bengali tests return early without system fonts - cargo test -p ghostty-config (255 passed)
This commit is contained in:
1 parent
b64a2886ec
commit
7b408257c4
19 files changed
+3204
-554
No files matched your search
@@ -3,89 +3,16 @@
|
||||
//! font, the codepoint is rendered using the default font.
|
||||
//! Port of `font/CodepointMap.zig`.
|
||||
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
|
||||
use crate::discovery::Descriptor;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entry {
|
||||
/// Unicode codepoint range (inclusive). Asserts range.0 <= range.1.
|
||||
pub range: (u32, u32),
|
||||
|
||||
/// The discovery descriptor of the font to use for this range.
|
||||
pub descriptor: Descriptor,
|
||||
}
|
||||
|
||||
/// The list of entries.
|
||||
///
|
||||
/// Note: we just do a linear search because we expect to always have very
|
||||
/// few entries, so the overhead of a binary search is not worth it. This is
|
||||
/// possible to defeat with some pathological inputs, but there is no realistic
|
||||
/// scenario where this will be a problem except people trying to fuck around.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CodepointMap {
|
||||
pub list: Vec<Entry>,
|
||||
}
|
||||
|
||||
impl CodepointMap {
|
||||
/// Add an entry to the map.
|
||||
///
|
||||
/// For conflicting codepoints, entries added later take priority over
|
||||
/// entries added earlier.
|
||||
pub fn add(&mut self, entry: Entry) {
|
||||
assert!(entry.range.0 <= entry.range.1);
|
||||
self.list.push(entry);
|
||||
}
|
||||
|
||||
/// Get a descriptor for a codepoint.
|
||||
pub fn get(&self, cp: u32) -> Option<&Descriptor> {
|
||||
self.list
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|e| e.range.0 <= cp && cp <= e.range.1)
|
||||
.map(|e| &e.descriptor)
|
||||
}
|
||||
|
||||
/// Hash with the given hasher.
|
||||
pub fn hash_into<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.list.len().hash(hasher);
|
||||
for entry in &self.list {
|
||||
entry.range.hash(hasher);
|
||||
hash_descriptor(&entry.descriptor, hasher);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a hash code that can be used to uniquely identify this
|
||||
/// action.
|
||||
pub fn hashcode(&self) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
self.hash_into(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Mirrors `Descriptor.hash` of the Zig discovery module (same fields, same
|
||||
/// float handling) without depending on how the discovery module exposes it.
|
||||
fn hash_descriptor<H: Hasher>(d: &Descriptor, hasher: &mut H) {
|
||||
d.family.hash(hasher);
|
||||
d.style.hash(hasher);
|
||||
d.codepoint.hash(hasher);
|
||||
d.size.to_bits().hash(hasher);
|
||||
d.bold.hash(hasher);
|
||||
d.italic.hash(hasher);
|
||||
d.monospace.hash(hasher);
|
||||
d.variations.len().hash(hasher);
|
||||
for v in &d.variations {
|
||||
v.id.hash(hasher);
|
||||
// This is not correct, but we don't currently depend on the
|
||||
// hash value being different based on decimal values of variations.
|
||||
(v.value as i64).hash(hasher);
|
||||
}
|
||||
}
|
||||
//!
|
||||
//! The canonical definition lives in `ghostty-config` (the `font-codepoint-map`
|
||||
//! option parses straight into it); it is re-exported here.
|
||||
|
||||
pub use ghostty_config::font_types::CodepointMap;
|
||||
pub use ghostty_config::font_types::CodepointMapEntry as Entry;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ghostty_config::font_types::Descriptor;
|
||||
|
||||
fn desc(family: &str) -> Descriptor {
|
||||
Descriptor {
|
||||
@@ -96,7 +23,7 @@ mod tests {
|
||||
|
||||
fn entry(a: u32, b: u32, family: &str) -> Entry {
|
||||
Entry {
|
||||
range: (a, b),
|
||||
range: [a, b],
|
||||
descriptor: desc(family),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user