diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-01 16:56:35 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-02 09:18:33 +0100 |
| commit | 37ac5d966ebaf97ac79c507028cd5b742b510b89 (patch) | |
| tree | 249d43ff0f8d880cb5d00c236993f8ff0c1f10d8 /src/library/text | |
| parent | f547c97072881069417acd3b79b08fb7ecf40ba2 (diff) | |
More dynamic content representation
Diffstat (limited to 'src/library/text')
| -rw-r--r-- | src/library/text/deco.rs | 6 | ||||
| -rw-r--r-- | src/library/text/link.rs | 22 | ||||
| -rw-r--r-- | src/library/text/mod.rs | 101 | ||||
| -rw-r--r-- | src/library/text/par.rs | 60 | ||||
| -rw-r--r-- | src/library/text/raw.rs | 21 | ||||
| -rw-r--r-- | src/library/text/repeat.rs | 24 | ||||
| -rw-r--r-- | src/library/text/shift.rs | 47 |
7 files changed, 156 insertions, 125 deletions
diff --git a/src/library/text/deco.rs b/src/library/text/deco.rs index ead928f6..158647f2 100644 --- a/src/library/text/deco.rs +++ b/src/library/text/deco.rs @@ -17,7 +17,7 @@ pub type StrikethroughNode = DecoNode<STRIKETHROUGH>; /// Typeset overlined text. pub type OverlineNode = DecoNode<OVERLINE>; -#[node(showable)] +#[node(Show)] impl<const L: DecoLine> DecoNode<L> { /// How to stroke the line. The text color and thickness are read from the /// font tables if `auto`. @@ -35,12 +35,12 @@ impl<const L: DecoLine> DecoNode<L> { pub const EVADE: bool = true; fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { - Ok(Content::show(Self(args.expect("body")?))) + Ok(Self(args.expect("body")?).pack()) } } impl<const L: DecoLine> Show for DecoNode<L> { - fn unguard(&self, sel: Selector) -> ShowNode { + fn unguard_parts(&self, sel: Selector) -> Content { Self(self.0.unguard(sel)).pack() } diff --git a/src/library/text/link.rs b/src/library/text/link.rs index 11d55956..1e9adc3e 100644 --- a/src/library/text/link.rs +++ b/src/library/text/link.rs @@ -17,7 +17,7 @@ impl LinkNode { } } -#[node(showable)] +#[node(Show)] impl LinkNode { /// The fill color of text in the link. Just the surrounding text color /// if `auto`. @@ -26,14 +26,12 @@ impl LinkNode { pub const UNDERLINE: Smart<bool> = Smart::Auto; fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { - Ok(Content::show({ - let dest = args.expect::<Destination>("destination")?; - let body = match dest { - Destination::Url(_) => args.eat()?, - Destination::Internal(_) => Some(args.expect("body")?), - }; - Self { dest, body } - })) + let dest = args.expect::<Destination>("destination")?; + let body = match dest { + Destination::Url(_) => args.eat()?, + Destination::Internal(_) => Some(args.expect("body")?), + }; + Ok(Self { dest, body }.pack()) } } @@ -50,7 +48,7 @@ castable! { } impl Show for LinkNode { - fn unguard(&self, sel: Selector) -> ShowNode { + fn unguard_parts(&self, sel: Selector) -> Content { Self { dest: self.dest.clone(), body: self.body.as_ref().map(|body| body.unguard(sel)), @@ -83,9 +81,9 @@ impl Show for LinkNode { text = text.trim_start_matches(prefix); } let shorter = text.len() < url.len(); - Content::Text(if shorter { text.into() } else { url.clone() }) + TextNode(if shorter { text.into() } else { url.clone() }).pack() } - Destination::Internal(_) => Content::Empty, + Destination::Internal(_) => Content::empty(), }) .styled(TextNode::LINK, Some(self.dest.clone()))) } diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs index d7d2ee38..18e747d0 100644 --- a/src/library/text/mod.rs +++ b/src/library/text/mod.rs @@ -5,7 +5,6 @@ mod link; mod par; mod quotes; mod raw; -mod repeat; mod shaping; mod shift; @@ -14,7 +13,6 @@ pub use link::*; pub use par::*; pub use quotes::*; pub use raw::*; -pub use repeat::*; pub use shaping::*; pub use shift::*; @@ -27,8 +25,8 @@ use crate::library::prelude::*; use crate::util::EcoString; /// A single run of text with the same style. -#[derive(Hash)] -pub struct TextNode; +#[derive(Debug, Clone, Hash)] +pub struct TextNode(pub EcoString); #[node] impl TextNode { @@ -142,7 +140,7 @@ impl TextNode { for item in args.items.iter().filter(|item| item.name.is_none()) { if EcoString::is(&item.value) { count += 1; - } else if Content::is(&item.value) { + } else if <Content as Cast<Spanned<Value>>>::is(&item.value) { content = true; } } @@ -433,6 +431,45 @@ impl Fold for Vec<(Tag, u32)> { } } +/// A text space. +#[derive(Debug, Clone, Hash)] +pub struct SpaceNode; + +#[node] +impl SpaceNode { + fn construct(_: &mut Vm, _: &mut Args) -> SourceResult<Content> { + Ok(Self.pack()) + } +} + +/// A line break. +#[derive(Debug, Clone, Hash)] +pub struct LinebreakNode { + pub justify: bool, +} + +#[node] +impl LinebreakNode { + fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { + let justify = args.named("justify")?.unwrap_or(false); + Ok(Self { justify }.pack()) + } +} + +/// A smart quote. +#[derive(Debug, Clone, Hash)] +pub struct SmartQuoteNode { + pub double: bool, +} + +#[node] +impl SmartQuoteNode { + fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { + let double = args.named("double")?.unwrap_or(true); + Ok(Self { double }.pack()) + } +} + /// Convert a string or content to lowercase. pub fn lower(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { case(Case::Lower, args) @@ -478,40 +515,19 @@ pub fn smallcaps(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true))) } -/// A toggle that turns on and off alternatingly if folded. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Toggle; - -impl Fold for Toggle { - type Output = bool; - - fn fold(self, outer: Self::Output) -> Self::Output { - !outer - } -} - -impl Fold for Decoration { - type Output = Vec<Self>; - - fn fold(self, mut outer: Self::Output) -> Self::Output { - outer.insert(0, self); - outer - } -} - /// Strong text, rendered in boldface by default. #[derive(Debug, Hash)] pub struct StrongNode(pub Content); -#[node(showable)] +#[node(Show)] impl StrongNode { fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { - Ok(Content::show(Self(args.expect("body")?))) + Ok(Self(args.expect("body")?).pack()) } } impl Show for StrongNode { - fn unguard(&self, sel: Selector) -> ShowNode { + fn unguard_parts(&self, sel: Selector) -> Content { Self(self.0.unguard(sel)).pack() } @@ -531,15 +547,15 @@ impl Show for StrongNode { #[derive(Debug, Hash)] pub struct EmphNode(pub Content); -#[node(showable)] +#[node(Show)] impl EmphNode { fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { - Ok(Content::show(Self(args.expect("body")?))) + Ok(Self(args.expect("body")?).pack()) } } impl Show for EmphNode { - fn unguard(&self, sel: Selector) -> ShowNode { + fn unguard_parts(&self, sel: Selector) -> Content { Self(self.0.unguard(sel)).pack() } @@ -554,3 +570,24 @@ impl Show for EmphNode { Ok(self.0.clone().styled(TextNode::ITALIC, Toggle)) } } + +/// A toggle that turns on and off alternatingly if folded. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Toggle; + +impl Fold for Toggle { + type Output = bool; + + fn fold(self, outer: Self::Output) -> Self::Output { + !outer + } +} + +impl Fold for Decoration { + type Output = Vec<Self>; + + fn fold(self, mut outer: Self::Output) -> Self::Output { + outer.insert(0, self); + outer + } +} diff --git a/src/library/text/par.rs b/src/library/text/par.rs index 477bf97f..7c862366 100644 --- a/src/library/text/par.rs +++ b/src/library/text/par.rs @@ -1,10 +1,10 @@ use std::cmp::Ordering; -use unicode_bidi::{BidiInfo, Level}; +use unicode_bidi::{BidiInfo, Level as BidiLevel}; use unicode_script::{Script, UnicodeScript}; use xi_unicode::LineBreakIterator; -use super::{shape, Lang, Quoter, Quotes, RepeatNode, ShapedText, TextNode}; +use super::{shape, Lang, Quoter, Quotes, ShapedText, TextNode}; use crate::library::layout::Spacing; use crate::library::prelude::*; use crate::util::EcoString; @@ -23,10 +23,10 @@ pub enum ParChild { /// Horizontal spacing between other children. Spacing(Spacing), /// An arbitrary inline-level node. - Node(LayoutNode), + Node(Content), } -#[node] +#[node(Layout)] impl ParNode { /// The spacing between lines. #[property(resolve)] @@ -54,9 +54,9 @@ impl ParNode { // node. Instead, it just ensures that the passed content lives is in a // separate paragraph and styles it. Ok(Content::sequence(vec![ - Content::Parbreak, + ParbreakNode.pack(), args.expect("body")?, - Content::Parbreak, + ParbreakNode.pack(), ])) } } @@ -82,6 +82,10 @@ impl Layout for ParNode { // Stack the lines into one frame per region. stack(&p, world, &lines, regions) } + + fn level(&self) -> Level { + Level::Block + } } impl Debug for ParNode { @@ -166,23 +170,39 @@ impl Resolve for Smart<Linebreaks> { } /// A paragraph break. +#[derive(Debug, Clone, Hash)] pub struct ParbreakNode; #[node] impl ParbreakNode { fn construct(_: &mut Vm, _: &mut Args) -> SourceResult<Content> { - Ok(Content::Parbreak) + Ok(Self.pack()) } } -/// A line break. -pub struct LinebreakNode; +/// A node that should be repeated to fill up a line. +#[derive(Debug, Hash)] +pub struct RepeatNode(pub Content); -#[node] -impl LinebreakNode { +#[node(Layout)] +impl RepeatNode { fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { - let justify = args.named("justify")?.unwrap_or(false); - Ok(Content::Linebreak { justify }) + Ok(Self(args.expect("body")?).pack()) + } +} + +impl Layout for RepeatNode { + fn layout( + &self, + world: Tracked<dyn World>, + regions: &Regions, + styles: StyleChain, + ) -> SourceResult<Vec<Frame>> { + self.0.layout_inline(world, regions, styles) + } + + fn level(&self) -> Level { + Level::Inline } } @@ -272,7 +292,7 @@ enum Segment<'a> { /// Horizontal spacing between other segments. Spacing(Spacing), /// An arbitrary inline-level layout node. - Node(&'a LayoutNode), + Node(&'a Content), } impl Segment<'_> { @@ -504,8 +524,8 @@ fn prepare<'a>( styles: StyleChain<'a>, ) -> SourceResult<Preparation<'a>> { let bidi = BidiInfo::new(&text, match styles.get(TextNode::DIR) { - Dir::LTR => Some(Level::ltr()), - Dir::RTL => Some(Level::rtl()), + Dir::LTR => Some(BidiLevel::ltr()), + Dir::RTL => Some(BidiLevel::rtl()), _ => None, }); @@ -529,12 +549,12 @@ fn prepare<'a>( } }, Segment::Node(node) => { - if let Some(repeat) = node.downcast() { + if let Some(repeat) = node.downcast::<RepeatNode>() { items.push(Item::Repeat(repeat, styles)); } else { let size = Size::new(regions.first.x, regions.base.y); let pod = Regions::one(size, regions.base, Axes::splat(false)); - let mut frame = node.layout(world, &pod, styles)?.remove(0); + let mut frame = node.layout_inline(world, &pod, styles)?.remove(0); frame.translate(Point::with_y(styles.get(TextNode::BASELINE))); frame.apply_role(Role::GenericInline); items.push(Item::Frame(frame)); @@ -566,13 +586,13 @@ fn shape_range<'a>( range: Range, styles: StyleChain<'a>, ) { - let mut process = |text, level: Level| { + let mut process = |text, level: BidiLevel| { let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL }; let shaped = shape(world, text, styles, dir); items.push(Item::Text(shaped)); }; - let mut prev_level = Level::ltr(); + let mut prev_level = BidiLevel::ltr(); let mut prev_script = Script::Unknown; let mut cursor = range.start; diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs index 85e8133c..0c769636 100644 --- a/src/library/text/raw.rs +++ b/src/library/text/raw.rs @@ -5,8 +5,8 @@ use syntect::highlighting::{ }; use syntect::parsing::SyntaxSet; -use super::{FontFamily, Hyphenate, TextNode}; -use crate::library::layout::BlockSpacing; +use super::{FontFamily, Hyphenate, LinebreakNode, TextNode}; +use crate::library::layout::{BlockNode, BlockSpacing}; use crate::library::prelude::*; /// Monospaced text with optional syntax highlighting. @@ -18,7 +18,7 @@ pub struct RawNode { pub block: bool, } -#[node(showable)] +#[node(Show)] impl RawNode { /// The language to syntax-highlight in. #[property(referenced)] @@ -34,15 +34,16 @@ impl RawNode { pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into()); fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { - Ok(Content::show(Self { + Ok(Self { text: args.expect("text")?, block: args.named("block")?.unwrap_or(false), - })) + } + .pack()) } } impl Show for RawNode { - fn unguard(&self, _: Selector) -> ShowNode { + fn unguard_parts(&self, _: Selector) -> Content { Self { text: self.text.clone(), ..*self }.pack() } @@ -86,7 +87,7 @@ impl Show for RawNode { let mut highlighter = HighlightLines::new(syntax, &THEME); for (i, line) in self.text.lines().enumerate() { if i != 0 { - seq.push(Content::Linebreak { justify: false }); + seq.push(LinebreakNode { justify: false }.pack()); } for (style, piece) in @@ -98,11 +99,11 @@ impl Show for RawNode { Content::sequence(seq) } else { - Content::Text(self.text.clone()) + TextNode(self.text.clone()).pack() }; if self.block { - realized = Content::block(realized); + realized = BlockNode(realized).pack(); } let mut map = StyleMap::new(); @@ -132,7 +133,7 @@ impl Show for RawNode { /// Style a piece of text with a syntect style. fn styled(piece: &str, foreground: Paint, style: Style) -> Content { - let mut body = Content::Text(piece.into()); + let mut body = TextNode(piece.into()).pack(); let paint = style.foreground.into(); if paint != foreground { diff --git a/src/library/text/repeat.rs b/src/library/text/repeat.rs deleted file mode 100644 index e3bae3fc..00000000 --- a/src/library/text/repeat.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::library::prelude::*; - -/// A node that should be repeated to fill up a line. -#[derive(Debug, Hash)] -pub struct RepeatNode(pub LayoutNode); - -#[node] -impl RepeatNode { - fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { - Ok(Content::inline(Self(args.expect("body")?))) - } -} - -impl Layout for RepeatNode { - fn layout( - &self, - world: Tracked<dyn World>, - regions: &Regions, - styles: StyleChain, - ) -> SourceResult<Vec<Frame>> { - // The actual repeating happens directly in the paragraph. - self.0.layout(world, regions, styles) - } -} diff --git a/src/library/text/shift.rs b/src/library/text/shift.rs index e2a636a6..c3cf8b03 100644 --- a/src/library/text/shift.rs +++ b/src/library/text/shift.rs @@ -1,5 +1,6 @@ -use super::{variant, TextNode, TextSize}; +use super::{variant, SpaceNode, TextNode, TextSize}; use crate::library::prelude::*; +use crate::model::SequenceNode; use crate::util::EcoString; /// Sub or superscript text. @@ -17,7 +18,7 @@ pub type SuperNode = ShiftNode<SUPERSCRIPT>; /// Shift the text into subscript. pub type SubNode = ShiftNode<SUBSCRIPT>; -#[node] +#[node(Show)] impl<const S: ScriptKind> ShiftNode<S> { /// Whether to prefer the dedicated sub- and superscript characters of the /// font. @@ -29,12 +30,12 @@ impl<const S: ScriptKind> ShiftNode<S> { pub const SIZE: TextSize = TextSize(Em::new(0.6).into()); fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { - Ok(Content::show(Self(args.expect("body")?))) + Ok(Self(args.expect("body")?).pack()) } } impl<const S: ScriptKind> Show for ShiftNode<S> { - fn unguard(&self, _: Selector) -> ShowNode { + fn unguard_parts(&self, _: Selector) -> Content { Self(self.0.clone()).pack() } @@ -54,7 +55,7 @@ impl<const S: ScriptKind> Show for ShiftNode<S> { if styles.get(Self::TYPOGRAPHIC) { if let Some(text) = search_text(&self.0, S) { if is_shapable(world, &text, styles) { - transformed = Some(Content::Text(text)); + transformed = Some(TextNode(text).pack()); } } }; @@ -71,28 +72,26 @@ impl<const S: ScriptKind> Show for ShiftNode<S> { /// Find and transform the text contained in `content` to the given script kind /// if and only if it only consists of `Text`, `Space`, and `Empty` leaf nodes. fn search_text(content: &Content, mode: ScriptKind) -> Option<EcoString> { - match content { - Content::Text(_) => { - if let Content::Text(t) = content { - if let Some(sup) = convert_script(t, mode) { - return Some(sup); - } - } - None + if content.is_empty() { + Some(EcoString::new()) + } else if content.is::<SpaceNode>() { + Some(' '.into()) + } else if let Some(text) = content.downcast::<TextNode>() { + if let Some(sup) = convert_script(&text.0, mode) { + return Some(sup); } - Content::Space => Some(' '.into()), - Content::Empty => Some(EcoString::new()), - Content::Sequence(seq) => { - let mut full = EcoString::new(); - for item in seq.iter() { - match search_text(item, mode) { - Some(text) => full.push_str(&text), - None => return None, - } + None + } else if let Some(seq) = content.downcast::<SequenceNode>() { + let mut full = EcoString::new(); + for item in seq.0.iter() { + match search_text(item, mode) { + Some(text) => full.push_str(&text), + None => return None, } - Some(full) } - _ => None, + Some(full) + } else { + None } } |
