refactor: address clippy warnings and improve code quality (#409)

- Fix string formatting with modern interpolation syntax
- Improve Debug implementation with finish_non_exhaustive()
- Fix function placement in shape.rs to avoid items_after_statements warning
- Use more idiomatic Rust patterns (map_or_else, next_back)
- Clean up conditional imports in vi.rs
- Convert multiple methods to `const` functions for optimization and consistency
- Introduce `core_maths` for enhanced no-std compatibility
- Update `Cargo.toml` for the new optional dependency and feature adjustments
This commit is contained in:
romanstingler 2025-08-11 21:58:59 +02:00 committed by GitHub
parent e80dbc3607
commit a2f1f4b2a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 282 additions and 299 deletions

View file

@ -11,6 +11,7 @@ rust-version = "1.75"
[dependencies] [dependencies]
bitflags = "2.4.1" bitflags = "2.4.1"
core_maths = { version = "0.1.1", optional = true }
cosmic_undo_2 = { version = "0.2.0", optional = true } cosmic_undo_2 = { version = "0.2.0", optional = true }
fontdb = { version = "0.23", default-features = false } fontdb = { version = "0.23", default-features = false }
hashbrown = { version = "0.14.1", optional = true, default-features = false } hashbrown = { version = "0.14.1", optional = true, default-features = false }
@ -48,7 +49,7 @@ optional = true
default = ["std", "swash", "fontconfig"] default = ["std", "swash", "fontconfig"]
fontconfig = ["fontdb/fontconfig", "std"] fontconfig = ["fontdb/fontconfig", "std"]
monospace_fallback = [] monospace_fallback = []
no_std = ["rustybuzz/libm", "hashbrown", "dep:libm"] no_std = ["rustybuzz/libm", "hashbrown", "dep:libm", "core_maths"]
peniko = ["dep:peniko"] peniko = ["dep:peniko"]
shape-run-cache = [] shape-run-cache = []
std = [ std = [

View file

@ -30,37 +30,37 @@ impl Color {
/// Get a tuple over all of the attributes, in `(r, g, b, a)` order. /// Get a tuple over all of the attributes, in `(r, g, b, a)` order.
#[inline] #[inline]
pub fn as_rgba_tuple(self) -> (u8, u8, u8, u8) { pub const fn as_rgba_tuple(self) -> (u8, u8, u8, u8) {
(self.r(), self.g(), self.b(), self.a()) (self.r(), self.g(), self.b(), self.a())
} }
/// Get an array over all of the components, in `[r, g, b, a]` order. /// Get an array over all of the components, in `[r, g, b, a]` order.
#[inline] #[inline]
pub fn as_rgba(self) -> [u8; 4] { pub const fn as_rgba(self) -> [u8; 4] {
[self.r(), self.g(), self.b(), self.a()] [self.r(), self.g(), self.b(), self.a()]
} }
/// Get the red component /// Get the red component
#[inline] #[inline]
pub fn r(&self) -> u8 { pub const fn r(&self) -> u8 {
((self.0 & 0x00_FF_00_00) >> 16) as u8 ((self.0 & 0x00_FF_00_00) >> 16) as u8
} }
/// Get the green component /// Get the green component
#[inline] #[inline]
pub fn g(&self) -> u8 { pub const fn g(&self) -> u8 {
((self.0 & 0x00_00_FF_00) >> 8) as u8 ((self.0 & 0x00_00_FF_00) >> 8) as u8
} }
/// Get the blue component /// Get the blue component
#[inline] #[inline]
pub fn b(&self) -> u8 { pub const fn b(&self) -> u8 {
(self.0 & 0x00_00_00_FF) as u8 (self.0 & 0x00_00_00_FF) as u8
} }
/// Get the alpha component /// Get the alpha component
#[inline] #[inline]
pub fn a(&self) -> u8 { pub const fn a(&self) -> u8 {
((self.0 & 0xFF_00_00_00) >> 24) as u8 ((self.0 & 0xFF_00_00_00) >> 24) as u8
} }
} }
@ -79,23 +79,23 @@ pub enum FamilyOwned {
impl FamilyOwned { impl FamilyOwned {
pub fn new(family: Family) -> Self { pub fn new(family: Family) -> Self {
match family { match family {
Family::Name(name) => FamilyOwned::Name(SmolStr::from(name)), Family::Name(name) => Self::Name(SmolStr::from(name)),
Family::Serif => FamilyOwned::Serif, Family::Serif => Self::Serif,
Family::SansSerif => FamilyOwned::SansSerif, Family::SansSerif => Self::SansSerif,
Family::Cursive => FamilyOwned::Cursive, Family::Cursive => Self::Cursive,
Family::Fantasy => FamilyOwned::Fantasy, Family::Fantasy => Self::Fantasy,
Family::Monospace => FamilyOwned::Monospace, Family::Monospace => Self::Monospace,
} }
} }
pub fn as_family(&self) -> Family { pub fn as_family(&self) -> Family {
match self { match self {
FamilyOwned::Name(name) => Family::Name(name), Self::Name(name) => Family::Name(name),
FamilyOwned::Serif => Family::Serif, Self::Serif => Family::Serif,
FamilyOwned::SansSerif => Family::SansSerif, Self::SansSerif => Family::SansSerif,
FamilyOwned::Cursive => Family::Cursive, Self::Cursive => Family::Cursive,
FamilyOwned::Fantasy => Family::Fantasy, Self::Fantasy => Family::Fantasy,
FamilyOwned::Monospace => Family::Monospace, Self::Monospace => Family::Monospace,
} }
} }
} }
@ -153,7 +153,7 @@ impl FeatureTag {
/// Stylistic Set 2 (font-specific alternate glyphs) /// Stylistic Set 2 (font-specific alternate glyphs)
pub const STYLISTIC_SET_2: Self = Self::new(b"ss02"); pub const STYLISTIC_SET_2: Self = Self::new(b"ss02");
pub fn as_bytes(&self) -> &[u8; 4] { pub const fn as_bytes(&self) -> &[u8; 4] {
&self.0 &self.0
} }
} }
@ -170,7 +170,7 @@ pub struct FontFeatures {
} }
impl FontFeatures { impl FontFeatures {
pub fn new() -> Self { pub const fn new() -> Self {
Self { Self {
features: Vec::new(), features: Vec::new(),
} }
@ -244,7 +244,7 @@ impl<'a> Attrs<'a> {
/// Create a new set of attributes with sane defaults /// Create a new set of attributes with sane defaults
/// ///
/// This defaults to a regular Sans-Serif font. /// This defaults to a regular Sans-Serif font.
pub fn new() -> Self { pub const fn new() -> Self {
Self { Self {
color_opt: None, color_opt: None,
family: Family::SansSerif, family: Family::SansSerif,
@ -260,43 +260,43 @@ impl<'a> Attrs<'a> {
} }
/// Set [Color] /// Set [Color]
pub fn color(mut self, color: Color) -> Self { pub const fn color(mut self, color: Color) -> Self {
self.color_opt = Some(color); self.color_opt = Some(color);
self self
} }
/// Set [Family] /// Set [Family]
pub fn family(mut self, family: Family<'a>) -> Self { pub const fn family(mut self, family: Family<'a>) -> Self {
self.family = family; self.family = family;
self self
} }
/// Set [Stretch] /// Set [Stretch]
pub fn stretch(mut self, stretch: Stretch) -> Self { pub const fn stretch(mut self, stretch: Stretch) -> Self {
self.stretch = stretch; self.stretch = stretch;
self self
} }
/// Set [Style] /// Set [Style]
pub fn style(mut self, style: Style) -> Self { pub const fn style(mut self, style: Style) -> Self {
self.style = style; self.style = style;
self self
} }
/// Set [Weight] /// Set [Weight]
pub fn weight(mut self, weight: Weight) -> Self { pub const fn weight(mut self, weight: Weight) -> Self {
self.weight = weight; self.weight = weight;
self self
} }
/// Set metadata /// Set metadata
pub fn metadata(mut self, metadata: usize) -> Self { pub const fn metadata(mut self, metadata: usize) -> Self {
self.metadata = metadata; self.metadata = metadata;
self self
} }
/// Set [`CacheKeyFlags`] /// Set [`CacheKeyFlags`]
pub fn cache_key_flags(mut self, cache_key_flags: CacheKeyFlags) -> Self { pub const fn cache_key_flags(mut self, cache_key_flags: CacheKeyFlags) -> Self {
self.cache_key_flags = cache_key_flags; self.cache_key_flags = cache_key_flags;
self self
} }
@ -308,7 +308,7 @@ impl<'a> Attrs<'a> {
} }
/// Set letter spacing (tracking) in EM /// Set letter spacing (tracking) in EM
pub fn letter_spacing(mut self, letter_spacing: f32) -> Self { pub const fn letter_spacing(mut self, letter_spacing: f32) -> Self {
self.letter_spacing_opt = Some(LetterSpacing(letter_spacing)); self.letter_spacing_opt = Some(LetterSpacing(letter_spacing));
self self
} }
@ -471,7 +471,9 @@ impl AttrsList {
for span in self.spans.iter() { for span in self.spans.iter() {
if span.0.end <= index { if span.0.end <= index {
continue; continue;
} else if span.0.start >= index { }
if span.0.start >= index {
removes.push((span.0.clone(), false)); removes.push((span.0.clone(), false));
} else { } else {
removes.push((span.0.clone(), true)); removes.push((span.0.clone(), true));

View file

@ -63,13 +63,12 @@ impl<'text> Iterator for BidiParagraphs<'text> {
let paragraph = &self.text[para.range]; let paragraph = &self.text[para.range];
// `para.range` includes the newline that splits the line, so remove it if present // `para.range` includes the newline that splits the line, so remove it if present
let mut char_indices = paragraph.char_indices(); let mut char_indices = paragraph.char_indices();
if let Some(i) = char_indices.next_back().and_then(|(i, c)| { char_indices
// `BidiClass::B` is a Paragraph_Separator (various newline characters) .next_back()
(bidi_class(c) == BidiClass::B).then_some(i) .and_then(|(i, c)| {
}) { // `BidiClass::B` is a Paragraph_Separator (various newline characters)
Some(&paragraph[0..i]) (bidi_class(c) == BidiClass::B).then_some(i)
} else { })
Some(paragraph) .map_or(Some(paragraph), |i| Some(&paragraph[0..i]))
}
} }
} }

View file

@ -2,11 +2,16 @@
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
use core::{cmp, fmt}; use core::{cmp, fmt};
#[cfg(not(feature = "std"))]
use core_maths::CoreFloat;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
#[cfg(feature = "swash")] #[cfg(feature = "swash")]
use crate::Color; use crate::Color;
use crate::{ use crate::{
Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Cursor, Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Cursor,
FontSystem, LayoutCursor, LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, Scroll, FontSystem, LayoutCursor, LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, Scroll,
@ -45,36 +50,34 @@ impl LayoutRun<'_> {
let mut x_end = None; let mut x_end = None;
let rtl_factor = if self.rtl { 1. } else { 0. }; let rtl_factor = if self.rtl { 1. } else { 0. };
let ltr_factor = 1. - rtl_factor; let ltr_factor = 1. - rtl_factor;
for glyph in self.glyphs.iter() { for glyph in self.glyphs {
let cursor = self.cursor_from_glyph_left(glyph); let cursor = self.cursor_from_glyph_left(glyph);
if cursor >= cursor_start && cursor <= cursor_end { if cursor >= cursor_start && cursor <= cursor_end {
if x_start.is_none() { if x_start.is_none() {
x_start = Some(glyph.x + glyph.w * rtl_factor); x_start = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
} }
x_end = Some(glyph.x + glyph.w * rtl_factor); x_end = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
} }
let cursor = self.cursor_from_glyph_right(glyph); let cursor = self.cursor_from_glyph_right(glyph);
if cursor >= cursor_start && cursor <= cursor_end { if cursor >= cursor_start && cursor <= cursor_end {
if x_start.is_none() { if x_start.is_none() {
x_start = Some(glyph.x + glyph.w * ltr_factor); x_start = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
} }
x_end = Some(glyph.x + glyph.w * ltr_factor); x_end = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
} }
} }
if let Some(x_start) = x_start { x_start.map(|x_start| {
let x_end = x_end.expect("end of cursor not found"); let x_end = x_end.expect("end of cursor not found");
let (x_start, x_end) = if x_start < x_end { let (x_start, x_end) = if x_start < x_end {
(x_start, x_end) (x_start, x_end)
} else { } else {
(x_end, x_start) (x_end, x_start)
}; };
Some((x_start, x_end - x_start)) (x_start, x_end - x_start)
} else { })
None
}
} }
fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor { const fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
if self.rtl { if self.rtl {
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before) Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
} else { } else {
@ -82,7 +85,7 @@ impl LayoutRun<'_> {
} }
} }
fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor { const fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
if self.rtl { if self.rtl {
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After) Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
} else { } else {
@ -102,7 +105,7 @@ pub struct LayoutRunIter<'b> {
} }
impl<'b> LayoutRunIter<'b> { impl<'b> LayoutRunIter<'b> {
pub fn new(buffer: &'b Buffer) -> Self { pub const fn new(buffer: &'b Buffer) -> Self {
Self { Self {
buffer, buffer,
line_i: buffer.scroll.line, line_i: buffer.scroll.line,
@ -276,7 +279,7 @@ impl Buffer {
pub fn borrow_with<'a>( pub fn borrow_with<'a>(
&'a mut self, &'a mut self,
font_system: &'a mut FontSystem, font_system: &'a mut FontSystem,
) -> BorrowedWithFontSystem<'a, Buffer> { ) -> BorrowedWithFontSystem<'a, Self> {
BorrowedWithFontSystem { BorrowedWithFontSystem {
inner: self, inner: self,
font_system, font_system,
@ -358,7 +361,7 @@ impl Buffer {
let layout = self let layout = self
.line_layout(font_system, line_i) .line_layout(font_system, line_i)
.expect("shape_until_cursor failed to scroll forwards"); .expect("shape_until_cursor failed to scroll forwards");
for layout_line in layout.iter() { for layout_line in layout {
total_height += layout_line.line_height_opt.unwrap_or(metrics.line_height); total_height += layout_line.line_height_opt.unwrap_or(metrics.line_height);
} }
if total_height > height + self.scroll.vertical { if total_height > height + self.scroll.vertical {
@ -379,18 +382,16 @@ impl Buffer {
if let Some(layout_cursor) = self.layout_cursor(font_system, cursor) { if let Some(layout_cursor) = self.layout_cursor(font_system, cursor) {
if let Some(layout_lines) = self.line_layout(font_system, layout_cursor.line) { if let Some(layout_lines) = self.line_layout(font_system, layout_cursor.line) {
if let Some(layout_line) = layout_lines.get(layout_cursor.layout) { if let Some(layout_line) = layout_lines.get(layout_cursor.layout) {
let (x_min, x_max) = if let Some(glyph) = layout_line let (x_min, x_max) = layout_line
.glyphs .glyphs
.get(layout_cursor.glyph) .get(layout_cursor.glyph)
.or_else(|| layout_line.glyphs.last()) .or_else(|| layout_line.glyphs.last())
{ .map_or((0.0, 0.0), |glyph| {
//TODO: use code from cursor_glyph_opt? //TODO: use code from cursor_glyph_opt?
let x_a = glyph.x; let x_a = glyph.x;
let x_b = glyph.x + glyph.w; let x_b = glyph.x + glyph.w;
(x_a.min(x_b), x_a.max(x_b)) (x_a.min(x_b), x_a.max(x_b))
} else { });
(0.0, 0.0)
};
if x_min < self.scroll.horizontal { if x_min < self.scroll.horizontal {
self.scroll.horizontal = x_min; self.scroll.horizontal = x_min;
self.redraw = true; self.redraw = true;
@ -419,7 +420,7 @@ impl Buffer {
let line_i = self.scroll.line - 1; let line_i = self.scroll.line - 1;
if let Some(layout) = self.line_layout(font_system, line_i) { if let Some(layout) = self.line_layout(font_system, line_i) {
let mut layout_height = 0.0; let mut layout_height = 0.0;
for layout_line in layout.iter() { for layout_line in layout {
layout_height += layout_height +=
layout_line.line_height_opt.unwrap_or(metrics.line_height); layout_line.line_height_opt.unwrap_or(metrics.line_height);
} }
@ -451,16 +452,15 @@ impl Buffer {
if prune { if prune {
self.lines[line_i].reset_shaping(); self.lines[line_i].reset_shaping();
continue; continue;
} else {
break;
} }
break;
} }
let mut layout_height = 0.0; let mut layout_height = 0.0;
let layout = self let layout = self
.line_layout(font_system, line_i) .line_layout(font_system, line_i)
.expect("shape_until_scroll invalid line"); .expect("shape_until_scroll invalid line");
for layout_line in layout.iter() { for layout_line in layout {
let line_height = layout_line.line_height_opt.unwrap_or(metrics.line_height); let line_height = layout_line.line_height_opt.unwrap_or(metrics.line_height);
layout_height += line_height; layout_height += line_height;
total_height += line_height; total_height += line_height;
@ -547,7 +547,7 @@ impl Buffer {
} }
/// Get the current [`Metrics`] /// Get the current [`Metrics`]
pub fn metrics(&self) -> Metrics { pub const fn metrics(&self) -> Metrics {
self.metrics self.metrics
} }
@ -561,7 +561,7 @@ impl Buffer {
} }
/// Get the current [`Wrap`] /// Get the current [`Wrap`]
pub fn wrap(&self) -> Wrap { pub const fn wrap(&self) -> Wrap {
self.wrap self.wrap
} }
@ -575,7 +575,7 @@ impl Buffer {
} }
/// Get the current `monospace_width` /// Get the current `monospace_width`
pub fn monospace_width(&self) -> Option<f32> { pub const fn monospace_width(&self) -> Option<f32> {
self.monospace_width self.monospace_width
} }
@ -593,7 +593,7 @@ impl Buffer {
} }
/// Get the current `tab_width` /// Get the current `tab_width`
pub fn tab_width(&self) -> u16 { pub const fn tab_width(&self) -> u16 {
self.tab_width self.tab_width
} }
@ -606,7 +606,7 @@ impl Buffer {
if tab_width != self.tab_width { if tab_width != self.tab_width {
self.tab_width = tab_width; self.tab_width = tab_width;
// Shaping must be reset when tab width is changed // Shaping must be reset when tab width is changed
for line in self.lines.iter_mut() { for line in &mut self.lines {
if line.shape_opt().is_some() && line.text().contains('\t') { if line.shape_opt().is_some() && line.text().contains('\t') {
line.reset_shaping(); line.reset_shaping();
} }
@ -617,7 +617,7 @@ impl Buffer {
} }
/// Get the current buffer dimensions (width, height) /// Get the current buffer dimensions (width, height)
pub fn size(&self) -> (Option<f32>, Option<f32>) { pub const fn size(&self) -> (Option<f32>, Option<f32>) {
(self.width_opt, self.height_opt) (self.width_opt, self.height_opt)
} }
@ -660,7 +660,7 @@ impl Buffer {
} }
/// Get the current scroll location /// Get the current scroll location
pub fn scroll(&self) -> Scroll { pub const fn scroll(&self) -> Scroll {
self.scroll self.scroll
} }
@ -758,8 +758,7 @@ impl Buffer {
let mut attrs_list = self let mut attrs_list = self
.lines .lines
.get_mut(line_count) .get_mut(line_count)
.map(BufferLine::reclaim_attrs) .map_or_else(|| AttrsList::new(&Attrs::new()), BufferLine::reclaim_attrs)
.unwrap_or_else(|| AttrsList::new(&Attrs::new()))
.reset(default_attrs); .reset(default_attrs);
let mut line_string = self let mut line_string = self
.lines .lines
@ -811,8 +810,7 @@ impl Buffer {
let next_attrs_list = self let next_attrs_list = self
.lines .lines
.get_mut(line_count + 1) .get_mut(line_count + 1)
.map(BufferLine::reclaim_attrs) .map_or_else(|| AttrsList::new(&Attrs::new()), BufferLine::reclaim_attrs)
.unwrap_or_else(|| AttrsList::new(&Attrs::new()))
.reset(default_attrs); .reset(default_attrs);
let next_line_string = self let next_line_string = self
.lines .lines
@ -856,7 +854,7 @@ impl Buffer {
} }
/// True if a redraw is needed /// True if a redraw is needed
pub fn redraw(&self) -> bool { pub const fn redraw(&self) -> bool {
self.redraw self.redraw
} }
@ -866,7 +864,7 @@ impl Buffer {
} }
/// Get the visible layout runs for rendering and other tasks /// Get the visible layout runs for rendering and other tasks
pub fn layout_runs(&self) -> LayoutRunIter { pub const fn layout_runs(&self) -> LayoutRunIter {
LayoutRunIter::new(self) LayoutRunIter::new(self)
} }
@ -991,14 +989,16 @@ impl Buffer {
}, },
}; };
let (new_index, new_affinity) = match layout_line.glyphs.get(layout_cursor.glyph) { let (new_index, new_affinity) =
Some(glyph) => (glyph.start, Affinity::After), layout_line.glyphs.get(layout_cursor.glyph).map_or_else(
None => match layout_line.glyphs.last() { || {
Some(glyph) => (glyph.end, Affinity::Before), layout_line
//TODO: is this correct? .glyphs
None => (0, Affinity::After), .last()
}, .map_or((0, Affinity::After), |glyph| (glyph.end, Affinity::Before))
}; },
|glyph| (glyph.start, Affinity::After),
);
if cursor.line != layout_cursor.line if cursor.line != layout_cursor.line
|| cursor.index != new_index || cursor.index != new_index
@ -1159,8 +1159,7 @@ impl Buffer {
cursor.index = line cursor.index = line
.text() .text()
.char_indices() .char_indices()
.filter_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) }) .find_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) })
.next()
.unwrap_or(0); .unwrap_or(0);
cursor_x_opt = None; cursor_x_opt = None;
} }
@ -1253,7 +1252,7 @@ impl Buffer {
.unicode_word_indices() .unicode_word_indices()
.map(|(i, word)| i + word.len()) .map(|(i, word)| i + word.len())
.find(|&i| i > cursor.index) .find(|&i| i > cursor.index)
.unwrap_or(line.text().len()); .unwrap_or_else(|| line.text().len());
} else if cursor.line + 1 < self.lines.len() { } else if cursor.line + 1 < self.lines.len() {
cursor.line += 1; cursor.line += 1;
cursor.index = 0; cursor.index = 0;
@ -1340,13 +1339,9 @@ impl Buffer {
F: FnMut(i32, i32, u32, u32, Color), F: FnMut(i32, i32, u32, u32, Color),
{ {
for run in self.layout_runs() { for run in self.layout_runs() {
for glyph in run.glyphs.iter() { for glyph in run.glyphs {
let physical_glyph = glyph.physical((0., 0.), 1.0); let physical_glyph = glyph.physical((0., 0.), 1.0);
let glyph_color = glyph.color_opt.map_or(color, |some| some);
let glyph_color = match glyph.color_opt {
Some(some) => some,
None => color,
};
cache.with_pixels( cache.with_pixels(
font_system, font_system,

View file

@ -95,7 +95,7 @@ impl BufferLine {
} }
/// Get line ending /// Get line ending
pub fn ending(&self) -> LineEnding { pub const fn ending(&self) -> LineEnding {
self.ending self.ending
} }
@ -114,7 +114,7 @@ impl BufferLine {
} }
/// Get attributes list /// Get attributes list
pub fn attrs_list(&self) -> &AttrsList { pub const fn attrs_list(&self) -> &AttrsList {
&self.attrs_list &self.attrs_list
} }
@ -133,7 +133,7 @@ impl BufferLine {
} }
/// Get the Text alignment /// Get the Text alignment
pub fn align(&self) -> Option<Align> { pub const fn align(&self) -> Option<Align> {
self.align self.align
} }
@ -155,7 +155,7 @@ impl BufferLine {
/// Append line at end of this line /// Append line at end of this line
/// ///
/// The wrap setting of the appended line will be lost /// The wrap setting of the appended line will be lost
pub fn append(&mut self, other: Self) { pub fn append(&mut self, other: &Self) {
let len = self.text.len(); let len = self.text.len();
self.text.push_str(other.text()); self.text.push_str(other.text());
@ -224,7 +224,7 @@ impl BufferLine {
} }
/// Get line shaping cache /// Get line shaping cache
pub fn shape_opt(&self) -> Option<&ShapeLine> { pub const fn shape_opt(&self) -> Option<&ShapeLine> {
self.shape_opt.get() self.shape_opt.get()
} }
@ -261,13 +261,13 @@ impl BufferLine {
} }
/// Get line layout cache /// Get line layout cache
pub fn layout_opt(&self) -> Option<&Vec<LayoutLine>> { pub const fn layout_opt(&self) -> Option<&Vec<LayoutLine>> {
self.layout_opt.get() self.layout_opt.get()
} }
/// Get line metadata. This will be None if [`BufferLine::set_metadata`] has not been called /// Get line metadata. This will be None if [`BufferLine::set_metadata`] has not been called
/// after the last reset of shaping and layout caches /// after the last reset of shaping and layout caches
pub fn metadata(&self) -> Option<usize> { pub const fn metadata(&self) -> Option<usize> {
self.metadata self.metadata
} }

View file

@ -11,7 +11,7 @@ pub enum Cached<T: Clone + Debug> {
impl<T: Clone + Debug> Cached<T> { impl<T: Clone + Debug> Cached<T> {
/// Gets the value if in state `Self::Used`. /// Gets the value if in state `Self::Used`.
pub fn get(&self) -> Option<&T> { pub const fn get(&self) -> Option<&T> {
match self { match self {
Self::Empty | Self::Unused(_) => None, Self::Empty | Self::Unused(_) => None,
Self::Used(t) => Some(t), Self::Used(t) => Some(t),
@ -27,7 +27,7 @@ impl<T: Clone + Debug> Cached<T> {
} }
/// Checks if the value is empty or unused. /// Checks if the value is empty or unused.
pub fn is_unused(&self) -> bool { pub const fn is_unused(&self) -> bool {
match self { match self {
Self::Empty | Self::Unused(_) => true, Self::Empty | Self::Unused(_) => true,
Self::Used(_) => false, Self::Used(_) => false,
@ -35,7 +35,7 @@ impl<T: Clone + Debug> Cached<T> {
} }
/// Checks if the value is used (i.e. cached for access). /// Checks if the value is used (i.e. cached for access).
pub fn is_used(&self) -> bool { pub const fn is_used(&self) -> bool {
match self { match self {
Self::Empty | Self::Unused(_) => false, Self::Empty | Self::Unused(_) => false,
Self::Used(_) => true, Self::Used(_) => true,

View file

@ -43,7 +43,7 @@ impl Affinity {
*self == Self::After *self == Self::After
} }
pub fn from_before(before: bool) -> Self { pub const fn from_before(before: bool) -> Self {
if before { if before {
Self::Before Self::Before
} else { } else {
@ -51,7 +51,7 @@ impl Affinity {
} }
} }
pub fn from_after(after: bool) -> Self { pub const fn from_after(after: bool) -> Self {
if after { if after {
Self::After Self::After
} else { } else {

View file

@ -1,21 +1,24 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
#[cfg(not(feature = "std"))]
use alloc::{
string::{String, ToString},
vec::Vec,
};
#[cfg(feature = "swash")]
use std::cmp;
use core::iter::once;
use unicode_segmentation::UnicodeSegmentation;
#[cfg(feature = "swash")] #[cfg(feature = "swash")]
use crate::Color; use crate::Color;
use crate::{ use crate::{
Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef, Change, ChangeItem, Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef, Change, ChangeItem,
Cursor, Edit, FontSystem, LayoutRun, Selection, Shaping, Cursor, Edit, FontSystem, LayoutRun, Selection, Shaping,
}; };
#[cfg(feature = "no_std")]
use alloc::{
string::{String, ToString},
vec::Vec,
};
#[cfg(feature = "swash")]
use std::cmp;
use core::iter::once;
use unicode_segmentation::UnicodeSegmentation;
/// A wrapper of [`Buffer`] for easy editing /// A wrapper of [`Buffer`] for easy editing
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Editor<'buffer> { pub struct Editor<'buffer> {
@ -66,30 +69,24 @@ fn cursor_glyph_opt(cursor: &Cursor, run: &LayoutRun) -> Option<(usize, f32)> {
fn cursor_position(cursor: &Cursor, run: &LayoutRun) -> Option<(i32, i32)> { fn cursor_position(cursor: &Cursor, run: &LayoutRun) -> Option<(i32, i32)> {
let (cursor_glyph, cursor_glyph_offset) = cursor_glyph_opt(cursor, run)?; let (cursor_glyph, cursor_glyph_offset) = cursor_glyph_opt(cursor, run)?;
let x = match run.glyphs.get(cursor_glyph) { let x = run.glyphs.get(cursor_glyph).map_or_else(
Some(glyph) => { || {
// Start of detected glyph run.glyphs.last().map_or(0, |glyph| {
if glyph.level.is_rtl() {
(glyph.x + glyph.w - cursor_glyph_offset) as i32
} else {
(glyph.x + cursor_glyph_offset) as i32
}
}
None => match run.glyphs.last() {
Some(glyph) => {
// End of last glyph
if glyph.level.is_rtl() { if glyph.level.is_rtl() {
glyph.x as i32 glyph.x as i32
} else { } else {
(glyph.x + glyph.w) as i32 (glyph.x + glyph.w) as i32
} }
} })
None => { },
// Start of empty line |glyph| {
0 if glyph.level.is_rtl() {
(glyph.x + glyph.w - cursor_glyph_offset) as i32
} else {
(glyph.x + cursor_glyph_offset) as i32
} }
}, },
}; );
Some((x, run.line_top as i32)) Some((x, run.line_top as i32))
} }
@ -135,7 +132,7 @@ impl<'buffer> Editor<'buffer> {
if let Some((start, end)) = selection_bounds { if let Some((start, end)) = selection_bounds {
if line_i >= start.line && line_i <= end.line { if line_i >= start.line && line_i <= end.line {
let mut range_opt = None; let mut range_opt = None;
for glyph in run.glyphs.iter() { for glyph in run.glyphs {
// Guess x offset based on characters // Guess x offset based on characters
let cluster = &run.text[glyph.start..glyph.end]; let cluster = &run.text[glyph.start..glyph.end];
let total = cluster.grapheme_indices(true).count(); let total = cluster.grapheme_indices(true).count();
@ -197,13 +194,10 @@ impl<'buffer> Editor<'buffer> {
f(x, y, 1, line_height as u32, cursor_color); f(x, y, 1, line_height as u32, cursor_color);
} }
for glyph in run.glyphs.iter() { for glyph in run.glyphs {
let physical_glyph = glyph.physical((0., 0.), 1.0); let physical_glyph = glyph.physical((0., 0.), 1.0);
let mut glyph_color = match glyph.color_opt { let mut glyph_color = glyph.color_opt.map_or(text_color, |some| some);
Some(some) => some,
None => text_color,
};
if text_color != selected_text_color { if text_color != selected_text_color {
if let Some((start, end)) = selection_bounds { if let Some((start, end)) = selection_bounds {
if line_i >= start.line if line_i >= start.line
@ -277,7 +271,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
} }
fn tab_width(&self) -> u16 { fn tab_width(&self) -> u16 {
self.with_buffer(|buffer| buffer.tab_width()) self.with_buffer(super::super::buffer::Buffer::tab_width)
} }
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) { fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
@ -334,12 +328,12 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
// Re-add part of line after selection // Re-add part of line after selection
if let Some(after) = after_opt { if let Some(after) = after_opt {
buffer.lines[start.line].append(after); buffer.lines[start.line].append(&after);
} }
// Re-add valid parts of end line // Re-add valid parts of end line
if let Some(end_line) = end_line_opt { if let Some(end_line) = end_line_opt {
buffer.lines[start.line].append(end_line); buffer.lines[start.line].append(&end_line);
} }
} }
@ -376,7 +370,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
let ending = buffer let ending = buffer
.lines .lines
.last() .last()
.map(|line| line.ending()) .map(super::super::buffer_line::BufferLine::ending)
.unwrap_or_default(); .unwrap_or_default();
let line = BufferLine::new( let line = BufferLine::new(
String::new(), String::new(),
@ -386,7 +380,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
buffer buffer
.lines .lines
.last() .last()
.map_or(Attrs::new(), |line| line.attrs_list().defaults()) .map_or_else(Attrs::new, |line| line.attrs_list().defaults())
}, },
|x| x.defaults(), |x| x.defaults(),
)), )),
@ -417,7 +411,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
let mut these_attrs = final_attrs.split_off(data_line.len()); let mut these_attrs = final_attrs.split_off(data_line.len());
remaining_split_len -= data_line.len(); remaining_split_len -= data_line.len();
core::mem::swap(&mut these_attrs, &mut final_attrs); core::mem::swap(&mut these_attrs, &mut final_attrs);
line.append(BufferLine::new( line.append(&BufferLine::new(
data_line data_line
.strip_suffix(char::is_control) .strip_suffix(char::is_control)
.unwrap_or(data_line), .unwrap_or(data_line),
@ -438,11 +432,11 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
final_attrs.split_off(remaining_split_len), final_attrs.split_off(remaining_split_len),
Shaping::Advanced, Shaping::Advanced,
); );
tmp.append(after); tmp.append(&after);
buffer.lines.insert(insert_line, tmp); buffer.lines.insert(insert_line, tmp);
cursor.line += 1; cursor.line += 1;
} else { } else {
line.append(after); line.append(&after);
} }
for data_line in lines_iter.rev() { for data_line in lines_iter.rev() {
remaining_split_len -= data_line.len(); remaining_split_len -= data_line.len();
@ -510,9 +504,8 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
} }
fn delete_selection(&mut self) -> bool { fn delete_selection(&mut self) -> bool {
let (start, end) = match self.selection_bounds() { let Some((start, end)) = self.selection_bounds() else {
Some(some) => some, return false;
None => return false,
}; };
// Reset cursor to start of selection // Reset cursor to start of selection
@ -538,7 +531,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
} }
} }
for item in change.items.iter() { for item in &change.items {
//TODO: edit cursor if needed? //TODO: edit cursor if needed?
if item.insert { if item.insert {
self.cursor = self.insert_at(item.start, &item.text, None); self.cursor = self.insert_at(item.start, &item.text, None);

View file

@ -130,7 +130,7 @@ impl Change {
// Reverse change (in place) // Reverse change (in place)
pub fn reverse(&mut self) { pub fn reverse(&mut self) {
self.items.reverse(); self.items.reverse();
for item in self.items.iter_mut() { for item in &mut self.items {
item.reverse(); item.reverse();
} }
} }
@ -192,7 +192,7 @@ pub trait Edit<'buffer> {
/// Get the [`Buffer`] redraw flag /// Get the [`Buffer`] redraw flag
fn redraw(&self) -> bool { fn redraw(&self) -> bool {
self.with_buffer(|buffer| buffer.redraw()) self.with_buffer(super::buffer::Buffer::redraw)
} }
/// Set the [`Buffer`] redraw flag /// Set the [`Buffer`] redraw flag
@ -273,7 +273,7 @@ pub trait Edit<'buffer> {
.unicode_word_indices() .unicode_word_indices()
.map(|(i, word)| i + word.len()) .map(|(i, word)| i + word.len())
.find(|&i| i > end.index) .find(|&i| i > end.index)
.unwrap_or(line.text().len()); .unwrap_or_else(|| line.text().len());
} }
Some((start, end)) Some((start, end))

View file

@ -1,7 +1,6 @@
#[cfg(feature = "no_std")]
use core::cmp;
use alloc::{collections::BTreeMap, string::String}; use alloc::{collections::BTreeMap, string::String};
#[cfg(feature = "swash")]
use core::cmp;
use modit::{Event, Key, Parser, TextObject, WordIter}; use modit::{Event, Key, Parser, TextObject, WordIter};
use crate::{ use crate::{

View file

@ -77,7 +77,7 @@ pub trait Fallback: Send + Sync {
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct Fallbacks { pub struct Fallbacks {
lists: Vec<&'static str>, lists: Vec<&'static str>,
common_fallback_range: Range<usize>, common_fallback_range: Range<usize>,
forbidden_fallback_range: Range<usize>, forbidden_fallback_range: Range<usize>,
@ -174,13 +174,14 @@ use log::warn as missing_warn;
// Default font gets None for both `weight_offset` and `script_non_matches`, and thus, it is // Default font gets None for both `weight_offset` and `script_non_matches`, and thus, it is
// always the first to be popped from the set. // always the first to be popped from the set.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct MonospaceFallbackInfo { pub struct MonospaceFallbackInfo {
font_weight_diff: Option<u16>, font_weight_diff: Option<u16>,
codepoint_non_matches: Option<usize>, codepoint_non_matches: Option<usize>,
font_weight: u16, font_weight: u16,
id: fontdb::ID, id: fontdb::ID,
} }
#[derive(Debug)]
pub struct FontFallbackIter<'a> { pub struct FontFallbackIter<'a> {
font_system: &'a mut FontSystem, font_system: &'a mut FontSystem,
font_match_keys: &'a [FontMatchKey], font_match_keys: &'a [FontMatchKey],
@ -223,7 +224,7 @@ impl<'a> FontFallbackIter<'a> {
} }
} }
pub fn check_missing(&mut self, word: &str) { pub fn check_missing(&self, word: &str) {
if self.end { if self.end {
missing_warn!( missing_warn!(
"Failed to find any fallback for {:?} locale '{}': '{}'", "Failed to find any fallback for {:?} locale '{}': '{}'",
@ -252,15 +253,16 @@ impl<'a> FontFallbackIter<'a> {
} }
pub fn face_name(&self, id: fontdb::ID) -> &str { pub fn face_name(&self, id: fontdb::ID) -> &str {
if let Some(face) = self.font_system.db().face(id) { self.font_system
if let Some((name, _)) = face.families.first() { .db()
name .face(id)
} else { .map_or("invalid font id", |face| {
&face.post_script_name if let Some((name, _)) = face.families.first() {
} name
} else { } else {
"invalid font id" &face.post_script_name
} }
})
} }
pub fn shape_caches(&mut self) -> &mut ShapeBuffer { pub fn shape_caches(&mut self) -> &mut ShapeBuffer {
@ -268,11 +270,10 @@ impl<'a> FontFallbackIter<'a> {
} }
fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool { fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool {
if let Some(face) = self.font_system.db().face(id) { self.font_system
face.families.iter().any(|(name, _)| name == family_name) .db()
} else { .face(id)
false .is_some_and(|face| face.families.iter().any(|(name, _)| name == family_name))
}
} }
fn default_font_match_key(&self) -> Option<&FontMatchKey> { fn default_font_match_key(&self) -> Option<&FontMatchKey> {
@ -304,7 +305,7 @@ impl<'a> FontFallbackIter<'a> {
'DEF_FAM: while self.default_i < self.default_families.len() { 'DEF_FAM: while self.default_i < self.default_families.len() {
self.default_i += 1; self.default_i += 1;
let is_mono = self.default_families[self.default_i - 1] == &Family::Monospace; let is_mono = self.default_families[self.default_i - 1] == &Family::Monospace;
let default_font_match_key = self.default_font_match_key().cloned(); let default_font_match_key = self.default_font_match_key().copied();
let word_chars_count = self.word.chars().count(); let word_chars_count = self.word.chars().count();
macro_rules! mk_mono_fallback_info { macro_rules! mk_mono_fallback_info {
@ -334,9 +335,8 @@ impl<'a> FontFallbackIter<'a> {
(false, Some(m_key)) => { (false, Some(m_key)) => {
if let Some(font) = self.font_system.get_font(m_key.id, self.ideal_weight) { if let Some(font) = self.font_system.get_font(m_key.id, self.ideal_weight) {
return Some(font); return Some(font);
} else {
break 'DEF_FAM;
} }
break 'DEF_FAM;
} }
(true, None) => (), (true, None) => (),
(true, Some(m_key)) => { (true, Some(m_key)) => {
@ -360,7 +360,7 @@ impl<'a> FontFallbackIter<'a> {
} }
} }
} }
}; }
let mono_ids_for_scripts = if is_mono && !self.scripts.is_empty() { let mono_ids_for_scripts = if is_mono && !self.scripts.is_empty() {
let scripts = self.scripts.iter().filter_map(|script| { let scripts = self.scripts.iter().filter_map(|script| {

View file

@ -27,16 +27,16 @@ impl Fallback for PlatformFallback {
} }
// Fallbacks to use after any script specific fallbacks // Fallbacks to use after any script specific fallbacks
fn common_fallback() -> &'static [&'static str] { const fn common_fallback() -> &'static [&'static str] {
&[] &[]
} }
// Fallbacks to never use // Fallbacks to never use
fn forbidden_fallback() -> &'static [&'static str] { const fn forbidden_fallback() -> &'static [&'static str] {
&[] &[]
} }
// Fallbacks to use per script // Fallbacks to use per script
fn script_fallback(_script: Script, _locale: &str) -> &'static [&'static str] { const fn script_fallback(_script: Script, _locale: &str) -> &'static [&'static str] {
&[] &[]
} }

View file

@ -27,7 +27,7 @@ impl Fallback for PlatformFallback {
} }
// Fallbacks to use after any script specific fallbacks // Fallbacks to use after any script specific fallbacks
fn common_fallback() -> &'static [&'static str] { const fn common_fallback() -> &'static [&'static str] {
//TODO: abstract style (sans/serif/monospaced) //TODO: abstract style (sans/serif/monospaced)
&[ &[
/* Sans-serif fallbacks */ /* Sans-serif fallbacks */
@ -49,7 +49,7 @@ fn common_fallback() -> &'static [&'static str] {
} }
// Fallbacks to never use // Fallbacks to never use
fn forbidden_fallback() -> &'static [&'static str] { const fn forbidden_fallback() -> &'static [&'static str] {
&[] &[]
} }

View file

@ -15,7 +15,7 @@ use alloc::vec::Vec;
use rustybuzz::Face as RustybuzzFace; use rustybuzz::Face as RustybuzzFace;
use self_cell::self_cell; use self_cell::self_cell;
pub(crate) mod fallback; pub mod fallback;
pub use fallback::{Fallback, PlatformFallback}; pub use fallback::{Fallback, PlatformFallback};
pub use self::system::*; pub use self::system::*;
@ -58,7 +58,7 @@ impl fmt::Debug for Font {
} }
impl Font { impl Font {
pub fn id(&self) -> fontdb::ID { pub const fn id(&self) -> fontdb::ID {
self.id self.id
} }
@ -119,9 +119,9 @@ impl Font {
let monospace_em_width = info let monospace_em_width = info
.monospaced .monospaced
.then(|| { .then(|| {
let hor_advance = face.glyph_hor_advance(face.glyph_index(' ')?)? as f32; let hor_advance = face.glyph_hor_advance(face.glyph_index(' ')?)?;
let upem = face.units_per_em() as f32; let upem = face.units_per_em();
Some(hor_advance / upem) Some(f32::from(hor_advance) / f32::from(upem))
}) })
.flatten(); .flatten();
@ -144,7 +144,7 @@ impl Font {
.cmap? .cmap?
.subtables .subtables
.into_iter() .into_iter()
.filter(|subtable| subtable.is_unicode()) .filter(ttf_parser::cmap::Subtable::is_unicode)
.for_each(|subtable| { .for_each(|subtable| {
unicode_codepoints.reserve(1024); unicode_codepoints.reserve(1024);
subtable.codepoints(|code_point| { subtable.codepoints(|code_point| {
@ -194,7 +194,7 @@ impl Font {
.into_iter() .into_iter()
.find(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wght")) .find(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wght"))
{ {
let wght = (weight.0 as f32).clamp(axis.min_value, axis.max_value); let wght = f32::from(weight.0).clamp(axis.min_value, axis.max_value);
let _ = face.set_variation(ttf_parser::Tag::from_bytes(b"wght"), wght); let _ = face.set_variation(ttf_parser::Tag::from_bytes(b"wght"), wght);
} }
face face

View file

@ -127,7 +127,7 @@ impl fmt::Debug for FontSystem {
f.debug_struct("FontSystem") f.debug_struct("FontSystem")
.field("locale", &self.locale) .field("locale", &self.locale)
.field("db", &self.db) .field("db", &self.db)
.finish() .finish_non_exhaustive()
} }
} }
@ -180,7 +180,7 @@ impl FontSystem {
HashMap::default(); HashMap::default();
if cfg!(feature = "monospace_fallback") { if cfg!(feature = "monospace_fallback") {
monospace_font_ids.iter().for_each(|&id| { for &id in &monospace_font_ids {
db.with_face_data(id, |font_data, face_index| { db.with_face_data(id, |font_data, face_index| {
let _ = ttf_parser::Face::parse(font_data, face_index).map(|face| { let _ = ttf_parser::Face::parse(font_data, face_index).map(|face| {
face.tables() face.tables()
@ -196,7 +196,7 @@ impl FontSystem {
}) })
}); });
}); });
}); }
} }
let per_script_monospace_font_ids = per_script_monospace_font_ids let per_script_monospace_font_ids = per_script_monospace_font_ids
@ -211,9 +211,9 @@ impl FontSystem {
db, db,
monospace_font_ids, monospace_font_ids,
per_script_monospace_font_ids, per_script_monospace_font_ids,
font_cache: Default::default(), font_cache: HashMap::default(),
font_matches_cache: Default::default(), font_matches_cache: HashMap::default(),
font_codepoint_support_info_cache: Default::default(), font_codepoint_support_info_cache: HashMap::default(),
monospace_fallbacks_buffer: BTreeSet::default(), monospace_fallbacks_buffer: BTreeSet::default(),
#[cfg(feature = "shape-run-cache")] #[cfg(feature = "shape-run-cache")]
shape_run_cache: crate::ShapeRunCache::default(), shape_run_cache: crate::ShapeRunCache::default(),
@ -234,7 +234,7 @@ impl FontSystem {
} }
/// Get the database. /// Get the database.
pub fn db(&self) -> &fontdb::Database { pub const fn db(&self) -> &fontdb::Database {
&self.db &self.db
} }
@ -258,15 +258,14 @@ impl FontSystem {
unsafe { unsafe {
self.db.make_shared_face_data(id); self.db.make_shared_face_data(id);
} }
match Font::new(&self.db, id, weight) { if let Some(font) = Font::new(&self.db, id, weight) {
Some(font) => Some(Arc::new(font)), Some(Arc::new(font))
None => { } else {
log::warn!( log::warn!(
"failed to load font '{}'", "failed to load font '{}'",
self.db.face(id)?.post_script_name self.db.face(id)?.post_script_name
); );
None None
}
} }
}) })
.clone() .clone()

View file

@ -102,7 +102,7 @@ impl SubpixelBin {
} }
} }
pub fn as_float(&self) -> f32 { pub const fn as_float(&self) -> f32 {
match self { match self {
Self::Zero => 0.0, Self::Zero => 0.0,
Self::One => 0.25, Self::One => 0.25,

View file

@ -2,10 +2,12 @@
use core::fmt::Display; use core::fmt::Display;
use crate::{math, CacheKey, CacheKeyFlags, Color};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::vec::Vec; use alloc::vec::Vec;
use crate::{math, CacheKey, CacheKeyFlags, Color}; #[cfg(not(feature = "std"))]
use core_maths::CoreFloat;
/// A laid out glyph /// A laid out glyph
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -78,8 +80,8 @@ impl LayoutGlyph {
self.glyph_id, self.glyph_id,
self.font_size * scale, self.font_size * scale,
( (
(self.x + x_offset) * scale + offset.0, (self.x + x_offset).mul_add(scale, offset.0),
math::truncf((self.y - y_offset) * scale + offset.1), // Hinting in Y axis math::truncf((self.y - y_offset).mul_add(scale, offset.1)), // Hinting in Y axis
), ),
self.font_weight, self.font_weight,
self.cache_key_flags, self.cache_key_flags,

View file

@ -18,7 +18,7 @@ pub enum LineEnding {
impl LineEnding { impl LineEnding {
/// Get the line ending as a str /// Get the line ending as a str
pub fn as_str(&self) -> &'static str { pub const fn as_str(&self) -> &'static str {
match self { match self {
Self::Lf => "\n", Self::Lf => "\n",
Self::CrLf => "\r\n", Self::CrLf => "\r\n",
@ -39,7 +39,7 @@ pub struct LineIter<'a> {
impl<'a> LineIter<'a> { impl<'a> LineIter<'a> {
/// Create an iterator of lines in a string slice /// Create an iterator of lines in a string slice
pub fn new(string: &'a str) -> Self { pub const fn new(string: &'a str) -> Self {
Self { Self {
string, string,
start: 0, start: 0,
@ -61,9 +61,9 @@ impl Iterator for LineIter<'_> {
LineEnding::CrLf LineEnding::CrLf
} else if after.starts_with("\n\r") { } else if after.starts_with("\n\r") {
LineEnding::LfCr LineEnding::LfCr
} else if after.starts_with("\n") { } else if after.starts_with('\n') {
LineEnding::Lf LineEnding::Lf
} else if after.starts_with("\r") { } else if after.starts_with('\r') {
LineEnding::Cr LineEnding::Cr
} else { } else {
//TODO: this should not be possible //TODO: this should not be possible

View file

@ -2,20 +2,23 @@
#![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_arguments)]
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::cmp::{max, min};
use core::fmt;
use core::mem;
use core::ops::Range;
use unicode_script::{Script, UnicodeScript};
use unicode_segmentation::UnicodeSegmentation;
use crate::fallback::FontFallbackIter; use crate::fallback::FontFallbackIter;
use crate::{ use crate::{
math, Align, AttrsList, CacheKeyFlags, Color, Font, FontSystem, LayoutGlyph, LayoutLine, math, Align, AttrsList, CacheKeyFlags, Color, Font, FontSystem, LayoutGlyph, LayoutLine,
Metrics, Wrap, Metrics, Wrap,
}; };
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::cmp::{max, min};
use core::fmt;
use core::mem;
use core::ops::Range;
#[cfg(not(feature = "std"))]
use core_maths::CoreFloat;
use unicode_script::{Script, UnicodeScript};
use unicode_segmentation::UnicodeSegmentation;
/// The shaping strategy of some text. /// The shaping strategy of some text.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -117,8 +120,8 @@ fn shape_fallback(
let run = &line[start_run..end_run]; let run = &line[start_run..end_run];
let font_scale = font.rustybuzz().units_per_em() as f32; let font_scale = font.rustybuzz().units_per_em() as f32;
let ascent = font.rustybuzz().ascender() as f32 / font_scale; let ascent = f32::from(font.rustybuzz().ascender()) / font_scale;
let descent = -font.rustybuzz().descender() as f32 / font_scale; let descent = -f32::from(font.rustybuzz().descender()) / font_scale;
let mut buffer = scratch.rustybuzz_buffer.take().unwrap_or_default(); let mut buffer = scratch.rustybuzz_buffer.take().unwrap_or_default();
buffer.set_direction(if span_rtl { buffer.set_direction(if span_rtl {
@ -197,7 +200,7 @@ fn shape_fallback(
color_opt: attrs.color_opt, color_opt: attrs.color_opt,
metadata: attrs.metadata, metadata: attrs.metadata,
cache_key_flags: attrs.cache_key_flags, cache_key_flags: attrs.cache_key_flags,
metrics_opt: attrs.metrics_opt.map(|x| x.into()), metrics_opt: attrs.metrics_opt.map(Into::into),
}); });
} }
@ -286,9 +289,8 @@ fn shape_run(
//TODO: improve performance! //TODO: improve performance!
while !missing.is_empty() { while !missing.is_empty() {
let font = match font_iter.next() { let Some(font) = font_iter.next() else {
Some(some) => some, break;
None => break,
}; };
log::trace!( log::trace!(
@ -335,9 +337,8 @@ fn shape_run(
while i < glyphs.len() { while i < glyphs.len() {
if glyphs[i].start >= start && glyphs[i].end <= end { if glyphs[i].start >= start && glyphs[i].end <= end {
break; break;
} else {
i += 1;
} }
i += 1;
} }
// Remove prior glyphs // Remove prior glyphs
@ -495,7 +496,7 @@ fn shape_skip(
color_opt: attrs.color_opt, color_opt: attrs.color_opt,
metadata: attrs.metadata, metadata: attrs.metadata,
cache_key_flags: attrs.cache_key_flags, cache_key_flags: attrs.cache_key_flags,
metrics_opt: attrs.metrics_opt.map(|x| x.into()), metrics_opt: attrs.metrics_opt.map(Into::into),
} }
}), }),
); );
@ -523,7 +524,7 @@ pub struct ShapeGlyph {
} }
impl ShapeGlyph { impl ShapeGlyph {
fn layout( const fn layout(
&self, &self,
font_size: f32, font_size: f32,
line_height_opt: Option<f32>, line_height_opt: Option<f32>,
@ -633,7 +634,7 @@ impl ShapeWord {
word.is_ascii() && !word.chars().any(|c| c.is_ascii_control() && c != '\t'); word.is_ascii() && !word.chars().any(|c| c.is_ascii_control() && c != '\t');
if is_simple_ascii && !word.is_empty() { if is_simple_ascii && !word.is_empty() {
let attrs = attrs_list.defaults(); let _attrs = attrs_list.defaults();
shaping.run( shaping.run(
&mut glyphs, &mut glyphs,
font_system, font_system,
@ -685,7 +686,7 @@ impl ShapeWord {
/// Get the width of the [`ShapeWord`] in pixels, using the [`ShapeGlyph::width`] function. /// Get the width of the [`ShapeWord`] in pixels, using the [`ShapeGlyph::width`] function.
pub fn width(&self, font_size: f32) -> f32 { pub fn width(&self, font_size: f32) -> f32 {
let mut width = 0.0; let mut width = 0.0;
for glyph in self.glyphs.iter() { for glyph in &self.glyphs {
width += glyph.width(font_size); width += glyph.width(font_size);
} }
width width
@ -920,7 +921,7 @@ impl ShapeLine {
log::trace!("Line {}: '{}'", if rtl { "RTL" } else { "LTR" }, line); log::trace!("Line {}: '{}'", if rtl { "RTL" } else { "LTR" }, line);
for para_info in bidi.paragraphs.iter() { for para_info in &bidi.paragraphs {
let line_rtl = para_info.level.is_rtl(); let line_rtl = para_info.level.is_rtl();
assert_eq!(line_rtl, rtl); assert_eq!(line_rtl, rtl);
@ -971,12 +972,12 @@ impl ShapeLine {
// Adjust for tabs // Adjust for tabs
let mut x = 0.0; let mut x = 0.0;
for span in spans.iter_mut() { for span in &mut spans {
for word in span.words.iter_mut() { for word in &mut span.words {
for glyph in word.glyphs.iter_mut() { for glyph in &mut word.glyphs {
if line.get(glyph.start..glyph.end) == Some("\t") { if line.get(glyph.start..glyph.end) == Some("\t") {
// Tabs are shaped as spaces, so they will always have the x_advance of a space. // Tabs are shaped as spaces, so they will always have the x_advance of a space.
let tab_x_advance = (tab_width as f32) * glyph.x_advance; let tab_x_advance = f32::from(tab_width) * glyph.x_advance;
let tab_stop = (math::floorf(x / tab_x_advance) + 1.0) * tab_x_advance; let tab_stop = (math::floorf(x / tab_x_advance) + 1.0) * tab_x_advance;
glyph.x_advance = tab_stop - x; glyph.x_advance = tab_stop - x;
} }
@ -987,7 +988,7 @@ impl ShapeLine {
self.rtl = rtl; self.rtl = rtl;
self.spans = spans; self.spans = spans;
self.metrics_opt = attrs_list.defaults().metrics_opt.map(|x| x.into()); self.metrics_opt = attrs_list.defaults().metrics_opt.map(Into::into);
// Return the buffer for later reuse. // Return the buffer for later reuse.
font_system.shape_buffer.spans = cached_spans; font_system.shape_buffer.spans = cached_spans;
@ -995,7 +996,7 @@ impl ShapeLine {
// A modified version of first part of unicode_bidi::bidi_info::visual_run // A modified version of first part of unicode_bidi::bidi_info::visual_run
fn adjust_levels(para: &unicode_bidi::Paragraph) -> Vec<unicode_bidi::Level> { fn adjust_levels(para: &unicode_bidi::Paragraph) -> Vec<unicode_bidi::Level> {
use unicode_bidi::BidiClass::*; use unicode_bidi::BidiClass::{B, BN, FSI, LRE, LRI, LRO, PDF, PDI, RLE, RLI, RLO, S, WS};
let text = para.info.text; let text = para.info.text;
let levels = &para.info.levels; let levels = &para.info.levels;
let original_classes = &para.info.original_classes; let original_classes = &para.info.original_classes;
@ -1141,6 +1142,23 @@ impl ShapeLine {
layout_lines: &mut Vec<LayoutLine>, layout_lines: &mut Vec<LayoutLine>,
match_mono_width: Option<f32>, match_mono_width: Option<f32>,
) { ) {
fn add_to_visual_line(
vl: &mut VisualLine,
span_index: usize,
start: (usize, usize),
end: (usize, usize),
width: f32,
number_of_blanks: u32,
) {
if end == start {
return;
}
vl.ranges.push((span_index, start, end));
vl.w += width;
vl.spaces += number_of_blanks;
}
// For each visual line a list of (span index, and range of words in that span) // For each visual line a list of (span index, and range of words in that span)
// Note that a BiDi visual line could have multiple spans or parts of them // Note that a BiDi visual line could have multiple spans or parts of them
// let mut vl_range_of_spans = Vec::with_capacity(1); // let mut vl_range_of_spans = Vec::with_capacity(1);
@ -1160,23 +1178,6 @@ impl ShapeLine {
v.glyphs v.glyphs
})); }));
fn add_to_visual_line(
vl: &mut VisualLine,
span_index: usize,
start: (usize, usize),
end: (usize, usize),
width: f32,
number_of_blanks: u32,
) {
if end == start {
return;
}
vl.ranges.push((span_index, start, end));
vl.w += width;
vl.spaces += number_of_blanks;
}
// This would keep the maximum number of spans that would fit on a visual line // This would keep the maximum number of spans that would fit on a visual line
// If one span is too large, this variable will hold the range of words inside that span // If one span is too large, this variable will hold the range of words inside that span
// that fits on a line. // that fits on a line.
@ -1187,7 +1188,7 @@ impl ShapeLine {
for (span_index, span) in self.spans.iter().enumerate() { for (span_index, span) in self.spans.iter().enumerate() {
let mut word_range_width = 0.; let mut word_range_width = 0.;
let mut number_of_blanks: u32 = 0; let mut number_of_blanks: u32 = 0;
for word in span.words.iter() { for word in &span.words {
let word_width = word.width(font_size); let word_width = word.width(font_size);
word_range_width += word_width; word_range_width += word_width;
if word.blank { if word.blank {
@ -1232,7 +1233,6 @@ impl ShapeLine {
width_before_last_blank = word_range_width; width_before_last_blank = word_range_width;
} }
word_range_width += word_width; word_range_width += word_width;
continue;
} else if wrap == Wrap::Glyph } else if wrap == Wrap::Glyph
// Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping. // Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping.
|| (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY)) || (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY))
@ -1266,7 +1266,6 @@ impl ShapeLine {
<= width_opt.unwrap_or(f32::INFINITY) <= width_opt.unwrap_or(f32::INFINITY)
{ {
word_range_width += glyph_width; word_range_width += glyph_width;
continue;
} else { } else {
add_to_visual_line( add_to_visual_line(
&mut current_visual_line, &mut current_visual_line,
@ -1358,7 +1357,6 @@ impl ShapeLine {
width_before_last_blank = word_range_width; width_before_last_blank = word_range_width;
} }
word_range_width += word_width; word_range_width += word_width;
continue;
} else if wrap == Wrap::Glyph } else if wrap == Wrap::Glyph
// Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping. // Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping.
|| (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY)) || (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY))
@ -1392,7 +1390,6 @@ impl ShapeLine {
<= width_opt.unwrap_or(f32::INFINITY) <= width_opt.unwrap_or(f32::INFINITY)
{ {
word_range_width += glyph_width; word_range_width += glyph_width;
continue;
} else { } else {
add_to_visual_line( add_to_visual_line(
&mut current_visual_line, &mut current_visual_line,
@ -1467,26 +1464,26 @@ impl ShapeLine {
} }
} }
if !current_visual_line.ranges.is_empty() { if current_visual_line.ranges.is_empty() {
visual_lines.push(current_visual_line);
} else {
current_visual_line.clear(); current_visual_line.clear();
cached_visual_lines.push(current_visual_line); cached_visual_lines.push(current_visual_line);
} else {
visual_lines.push(current_visual_line);
} }
// Create the LayoutLines using the ranges inside visual lines // Create the LayoutLines using the ranges inside visual lines
let align = align.unwrap_or(if self.rtl { Align::Right } else { Align::Left }); let align = align.unwrap_or(if self.rtl { Align::Right } else { Align::Left });
let line_width = match width_opt { let line_width = width_opt.map_or_else(
Some(width) => width, || {
None => {
let mut width: f32 = 0.0; let mut width: f32 = 0.0;
for visual_line in visual_lines.iter() { for visual_line in &visual_lines {
width = width.max(visual_line.w); width = width.max(visual_line.w);
} }
width width
} },
}; |width| width,
);
let start_x = if self.rtl { line_width } else { 0.0 }; let start_x = if self.rtl { line_width } else { 0.0 };
@ -1546,7 +1543,7 @@ impl ShapeLine {
let mut process_range = |range: Range<usize>| { let mut process_range = |range: Range<usize>| {
for &(span_index, (starting_word, starting_glyph), (ending_word, ending_glyph)) in for &(span_index, (starting_word, starting_glyph), (ending_word, ending_glyph)) in
visual_line.ranges[range.clone()].iter() &visual_line.ranges[range]
{ {
let span = &self.spans[span_index]; let span = &self.spans[span_index];
// If ending_glyph is not 0 we need to include glyphs from the ending_word // If ending_glyph is not 0 we need to include glyphs from the ending_word
@ -1585,12 +1582,14 @@ impl ShapeLine {
_ => font_size, _ => font_size,
}; };
let x_advance = glyph_font_size * glyph.x_advance let x_advance = glyph_font_size.mul_add(
+ if word.blank { glyph.x_advance,
if word.blank {
justification_expansion justification_expansion
} else { } else {
0.0 0.0
}; },
);
if self.rtl { if self.rtl {
x -= x_advance; x -= x_advance;
} }
@ -1626,12 +1625,12 @@ impl ShapeLine {
} }
let mut line_height_opt: Option<f32> = None; let mut line_height_opt: Option<f32> = None;
for glyph in glyphs.iter() { for glyph in &glyphs {
if let Some(glyph_line_height) = glyph.line_height_opt { if let Some(glyph_line_height) = glyph.line_height_opt {
line_height_opt = match line_height_opt { line_height_opt = line_height_opt
Some(line_height) => Some(line_height.max(glyph_line_height)), .map_or(Some(glyph_line_height), |line_height| {
None => Some(glyph_line_height), Some(line_height.max(glyph_line_height))
}; });
} }
} }
@ -1657,7 +1656,7 @@ impl ShapeLine {
max_ascent: 0.0, max_ascent: 0.0,
max_descent: 0.0, max_descent: 0.0,
line_height_opt: self.metrics_opt.map(|x| x.line_height), line_height_opt: self.metrics_opt.map(|x| x.line_height),
glyphs: Default::default(), glyphs: Vec::default(),
}); });
} }

View file

@ -17,12 +17,9 @@ fn swash_image(
context: &mut ScaleContext, context: &mut ScaleContext,
cache_key: CacheKey, cache_key: CacheKey,
) -> Option<SwashImage> { ) -> Option<SwashImage> {
let font = match font_system.get_font(cache_key.font_id, cache_key.font_weight) { let Some(font) = font_system.get_font(cache_key.font_id, cache_key.font_weight) else {
Some(some) => some, log::warn!("did not find font {:?}", cache_key.font_id);
None => { return None;
log::warn!("did not find font {:?}", cache_key.font_id);
return None;
}
}; };
let variable_width = font let variable_width = font
@ -38,7 +35,7 @@ fn swash_image(
if let Some(variation) = variable_width { if let Some(variation) = variable_width {
scaler = scaler.variations(std::iter::once(swash::Setting { scaler = scaler.variations(std::iter::once(swash::Setting {
tag: swash::Tag::from_be_bytes(*b"wght"), tag: swash::Tag::from_be_bytes(*b"wght"),
value: (cache_key.font_weight.0 as f32) value: f32::from(cache_key.font_weight.0)
.clamp(variation.min_value(), variation.max_value()), .clamp(variation.min_value(), variation.max_value()),
})); }));
} }
@ -87,12 +84,9 @@ fn swash_outline_commands(
) -> Option<Box<[swash::zeno::Command]>> { ) -> Option<Box<[swash::zeno::Command]>> {
use swash::zeno::PathData as _; use swash::zeno::PathData as _;
let font = match font_system.get_font(cache_key.font_id, cache_key.font_weight) { let Some(font) = font_system.get_font(cache_key.font_id, cache_key.font_weight) else {
Some(some) => some, log::warn!("did not find font {:?}", cache_key.font_id);
None => { return None;
log::warn!("did not find font {:?}", cache_key.font_id);
return None;
}
}; };
// Build the scaler // Build the scaler
@ -206,7 +200,7 @@ impl SwashCache {
f( f(
x + off_x, x + off_x,
y + off_y, y + off_y,
Color(((image.data[i] as u32) << 24) | base.0 & 0xFF_FF_FF), Color((u32::from(image.data[i]) << 24) | base.0 & 0xFF_FF_FF),
); );
i += 1; i += 1;
} }