diff --git a/examples/editor-libcosmic/src/main.rs b/examples/editor-libcosmic/src/main.rs index c84efe2e13..12b26dce00 100644 --- a/examples/editor-libcosmic/src/main.rs +++ b/examples/editor-libcosmic/src/main.rs @@ -84,7 +84,7 @@ fn main() -> cosmic::iced::Result { pub struct Window { theme: Theme, path_opt: Option, - attrs: Attrs<'static>, + attrs: Attrs, font_size: FontSize, #[cfg(not(feature = "vi"))] editor: Mutex>, @@ -111,7 +111,7 @@ impl Window { let mut editor = self.editor.lock().unwrap(); let mut font_system = FONT_SYSTEM.lock().unwrap(); let mut editor = editor.borrow_with(&mut font_system); - match editor.load_text(&path, self.attrs) { + match editor.load_text(&path, &self.attrs) { Ok(()) => { log::info!("opened '{}'", path.display()); self.path_opt = Some(path); @@ -131,8 +131,11 @@ impl Application for Window { type Theme = Theme; fn new(_flags: ()) -> (Self, Command) { - let attrs = cosmic_text::Attrs::new().family(cosmic_text::Family::Monospace); + let attrs = cosmic_text::Attrs::builder() + .family(cosmic_text::Family::Monospace) + .build(); + #[cfg_attr(feature = "vi", allow(unused_mut))] let mut editor = SyntaxEditor::new( Buffer::new( &mut FONT_SYSTEM.lock().unwrap(), @@ -146,7 +149,7 @@ impl Application for Window { #[cfg(feature = "vi")] let mut editor = cosmic_text::ViEditor::new(editor); - update_attrs(&mut editor, attrs); + update_attrs(&mut editor, &attrs); let mut window = Window { theme: Theme::dark(), @@ -203,34 +206,35 @@ impl Application for Window { } } Message::Bold(bold) => { - self.attrs = self.attrs.weight(if bold { + self.attrs.weight = if bold { cosmic_text::Weight::BOLD } else { cosmic_text::Weight::NORMAL - }); + }; let mut editor = self.editor.lock().unwrap(); - update_attrs(&mut *editor, self.attrs); + update_attrs(&mut *editor, &self.attrs); } Message::Italic(italic) => { - self.attrs = self.attrs.style(if italic { + self.attrs.style = if italic { cosmic_text::Style::Italic } else { cosmic_text::Style::Normal - }); + }; let mut editor = self.editor.lock().unwrap(); - update_attrs(&mut *editor, self.attrs); + update_attrs(&mut *editor, &self.attrs); } Message::Monospaced(monospaced) => { - self.attrs = self.attrs.family(if monospaced { + self.attrs.family_owned = if monospaced { cosmic_text::Family::Monospace } else { cosmic_text::Family::SansSerif - }); + } + .into(); let mut editor = self.editor.lock().unwrap(); - update_attrs(&mut *editor, self.attrs); + update_attrs(&mut *editor, &self.attrs); } Message::FontSizeChanged(font_size) => { self.font_size = font_size; @@ -260,7 +264,7 @@ impl Application for Window { let Color { r, g, b, a } = self.theme.cosmic().on_bg_color().into(); let as_u8 = |component: f32| (component * 255.0) as u8; - self.attrs = self.attrs.color(cosmic_text::Color::rgba( + self.attrs.color_opt = Some(cosmic_text::Color::rgba( as_u8(r), as_u8(g), as_u8(b), @@ -275,7 +279,7 @@ impl Application for Window { "Dark" | _ => editor.update_theme("base16-eighties.dark"), }; - update_attrs(&mut *editor, self.attrs); + update_attrs(&mut *editor, &self.attrs); } } @@ -335,7 +339,7 @@ impl Application for Window { text("Monospaced:"), toggler( None, - self.attrs.family == cosmic_text::Family::Monospace, + self.attrs.family_owned == cosmic_text::FamilyOwned::Monospace, Message::Monospaced ), text("Theme:"), @@ -374,9 +378,9 @@ impl Application for Window { } } -fn update_attrs(editor: &mut T, attrs: Attrs) { +fn update_attrs(editor: &mut T, attrs: &Attrs) { editor.buffer_mut().lines.iter_mut().for_each(|line| { - line.set_attrs_list(AttrsList::new(attrs)); + line.set_attrs_list(AttrsList::new(attrs.clone())); }); } diff --git a/examples/editor-orbclient/src/main.rs b/examples/editor-orbclient/src/main.rs index ca823138d4..282740b3ec 100644 --- a/examples/editor-orbclient/src/main.rs +++ b/examples/editor-orbclient/src/main.rs @@ -57,6 +57,7 @@ fn main() { let line_x = 8.0 * display_scale; + #[cfg_attr(feature = "vi", allow(unused_mut))] let mut editor = SyntaxEditor::new( Buffer::new(&mut font_system, font_sizes[font_size_i]), &syntax_system, @@ -72,8 +73,7 @@ fn main() { editor .buffer_mut() .set_size(window.width() as f32 - line_x * 2.0, window.height() as f32); - - let attrs = Attrs::new().family(Family::Monospace); + let attrs = Attrs::builder().family(Family::Monospace).build(); match editor.load_text(&path, attrs) { Ok(()) => (), Err(err) => { diff --git a/examples/rich-text/src/main.rs b/examples/rich-text/src/main.rs index b5d1ab4629..03681db80f 100644 --- a/examples/rich-text/src/main.rs +++ b/examples/rich-text/src/main.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use cosmic_text::{ - Action, Attrs, AttrsList, Buffer, BufferLine, Color, Edit, Editor, Family, FontSystem, Metrics, - Shaping, Style, SwashCache, Weight, + Action, Attrs, AttrsBuilder, AttrsList, Buffer, BufferLine, Color, Edit, Editor, Family, + FontSystem, Metrics, Shaping, Style, SwashCache, Weight, }; use orbclient::{EventOption, Renderer, Window, WindowFlag}; use std::{ @@ -47,98 +47,98 @@ fn main() { .buffer_mut() .set_size(window.width() as f32, window.height() as f32); - let attrs = Attrs::new(); - let serif_attrs = attrs.family(Family::Serif); - let mono_attrs = attrs.family(Family::Monospace); - let comic_attrs = attrs.family(Family::Name("Comic Neue")); + let attrs = Attrs::builder(); + let serif_attrs = attrs.clone().family(Family::Serif); + let mono_attrs = attrs.clone().family(Family::Monospace); + let comic_attrs = attrs.clone().family(Family::Name("Comic Neue")); editor.buffer_mut().lines.clear(); - let lines: &[&[(&str, Attrs)]] = &[ + let lines: &[&[(&str, AttrsBuilder)]] = &[ &[ - ("B", attrs.weight(Weight::BOLD)), - ("old ", attrs), - ("I", attrs.style(Style::Italic)), - ("talic ", attrs), - ("f", attrs), - ("i ", attrs), - ("f", attrs.weight(Weight::BOLD)), - ("i ", attrs), - ("f", attrs.style(Style::Italic)), - ("i ", attrs), + ("B", attrs.clone().weight(Weight::BOLD)), + ("old ", attrs.clone()), + ("I", attrs.clone().style(Style::Italic)), + ("talic ", attrs.clone()), + ("f", attrs.clone()), + ("i ", attrs.clone()), + ("f", attrs.clone().weight(Weight::BOLD)), + ("i ", attrs.clone()), + ("f", attrs.clone().style(Style::Italic)), + ("i ", attrs.clone()), ], &[ - ("Sans-Serif Normal ", attrs), - ("Sans-Serif Bold ", attrs.weight(Weight::BOLD)), - ("Sans-Serif Italic ", attrs.style(Style::Italic)), + ("Sans-Serif Normal ", attrs.clone()), + ("Sans-Serif Bold ", attrs.clone().weight(Weight::BOLD)), + ("Sans-Serif Italic ", attrs.clone().style(Style::Italic)), ( "Sans-Serif Bold Italic", - attrs.weight(Weight::BOLD).style(Style::Italic), + attrs.clone().weight(Weight::BOLD).style(Style::Italic), ), ], &[ - ("Serif Normal ", serif_attrs), - ("Serif Bold ", serif_attrs.weight(Weight::BOLD)), - ("Serif Italic ", serif_attrs.style(Style::Italic)), + ("Serif Normal ", serif_attrs.clone()), + ("Serif Bold ", serif_attrs.clone().weight(Weight::BOLD)), + ("Serif Italic ", serif_attrs.clone().style(Style::Italic)), ( "Serif Bold Italic", serif_attrs.weight(Weight::BOLD).style(Style::Italic), ), ], &[ - ("Mono Normal ", mono_attrs), - ("Mono Bold ", mono_attrs.weight(Weight::BOLD)), - ("Mono Italic ", mono_attrs.style(Style::Italic)), + ("Mono Normal ", mono_attrs.clone()), + ("Mono Bold ", mono_attrs.clone().weight(Weight::BOLD)), + ("Mono Italic ", mono_attrs.clone().style(Style::Italic)), ( "Mono Bold Italic", mono_attrs.weight(Weight::BOLD).style(Style::Italic), ), ], &[ - ("Comic Normal ", comic_attrs), - ("Comic Bold ", comic_attrs.weight(Weight::BOLD)), - ("Comic Italic ", comic_attrs.style(Style::Italic)), + ("Comic Normal ", comic_attrs.clone()), + ("Comic Bold ", comic_attrs.clone().weight(Weight::BOLD)), + ("Comic Italic ", comic_attrs.clone().style(Style::Italic)), ( "Comic Bold Italic", comic_attrs.weight(Weight::BOLD).style(Style::Italic), ), ], &[ - ("R", attrs.color(Color::rgb(0xFF, 0x00, 0x00))), - ("A", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))), - ("I", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))), - ("N", attrs.color(Color::rgb(0x00, 0xFF, 0x00))), - ("B", attrs.color(Color::rgb(0x00, 0x00, 0xFF))), - ("O", attrs.color(Color::rgb(0x4B, 0x00, 0x82))), - ("W ", attrs.color(Color::rgb(0x94, 0x00, 0xD3))), - ("Red ", attrs.color(Color::rgb(0xFF, 0x00, 0x00))), - ("Orange ", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))), - ("Yellow ", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))), - ("Green ", attrs.color(Color::rgb(0x00, 0xFF, 0x00))), - ("Blue ", attrs.color(Color::rgb(0x00, 0x00, 0xFF))), - ("Indigo ", attrs.color(Color::rgb(0x4B, 0x00, 0x82))), - ("Violet ", attrs.color(Color::rgb(0x94, 0x00, 0xD3))), - ("U", attrs.color(Color::rgb(0x94, 0x00, 0xD3))), - ("N", attrs.color(Color::rgb(0x4B, 0x00, 0x82))), - ("I", attrs.color(Color::rgb(0x00, 0x00, 0xFF))), - ("C", attrs.color(Color::rgb(0x00, 0xFF, 0x00))), - ("O", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))), - ("R", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))), - ("N", attrs.color(Color::rgb(0xFF, 0x00, 0x00))), + ("R", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))), + ("A", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))), + ("I", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))), + ("N", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))), + ("B", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))), + ("O", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))), + ("W ", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))), + ("Red ", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))), + ("Orange ", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))), + ("Yellow ", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))), + ("Green ", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))), + ("Blue ", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))), + ("Indigo ", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))), + ("Violet ", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))), + ("U", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))), + ("N", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))), + ("I", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))), + ("C", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))), + ("O", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))), + ("R", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))), + ("N", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))), ], &[( "生活,삶,जिंदगी 😀 FPS", - attrs.color(Color::rgb(0xFF, 0x00, 0x00)), + attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00)), )], ]; - for &line in lines { + for line in lines { let mut line_text = String::new(); - let mut attrs_list = AttrsList::new(attrs); - for &(text, attrs) in line { + let mut attrs_list = AttrsList::new(attrs.clone().build()); + for (text, attrs) in line.iter() { let start = line_text.len(); line_text.push_str(text); let end = line_text.len(); - attrs_list.add_span(start..end, attrs); + attrs_list.add_span(start..end, attrs.clone().build()); } editor .buffer_mut() diff --git a/src/attrs.rs b/src/attrs.rs index eeec35351c..c1514ec44b 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -1,10 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +use alloc::borrow::Cow; #[cfg(not(feature = "std"))] -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; +use alloc::{string::ToString, vec::Vec}; use core::ops::Range; pub use fontdb::{Family, Stretch, Style, Weight}; @@ -55,7 +53,7 @@ impl Color { /// An owned version of [`Family`] #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum FamilyOwned { - Name(String), + Name(Cow<'static, str>), Serif, SansSerif, Cursive, @@ -66,7 +64,7 @@ pub enum FamilyOwned { impl FamilyOwned { pub fn new(family: Family) -> Self { match family { - Family::Name(name) => FamilyOwned::Name(name.to_string()), + Family::Name(name) => FamilyOwned::Name(Cow::Owned(name.to_string())), Family::Serif => FamilyOwned::Serif, Family::SansSerif => FamilyOwned::SansSerif, Family::Cursive => FamilyOwned::Cursive, @@ -87,68 +85,99 @@ impl FamilyOwned { } } -/// Text attributes -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct Attrs<'a> { - //TODO: should this be an option? - pub color_opt: Option, - pub family: Family<'a>, - pub stretch: Stretch, - pub style: Style, - pub weight: Weight, - pub metadata: usize, +impl From> for FamilyOwned { + fn from(value: Family<'static>) -> Self { + match value { + Family::Name(name) => FamilyOwned::Name(Cow::Borrowed(name)), + Family::Serif => FamilyOwned::Serif, + Family::SansSerif => FamilyOwned::SansSerif, + Family::Cursive => FamilyOwned::Cursive, + Family::Fantasy => FamilyOwned::Fantasy, + Family::Monospace => FamilyOwned::Monospace, + } + } } -impl<'a> Attrs<'a> { - /// Create a new set of attributes with sane defaults - /// - /// This defaults to a regular Sans-Serif font. - pub fn new() -> Self { - Self { - color_opt: None, - family: Family::SansSerif, - stretch: Stretch::Normal, - style: Style::Normal, - weight: Weight::NORMAL, - metadata: 0, - } +#[derive(Clone, Debug)] +#[repr(transparent)] +pub struct AttrsBuilder(Attrs); + +impl AttrsBuilder { + pub fn new(attrs: Attrs) -> Self { + Self(attrs) + } + + pub fn build(self) -> Attrs { + self.0 } /// Set [Color] pub fn color(mut self, color: Color) -> Self { - self.color_opt = Some(color); + self.0.color_opt = Some(color); self } /// Set [Family] - pub fn family(mut self, family: Family<'a>) -> Self { - self.family = family; + pub fn family(mut self, family: impl Into) -> Self { + self.0.family_owned = family.into(); self } /// Set [Stretch] pub fn stretch(mut self, stretch: Stretch) -> Self { - self.stretch = stretch; + self.0.stretch = stretch; self } /// Set [Style] pub fn style(mut self, style: Style) -> Self { - self.style = style; + self.0.style = style; self } /// Set [Weight] pub fn weight(mut self, weight: Weight) -> Self { - self.weight = weight; + self.0.weight = weight; self } /// Set metadata pub fn metadata(mut self, metadata: usize) -> Self { - self.metadata = metadata; + self.0.metadata = metadata; self } +} + +/// Text attributes +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct Attrs { + //TODO: should this be an option? + pub color_opt: Option, + pub family_owned: FamilyOwned, + pub stretch: Stretch, + pub style: Style, + pub weight: Weight, + pub metadata: usize, +} + +impl Attrs { + /// Create a new set of attributes with sane defaults + /// + /// This defaults to a regular Sans-Serif font. + pub fn new() -> Self { + Self { + color_opt: None, + family_owned: FamilyOwned::SansSerif, + stretch: Stretch::Normal, + style: Style::Normal, + weight: Weight::NORMAL, + metadata: 0, + } + } + + pub fn builder() -> AttrsBuilder { + AttrsBuilder::new(Self::new()) + } /// Check if font matches pub fn matches(&self, face: &fontdb::FaceInfo) -> bool { @@ -161,46 +190,22 @@ impl<'a> Attrs<'a> { /// Check if this set of attributes can be shaped with another pub fn compatible(&self, other: &Self) -> bool { - self.family == other.family + self.family_owned == other.family_owned && self.stretch == other.stretch && self.style == other.style && self.weight == other.weight } } -/// An owned version of [`Attrs`] -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct AttrsOwned { - //TODO: should this be an option? - pub color_opt: Option, - pub family_owned: FamilyOwned, - pub stretch: Stretch, - pub style: Style, - pub weight: Weight, - pub metadata: usize, -} - -impl AttrsOwned { - pub fn new(attrs: Attrs) -> Self { - Self { - color_opt: attrs.color_opt, - family_owned: FamilyOwned::new(attrs.family), - stretch: attrs.stretch, - style: attrs.style, - weight: attrs.weight, - metadata: attrs.metadata, - } +impl AsRef for Attrs { + fn as_ref(&self) -> &Attrs { + self } +} - pub fn as_attrs(&self) -> Attrs { - Attrs { - color_opt: self.color_opt, - family: self.family_owned.as_family(), - stretch: self.stretch, - style: self.style, - weight: self.weight, - metadata: self.metadata, - } +impl From<&Attrs> for Attrs { + fn from(value: &Attrs) -> Self { + value.clone() } } @@ -208,26 +213,26 @@ impl AttrsOwned { //TODO: have this clean up the spans when changes are made #[derive(Debug, Eq, PartialEq)] pub struct AttrsList { - defaults: AttrsOwned, - spans: RangeMap, + defaults: Attrs, + spans: RangeMap, } impl AttrsList { /// Create a new attributes list with a set of default [Attrs] pub fn new(defaults: Attrs) -> Self { Self { - defaults: AttrsOwned::new(defaults), + defaults, spans: RangeMap::new(), } } /// Get the default [Attrs] - pub fn defaults(&self) -> Attrs { - self.defaults.as_attrs() + pub fn defaults(&self) -> &Attrs { + &self.defaults } /// Get the current attribute spans - pub fn spans(&self) -> Vec<(&Range, &AttrsOwned)> { + pub fn spans(&self) -> Vec<(&Range, &Attrs)> { self.spans.iter().collect() } @@ -237,28 +242,25 @@ impl AttrsList { } /// Add an attribute span, removes any previous matching parts of spans - pub fn add_span(&mut self, range: Range, attrs: Attrs) { + pub fn add_span(&mut self, range: Range, attrs: impl Into) { //do not support 1..1 even if by accident. if range.start == range.end { return; } - self.spans.insert(range, AttrsOwned::new(attrs)); + self.spans.insert(range, attrs.into()); } /// Get the attribute span for an index /// /// This returns a span that contains the index - pub fn get_span(&self, index: usize) -> Attrs { - self.spans - .get(&index) - .map(|v| v.as_attrs()) - .unwrap_or(self.defaults.as_attrs()) + pub fn get_span(&self, index: usize) -> &Attrs { + self.spans.get(&index).unwrap_or(&self.defaults) } /// Split attributes list at an offset pub fn split_off(&mut self, index: usize) -> Self { - let mut new = Self::new(self.defaults.as_attrs()); + let mut new = Self::new(self.defaults.clone()); let mut removes = Vec::new(); //get the keys we need to remove or fix. diff --git a/src/buffer.rs b/src/buffer.rs index fd85c74e13..1d531c6725 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -599,22 +599,33 @@ impl Buffer { &mut self, font_system: &mut FontSystem, text: &str, - attrs: Attrs, + attrs: impl AsRef + Into, shaping: Shaping, ) { self.lines.clear(); - for line in BidiParagraphs::new(text) { - self.lines.push(BufferLine::new( - line.to_string(), - AttrsList::new(attrs), - shaping, - )); - } - // Make sure there is always one line - if self.lines.is_empty() { + let mut lines = BidiParagraphs::new(text).peekable(); + if lines.peek().is_some() { + while let Some(line) = lines.next() { + if lines.peek().is_some() { + self.lines.push(BufferLine::new( + line.to_string(), + AttrsList::new(attrs.as_ref().clone()), + shaping, + )); + } else { + self.lines.push(BufferLine::new( + line.to_string(), + AttrsList::new(attrs.into()), + shaping, + )); + break; + } + } + } else { + // Make sure there is always one line self.lines.push(BufferLine::new( String::new(), - AttrsList::new(attrs), + AttrsList::new(attrs.into()), shaping, )); } @@ -825,7 +836,12 @@ impl<'a> BorrowedWithFontSystem<'a, Buffer> { } /// Set text of buffer, using provided attributes for each line by default - pub fn set_text(&mut self, text: &str, attrs: Attrs, shaping: Shaping) { + pub fn set_text( + &mut self, + text: &str, + attrs: impl AsRef + Into, + shaping: Shaping, + ) { self.inner.set_text(self.font_system, text, attrs, shaping); } diff --git a/src/buffer_line.rs b/src/buffer_line.rs index 205d4e2ae0..d456fa92a9 100644 --- a/src/buffer_line.rs +++ b/src/buffer_line.rs @@ -133,7 +133,7 @@ impl BufferLine { for (other_range, attrs) in other.attrs_list.spans() { // Add previous attrs spans let range = other_range.start + len..other_range.end + len; - self.attrs_list.add_span(range, attrs.as_attrs()); + self.attrs_list.add_span(range, attrs); } self.reset(); diff --git a/src/edit/editor.rs b/src/edit/editor.rs index 2374ab8a5c..53606fb371 100644 --- a/src/edit/editor.rs +++ b/src/edit/editor.rs @@ -235,8 +235,9 @@ impl Edit for Editor { let after_len = after.text().len(); // Collect attributes - let mut final_attrs = attrs_list - .unwrap_or_else(|| AttrsList::new(line.attrs_list().get_span(line.text().len()))); + let mut final_attrs = attrs_list.unwrap_or_else(|| { + AttrsList::new(line.attrs_list().get_span(line.text().len()).clone()) + }); // Append the inserted text, line by line // we want to see a blank entry if the string ends with a newline diff --git a/src/edit/syntect.rs b/src/edit/syntect.rs index 62e9352f21..621c9945f8 100644 --- a/src/edit/syntect.rs +++ b/src/edit/syntect.rs @@ -8,8 +8,8 @@ use syntect::highlighting::{ use syntect::parsing::{ParseState, ScopeStack, SyntaxReference, SyntaxSet}; use crate::{ - Action, AttrsList, BorrowedWithFontSystem, Buffer, Color, Cursor, Edit, Editor, FontSystem, - Shaping, Style, Weight, Wrap, + Action, AttrsBuilder, AttrsList, BorrowedWithFontSystem, Buffer, Color, Cursor, Edit, Editor, + FontSystem, Shaping, Style, Weight, Wrap, }; #[derive(Debug)] @@ -81,11 +81,11 @@ impl<'a> SyntaxEditor<'a> { /// /// Returns an [`io::Error`] if reading the file fails #[cfg(feature = "std")] - pub fn load_text>( + pub fn load_text( &mut self, font_system: &mut FontSystem, - path: P, - attrs: crate::Attrs, + path: impl AsRef, + attrs: impl AsRef + Into, ) -> io::Result<()> { let path = path.as_ref(); @@ -192,11 +192,11 @@ impl<'a> Edit for SyntaxEditor<'a> { ); let attrs = line.attrs_list().defaults(); - let mut attrs_list = AttrsList::new(attrs); + let mut attrs_list = AttrsList::new(attrs.clone()); for (style, _, range) in ranges { attrs_list.add_span( range, - attrs + AttrsBuilder::new(attrs.clone()) .color(Color::rgba( style.foreground.r, style.foreground.g, @@ -213,7 +213,8 @@ impl<'a> Edit for SyntaxEditor<'a> { Weight::BOLD } else { Weight::NORMAL - }), //TODO: underline + }) + .build(), //TODO: underline ); } @@ -291,7 +292,11 @@ impl<'a, 'b> BorrowedWithFontSystem<'b, SyntaxEditor<'a>> { /// /// Returns an [`io::Error`] if reading the file fails #[cfg(feature = "std")] - pub fn load_text>(&mut self, path: P, attrs: crate::Attrs) -> io::Result<()> { + pub fn load_text( + &mut self, + path: impl AsRef, + attrs: impl AsRef + Into, + ) -> io::Result<()> { self.inner.load_text(self.font_system, path, attrs) } } diff --git a/src/edit/vi.rs b/src/edit/vi.rs index b0309aa7f8..0c496c11da 100644 --- a/src/edit/vi.rs +++ b/src/edit/vi.rs @@ -32,11 +32,11 @@ impl<'a> ViEditor<'a> { /// Load text from a file, and also set syntax to the best option #[cfg(feature = "std")] - pub fn load_text>( + pub fn load_text( &mut self, font_system: &mut FontSystem, - path: P, - attrs: crate::Attrs, + path: impl AsRef, + attrs: impl AsRef + Into, ) -> std::io::Result<()> { self.editor.load_text(font_system, path, attrs) } @@ -457,10 +457,10 @@ impl<'a> Edit for ViEditor<'a> { impl<'a, 'b> BorrowedWithFontSystem<'b, ViEditor<'a>> { /// Load text from a file, and also set syntax to the best option #[cfg(feature = "std")] - pub fn load_text>( + pub fn load_text( &mut self, - path: P, - attrs: crate::Attrs, + path: impl AsRef, + attrs: impl AsRef + Into, ) -> std::io::Result<()> { self.inner.load_text(self.font_system, path, attrs) } diff --git a/src/font/system/no_std.rs b/src/font/system/no_std.rs index af0f00032d..ec8ae89c91 100644 --- a/src/font/system/no_std.rs +++ b/src/font/system/no_std.rs @@ -49,11 +49,14 @@ impl FontSystem { get_font(&self.db, id) } - pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc> { + pub fn get_font_matches( + &mut self, + attrs: impl AsRef + Into, + ) -> Arc> { let ids = self .db .faces() - .filter(|face| attrs.matches(face)) + .filter(|face| attrs.as_ref().matches(face)) .map(|face| face.id) .collect::>(); diff --git a/src/font/system/std.rs b/src/font/system/std.rs index 4a355c0179..df6babf678 100644 --- a/src/font/system/std.rs +++ b/src/font/system/std.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc}; -use crate::{Attrs, AttrsOwned, Font}; +use crate::{Attrs, Font}; /// Access system fonts #[derive(Debug)] @@ -10,7 +10,7 @@ pub struct FontSystem { locale: String, db: fontdb::Database, font_cache: HashMap>>, - font_matches_cache: HashMap>>, + font_matches_cache: HashMap>>, } impl FontSystem { @@ -93,30 +93,35 @@ impl FontSystem { get_font(&mut self.font_cache, &mut self.db, id) } - pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc> { - self.font_matches_cache - //TODO: do not create AttrsOwned unless entry does not already exist - .entry(AttrsOwned::new(attrs)) - .or_insert_with(|| { - #[cfg(not(target_arch = "wasm32"))] - let now = std::time::Instant::now(); - - let ids = self - .db - .faces() - .filter(|face| attrs.matches(face)) - .map(|face| face.id) - .collect::>(); - - #[cfg(not(target_arch = "wasm32"))] - { - let elapsed = now.elapsed(); - log::debug!("font matches for {:?} in {:?}", attrs, elapsed); - } + pub fn get_font_matches( + &mut self, + attrs: impl AsRef + Into, + ) -> Arc> { + if let Some(matches) = self.font_matches_cache.get(attrs.as_ref()) { + matches.clone() + } else { + #[cfg(not(target_arch = "wasm32"))] + let now = std::time::Instant::now(); - Arc::new(ids) - }) - .clone() + let ids = self + .db + .faces() + .filter(|face| attrs.as_ref().matches(face)) + .map(|face| face.id) + .collect::>(); + + #[cfg(not(target_arch = "wasm32"))] + { + let elapsed = now.elapsed(); + log::debug!("font matches for {:?} in {:?}", attrs.as_ref(), elapsed); + } + + let font_matches = Arc::new(ids); + + self.font_matches_cache + .insert(attrs.into(), font_matches.clone()); + font_matches + } } } diff --git a/src/shape.rs b/src/shape.rs index 0b6bc210b6..ebd7be5c29 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -168,7 +168,7 @@ fn shape_run( let fonts = font_system.get_font_matches(attrs); - let default_families = [&attrs.family]; + let default_families = [&attrs.family_owned.as_family()]; let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, scripts); let font = font_iter.next().expect("no default font found"); @@ -268,7 +268,7 @@ fn shape_skip( let attrs = attrs_list.get_span(start_run); let fonts = font_system.get_font_matches(attrs); - let default_families = [&attrs.family]; + let default_families = [&attrs.family_owned.as_family()]; let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, Vec::new()); let font = font_iter.next().expect("no default font found"); @@ -386,7 +386,7 @@ impl ShapeWord { for (egc_i, _egc) in word.grapheme_indices(true) { let start_egc = word_range.start + egc_i; let attrs_egc = attrs_list.get_span(start_egc); - if !attrs.compatible(&attrs_egc) { + if !attrs.compatible(attrs_egc) { //TODO: more efficient glyphs.append(&mut shaping.run( font_system,