Skip to content

Commit

Permalink
Refactor frame metadata into tags
Browse files Browse the repository at this point in the history
  • Loading branch information
laurmaedje committed May 21, 2024
1 parent 4f8bbb0 commit cfed1c5
Show file tree
Hide file tree
Showing 24 changed files with 248 additions and 222 deletions.
3 changes: 1 addition & 2 deletions crates/typst-ide/src/jump.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::num::NonZeroUsize;

use ecow::EcoString;
use typst::introspection::Meta;
use typst::layout::{Frame, FrameItem, Point, Position, Size};
use typst::model::{Destination, Document};
use typst::syntax::{FileId, LinkedNode, Side, Source, Span, SyntaxKind};
Expand Down Expand Up @@ -37,7 +36,7 @@ pub fn jump_from_click(
) -> Option<Jump> {
// Try to find a link first.
for (pos, item) in frame.items() {
if let FrameItem::Meta(Meta::Link(dest), size) = item {
if let FrameItem::Link(dest, size) = item {
if is_in_rect(*pos, *size, click) {
return Some(match dest {
Destination::Url(url) => Jump::Url(url.clone()),
Expand Down
8 changes: 2 additions & 6 deletions crates/typst-pdf/src/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use pdf_writer::types::{
};
use pdf_writer::writers::{PageLabel, Resources};
use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str, TextStr};
use typst::introspection::Meta;
use typst::layout::{
Abs, Em, Frame, FrameItem, GroupItem, Page, Point, Ratio, Size, Transform,
};
Expand Down Expand Up @@ -749,11 +748,8 @@ pub(crate) fn write_frame(ctx: &mut PageContext, frame: &Frame) {
FrameItem::Text(text) => write_text(ctx, pos, text),
FrameItem::Shape(shape, _) => write_shape(ctx, pos, shape),
FrameItem::Image(image, size, _) => write_image(ctx, x, y, image, *size),
FrameItem::Meta(meta, size) => match meta {
Meta::Link(dest) => write_link(ctx, pos, dest, *size),
Meta::Elem(_) => {}
Meta::Hide => {}
},
FrameItem::Link(dest, size) => write_link(ctx, pos, dest, *size),
FrameItem::Tag(_) => {}
}
}
}
Expand Down
8 changes: 2 additions & 6 deletions crates/typst-render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ mod shape;
mod text;

use tiny_skia as sk;
use typst::introspection::Meta;
use typst::layout::{
Abs, Axes, Frame, FrameItem, FrameKind, GroupItem, Point, Size, Transform,
};
Expand Down Expand Up @@ -162,11 +161,8 @@ fn render_frame(canvas: &mut sk::Pixmap, state: State, frame: &Frame) {
FrameItem::Image(image, size, _) => {
image::render_image(canvas, state.pre_translate(*pos), image, *size);
}
FrameItem::Meta(meta, _) => match meta {
Meta::Link(_) => {}
Meta::Elem(_) => {}
Meta::Hide => {}
},
FrameItem::Link(_, _) => {}
FrameItem::Tag(_) => {}
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions crates/typst-svg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,9 @@ impl SVGRenderer {
}

for (pos, item) in frame.items() {
// File size optimization
if matches!(item, FrameItem::Meta(_, _)) {
// File size optimization.
// TODO: SVGs could contain links, couldn't they?
if matches!(item, FrameItem::Link(_, _) | FrameItem::Tag(_)) {
continue;
}

Expand All @@ -206,7 +207,8 @@ impl SVGRenderer {
self.render_shape(state.pre_translate(*pos), shape)
}
FrameItem::Image(image, size, _) => self.render_image(image, size),
FrameItem::Meta(_, _) => unreachable!(),
FrameItem::Link(_, _) => unreachable!(),
FrameItem::Tag(_) => unreachable!(),
};

self.xml.end_element();
Expand Down
10 changes: 5 additions & 5 deletions crates/typst/src/foundations/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ use crate::foundations::{
NativeElement, Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles,
Value,
};
use crate::introspection::{Location, Meta, MetaElem};
use crate::introspection::{Location, TagElem};
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
use crate::model::{Destination, EmphElem, StrongElem};
use crate::model::{Destination, EmphElem, LinkElem, StrongElem};
use crate::realize::{Behave, Behaviour};
use crate::syntax::Span;
use crate::text::UnderlineElem;
Expand Down Expand Up @@ -472,16 +472,16 @@ impl Content {

/// Link the content somewhere.
pub fn linked(self, dest: Destination) -> Self {
self.styled(MetaElem::set_data(smallvec![Meta::Link(dest)]))
self.styled(LinkElem::set_dests(smallvec![dest]))
}

/// Make the content linkable by `.linked(Destination::Location(loc))`.
///
/// Should be used in combination with [`Location::variant`].
pub fn backlinked(self, loc: Location) -> Self {
let mut backlink = Content::empty();
let mut backlink = Content::empty().spanned(self.span());
backlink.set_location(loc);
self.styled(MetaElem::set_data(smallvec![Meta::Elem(backlink)]))
TagElem::packed(backlink) + self
}

/// Set alignments for this content.
Expand Down
4 changes: 2 additions & 2 deletions crates/typst/src/introspection/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::foundations::{
Element, Func, IntoValue, Label, LocatableSelector, NativeElement, Packed, Repr,
Selector, Show, Smart, Str, StyleChain, Value,
};
use crate::introspection::{Introspector, Locatable, Location, Locator, Meta};
use crate::introspection::{Introspector, Locatable, Location, Locator};
use crate::layout::{Frame, FrameItem, PageElem};
use crate::math::EquationElem;
use crate::model::{FigureElem, HeadingElem, Numbering, NumberingPattern};
Expand Down Expand Up @@ -820,7 +820,7 @@ impl ManualPageCounter {
for (_, item) in page.items() {
match item {
FrameItem::Group(group) => self.visit(engine, &group.frame)?,
FrameItem::Meta(Meta::Elem(elem), _) => {
FrameItem::Tag(elem) => {
let Some(elem) = elem.to_packed::<CounterUpdateElem>() else {
continue;
};
Expand Down
12 changes: 6 additions & 6 deletions crates/typst/src/introspection/introspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use smallvec::SmallVec;

use crate::diag::{bail, StrResult};
use crate::foundations::{Content, Label, Repr, Selector};
use crate::introspection::{Location, Meta};
use crate::introspection::Location;
use crate::layout::{Frame, FrameItem, Page, Point, Position, Transform};
use crate::model::Numbering;
use crate::utils::NonZeroExt;
Expand Down Expand Up @@ -61,18 +61,18 @@ impl Introspector {
.pre_concat(group.transform);
self.extract(&group.frame, page, ts);
}
FrameItem::Meta(Meta::Elem(content), _)
if !self.elems.contains_key(&content.location().unwrap()) =>
FrameItem::Tag(elem)
if !self.elems.contains_key(&elem.location().unwrap()) =>
{
let pos = pos.transform(ts);
let ret = self.elems.insert(
content.location().unwrap(),
(content.clone(), Position { page, point: pos }),
elem.location().unwrap(),
(elem.clone(), Position { page, point: pos }),
);
assert!(ret.is_none(), "duplicate locations");

// Build the label cache.
if let Some(label) = content.label() {
if let Some(label) = elem.label() {
self.labels.entry(label).or_default().push(self.elems.len() - 1);
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/typst/src/introspection/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashMap;

use comemo::{Track, Tracked, Validate};

use crate::introspection::{Location, Meta};
use crate::introspection::Location;
use crate::layout::{Frame, FrameItem};

/// Provides locations for elements in the document.
Expand Down Expand Up @@ -77,7 +77,7 @@ impl<'a> Locator<'a> {
for (_, item) in frame.items() {
match item {
FrameItem::Group(group) => self.visit_frame(&group.frame),
FrameItem::Meta(Meta::Elem(elem), _) => {
FrameItem::Tag(elem) => {
let hashes = self.hashes.get_mut();
let loc = elem.location().unwrap();
let entry = hashes.entry(loc.hash).or_default();
Expand Down
83 changes: 30 additions & 53 deletions crates/typst/src/introspection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ pub use self::metadata::*;
pub use self::query_::*;
pub use self::state::*;

use std::fmt::{self, Debug, Formatter};

use ecow::{eco_format, EcoString};
use smallvec::SmallVec;

use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::NativeElement;
use crate::foundations::{
category, elem, ty, Category, Content, Packed, Repr, Scope, Unlabellable,
category, elem, Args, Category, Construct, Content, Packed, Scope, Unlabellable,
};
use crate::model::Destination;
use crate::realize::{Behave, Behaviour};

/// Interactions between document parts.
Expand Down Expand Up @@ -59,59 +56,39 @@ pub fn define(global: &mut Scope) {
global.define_func::<locate>();
}

/// Hosts metadata and ensures metadata is produced even for empty elements.
#[elem(Behave, Unlabellable)]
pub struct MetaElem {
/// Metadata that should be attached to all elements affected by this style
/// property.
///
/// This must be accessed and applied to all frames produced by elements
/// that manually handle styles (because their children can have varying
/// styles). This currently includes flow, par, and equation.
///
/// Other elements don't manually need to handle it because their parents
/// that result from realization will take care of it and the metadata can
/// only apply to them as a whole, not part of it (because they don't manage
/// styles).
#[fold]
pub data: SmallVec<[Meta; 1]>,
/// Holds a locatable element that was realized.
///
/// The `TagElem` is handled by all layouters. The held element becomes
/// available for introspection in the next compiler iteration.
#[elem(Behave, Unlabellable, Construct)]
pub struct TagElem {
/// The introspectible element.
#[required]
#[internal]
pub elem: Content,
}

impl Unlabellable for Packed<MetaElem> {}

impl Behave for Packed<MetaElem> {
fn behaviour(&self) -> Behaviour {
Behaviour::Invisible
impl TagElem {
/// Create a packed tag element.
pub fn packed(elem: Content) -> Content {
let span = elem.span();
let mut content = Self::new(elem).pack().spanned(span);
// We can skip preparation for the `TagElem`.
content.mark_prepared();
content
}
}

/// Meta information that isn't visible or renderable.
#[ty]
#[derive(Clone, PartialEq, Hash)]
pub enum Meta {
/// An internal or external link to a destination.
Link(Destination),
/// An identifiable element that produces something within the area this
/// metadata is attached to.
Elem(Content),
/// Indicates that content should be hidden. This variant doesn't appear
/// in the final frames as it is removed alongside the content that should
/// be hidden.
Hide,
}

impl Debug for Meta {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Link(dest) => write!(f, "Link({dest:?})"),
Self::Elem(content) => write!(f, "Elem({:?})", content.func()),
Self::Hide => f.pad("Hide"),
}
impl Construct for TagElem {
fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
bail!(args.span, "cannot be constructed manually")
}
}

impl Repr for Meta {
fn repr(&self) -> EcoString {
eco_format!("{self:?}")
impl Unlabellable for Packed<TagElem> {}

impl Behave for Packed<TagElem> {
fn behaviour(&self) -> Behaviour {
Behaviour::Invisible
}
}

0 comments on commit cfed1c5

Please sign in to comment.