summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-17 11:32:15 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-17 11:45:57 +0100
commit312197b276748e1a17258ad21837850f582a467c (patch)
tree3fd0c078a2673a98b74bc12b4d654a4c143b4e1f /src
parente8435df5ec718e8ecc8a2ad48e4eb3ddd1f92a72 (diff)
Counters
Diffstat (limited to 'src')
-rw-r--r--src/doc.rs66
-rw-r--r--src/eval/library.rs11
-rw-r--r--src/eval/methods.rs15
-rw-r--r--src/export/pdf/page.rs6
-rw-r--r--src/geom/align.rs20
-rw-r--r--src/ide/analyze.rs2
-rw-r--r--src/ide/jump.rs7
-rw-r--r--src/model/content.rs48
-rw-r--r--src/model/realize.rs52
-rw-r--r--src/model/styles.rs16
-rw-r--r--src/model/typeset.rs168
-rw-r--r--src/syntax/ast.rs3
-rw-r--r--src/util/mod.rs14
13 files changed, 243 insertions, 185 deletions
diff --git a/src/doc.rs b/src/doc.rs
index 03885b03..f575ff1f 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -14,7 +14,7 @@ use crate::geom::{
Numeric, Paint, Point, Rel, RgbaColor, Shape, Sides, Size, Stroke, Transform,
};
use crate::image::Image;
-use crate::model::{node, Content, Fold, Introspector, StableId, StyleChain};
+use crate::model::{Content, Introspector, MetaNode, StableId, StyleChain};
use crate::syntax::Span;
/// A finished document with metadata and page frames.
@@ -271,16 +271,15 @@ impl Frame {
}
/// Attach the metadata from this style chain to the frame.
- pub fn meta(&mut self, styles: StyleChain) {
- if self.is_empty() {
- return;
- }
- for meta in MetaNode::data_in(styles) {
- if matches!(meta, Meta::Hide) {
- self.clear();
- break;
+ pub fn meta(&mut self, styles: StyleChain, force: bool) {
+ if force || !self.is_empty() {
+ for meta in MetaNode::data_in(styles) {
+ if matches!(meta, Meta::Hide) {
+ self.clear();
+ break;
+ }
+ self.prepend(Point::zero(), Element::Meta(meta, self.size));
}
- self.prepend(Point::zero(), Element::Meta(meta, self.size));
}
}
@@ -607,6 +606,16 @@ pub enum Meta {
Node(Content),
}
+cast_from_value! {
+ Meta: "meta",
+}
+
+impl PartialEq for Meta {
+ fn eq(&self, other: &Self) -> bool {
+ crate::util::hash128(self) == crate::util::hash128(other)
+ }
+}
+
/// A possibly unresolved link.
#[derive(Debug, Clone, Hash)]
pub enum Link {
@@ -623,45 +632,14 @@ impl Link {
pub fn resolve<'a>(
&self,
introspector: impl FnOnce() -> &'a Introspector,
- ) -> Option<Destination> {
+ ) -> Destination {
match self {
- Self::Dest(dest) => Some(dest.clone()),
- Self::Node(id) => introspector().location(*id).map(Destination::Internal),
+ Self::Dest(dest) => dest.clone(),
+ Self::Node(id) => Destination::Internal(introspector().location(*id)),
}
}
}
-/// Host for metadata.
-///
-/// Display: Meta
-/// Category: special
-#[node]
-pub struct MetaNode {
- /// Metadata that should be attached to all elements affected by this style
- /// property.
- #[fold]
- pub data: Vec<Meta>,
-}
-
-impl Fold for Vec<Meta> {
- type Output = Self;
-
- fn fold(mut self, outer: Self::Output) -> Self::Output {
- self.extend(outer);
- self
- }
-}
-
-cast_from_value! {
- Meta: "meta",
-}
-
-impl PartialEq for Meta {
- fn eq(&self, other: &Self) -> bool {
- crate::util::hash128(self) == crate::util::hash128(other)
- }
-}
-
/// A link destination.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Destination {
diff --git a/src/eval/library.rs b/src/eval/library.rs
index 1240d9bb..45c23d17 100644
--- a/src/eval/library.rs
+++ b/src/eval/library.rs
@@ -6,11 +6,12 @@ use comemo::Tracked;
use ecow::EcoString;
use once_cell::sync::OnceCell;
-use super::Module;
+use super::{Args, Dynamic, Module, Value};
use crate::diag::SourceResult;
use crate::doc::Document;
use crate::geom::{Abs, Dir};
use crate::model::{Content, Introspector, Label, NodeId, StyleChain, StyleMap, Vt};
+use crate::syntax::Span;
use crate::util::hash128;
use crate::World;
@@ -89,6 +90,14 @@ pub struct LangItems {
pub math_accent: fn(base: Content, accent: char) -> Content,
/// A fraction in a formula: `x/2`.
pub math_frac: fn(num: Content, denom: Content) -> Content,
+ /// Dispatch a method on a counter. This is hacky and should be superseded
+ /// by more dynamic method dispatch.
+ pub counter_method: fn(
+ dynamic: &Dynamic,
+ method: &str,
+ args: Args,
+ span: Span,
+ ) -> SourceResult<Value>,
}
impl Debug for LangItems {
diff --git a/src/eval/methods.rs b/src/eval/methods.rs
index 197a2f65..a449ac16 100644
--- a/src/eval/methods.rs
+++ b/src/eval/methods.rs
@@ -134,6 +134,14 @@ pub fn call(
_ => return missing(),
},
+ Value::Dyn(dynamic) => {
+ if dynamic.type_name() == "counter" {
+ return (vm.items.counter_method)(&dynamic, method, args, span);
+ }
+
+ return missing();
+ }
+
_ => return missing(),
};
@@ -281,6 +289,13 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] {
],
"function" => &[("where", true), ("with", true)],
"arguments" => &[("named", false), ("pos", false)],
+ "counter" => &[
+ ("get", true),
+ ("final", true),
+ ("both", true),
+ ("step", true),
+ ("update", true),
+ ],
_ => &[],
}
}
diff --git a/src/export/pdf/page.rs b/src/export/pdf/page.rs
index df7b517f..5347d831 100644
--- a/src/export/pdf/page.rs
+++ b/src/export/pdf/page.rs
@@ -114,11 +114,7 @@ fn write_page(ctx: &mut PdfContext, page: Page) {
let mut annotation = annotations.push();
annotation.subtype(AnnotationType::Link).rect(rect);
annotation.border(0.0, 0.0, 0.0, None);
-
- let dest = link.resolve(|| &ctx.introspector);
- let Some(dest) = dest else { continue };
-
- match dest {
+ match link.resolve(|| &ctx.introspector) {
Destination::Url(uri) => {
annotation
.action()
diff --git a/src/geom/align.rs b/src/geom/align.rs
index b14e6775..239a6e70 100644
--- a/src/geom/align.rs
+++ b/src/geom/align.rs
@@ -143,6 +143,26 @@ cast_to_value! {
}
}
+impl From<Axes<GenAlign>> for Axes<Option<GenAlign>> {
+ fn from(axes: Axes<GenAlign>) -> Self {
+ axes.map(Some)
+ }
+}
+
+impl From<Axes<Align>> for Axes<Option<GenAlign>> {
+ fn from(axes: Axes<Align>) -> Self {
+ axes.map(GenAlign::Specific).into()
+ }
+}
+
+impl From<Align> for Axes<Option<GenAlign>> {
+ fn from(align: Align) -> Self {
+ let mut axes = Axes::splat(None);
+ axes.set(align.axis(), Some(align.into()));
+ axes
+ }
+}
+
impl Resolve for GenAlign {
type Output = Align;
diff --git a/src/ide/analyze.rs b/src/ide/analyze.rs
index ed868e53..ccb89a9c 100644
--- a/src/ide/analyze.rs
+++ b/src/ide/analyze.rs
@@ -78,7 +78,7 @@ pub fn analyze_labels(
let items = &world.library().items;
// Labels in the document.
- for node in introspector.nodes() {
+ for node in introspector.all() {
let Some(label) = node.label() else { continue };
let details = node
.field("caption")
diff --git a/src/ide/jump.rs b/src/ide/jump.rs
index 0aa97b56..17e318a7 100644
--- a/src/ide/jump.rs
+++ b/src/ide/jump.rs
@@ -36,12 +36,9 @@ pub fn jump_from_click(
for (pos, element) in frame.elements() {
if let Element::Meta(Meta::Link(link), size) = element {
if is_in_rect(*pos, *size, click) {
- let dest = link.resolve(|| {
+ return Some(Jump::Dest(link.resolve(|| {
introspector.get_or_insert_with(|| Introspector::new(frames))
- });
-
- let Some(dest) = dest else { continue };
- return Some(Jump::Dest(dest));
+ })));
}
}
}
diff --git a/src/model/content.rs b/src/model/content.rs
index 58b80487..11ad635f 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -8,8 +8,12 @@ use comemo::Tracked;
use ecow::{eco_format, EcoString, EcoVec};
use once_cell::sync::Lazy;
-use super::{node, Guard, Locatable, Recipe, StableId, Style, StyleMap, Synthesize};
+use super::{
+ node, Behave, Behaviour, Fold, Guard, Locatable, Recipe, StableId, Style, StyleMap,
+ Synthesize,
+};
use crate::diag::{SourceResult, StrResult};
+use crate::doc::Meta;
use crate::eval::{
cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm,
};
@@ -35,9 +39,15 @@ enum Modifier {
}
impl Content {
+ /// Create a content of the given node kind.
pub fn new<T: Node>() -> Self {
+ Self::new_of(T::id())
+ }
+
+ /// Create a content of the given node kind.
+ pub fn new_of(id: NodeId) -> Self {
Self {
- id: T::id(),
+ id,
span: Span::detached(),
fields: EcoVec::new(),
modifiers: EcoVec::new(),
@@ -133,11 +143,10 @@ impl Content {
.map(|(_, value)| value)
}
- /// Access a field on the content as a specified type.
- #[track_caller]
+ /// Try to access a field on the content as a specified type.
pub fn cast_field<T: Cast>(&self, name: &str) -> Option<T> {
match self.field(name) {
- Some(value) => Some(value.clone().cast().unwrap()),
+ Some(value) => value.clone().cast().ok(),
None => None,
}
}
@@ -145,7 +154,7 @@ impl Content {
/// Expect a field on the content to exist as a specified type.
#[track_caller]
pub fn expect_field<T: Cast>(&self, name: &str) -> T {
- self.cast_field(name).unwrap()
+ self.field(name).unwrap().clone().cast().unwrap()
}
/// List all fields on the content.
@@ -500,6 +509,33 @@ cast_from_value! {
StyleMap: "style map",
}
+/// Host for metadata.
+///
+/// Display: Meta
+/// Category: special
+#[node(Behave)]
+pub struct MetaNode {
+ /// Metadata that should be attached to all elements affected by this style
+ /// property.
+ #[fold]
+ pub data: Vec<Meta>,
+}
+
+impl Behave for MetaNode {
+ fn behaviour(&self) -> Behaviour {
+ Behaviour::Ignorant
+ }
+}
+
+impl Fold for Vec<Meta> {
+ type Output = Self;
+
+ fn fold(mut self, outer: Self::Output) -> Self::Output {
+ self.extend(outer);
+ self
+ }
+}
+
/// The missing key access error message.
#[cold]
#[track_caller]
diff --git a/src/model/realize.rs b/src/model/realize.rs
index 7a171cfc..70c75644 100644
--- a/src/model/realize.rs
+++ b/src/model/realize.rs
@@ -1,6 +1,7 @@
-use super::{Content, NodeId, Recipe, Selector, StyleChain, Vt};
+use super::{Content, MetaNode, Node, NodeId, Recipe, Selector, StyleChain, Vt};
use crate::diag::SourceResult;
-use crate::doc::{Meta, MetaNode};
+use crate::doc::Meta;
+use crate::util::hash128;
/// Whether the target is affected by show rules in the given style chain.
pub fn applicable(target: &Content, styles: StyleChain) -> bool {
@@ -36,7 +37,7 @@ pub fn realize(
if target.needs_preparation() {
let mut node = target.clone();
if target.can::<dyn Locatable>() || target.label().is_some() {
- let id = vt.identify(target);
+ let id = vt.provider.identify(hash128(target));
node.set_stable_id(id);
}
@@ -47,8 +48,12 @@ pub fn realize(
node.mark_prepared();
if node.stable_id().is_some() {
+ let span = node.span();
let meta = Meta::Node(node.clone());
- return Ok(Some(node.styled(MetaNode::set_data(vec![meta]))));
+ return Ok(Some(
+ (node + MetaNode::new().pack().spanned(span))
+ .styled(MetaNode::set_data(vec![meta])),
+ ));
}
return Ok(Some(node));
@@ -103,7 +108,7 @@ fn try_apply(
return Ok(None);
}
- recipe.apply(vt.world(), target.clone().guarded(guard)).map(Some)
+ recipe.apply(vt.world, target.clone().guarded(guard)).map(Some)
}
Some(Selector::Label(label)) => {
@@ -111,7 +116,7 @@ fn try_apply(
return Ok(None);
}
- recipe.apply(vt.world(), target.clone().guarded(guard)).map(Some)
+ recipe.apply(vt.world, target.clone().guarded(guard)).map(Some)
}
Some(Selector::Regex(regex)) => {
@@ -135,7 +140,7 @@ fn try_apply(
}
let piece = make(m.as_str().into()).guarded(guard);
- let transformed = recipe.apply(vt.world(), piece)?;
+ let transformed = recipe.apply(vt.world, piece)?;
result.push(transformed);
cursor = m.end();
}
@@ -151,6 +156,9 @@ fn try_apply(
Ok(Some(Content::sequence(result)))
}
+ // Not supported here.
+ Some(Selector::Any(_)) => Ok(None),
+
None => Ok(None),
}
}
@@ -178,6 +186,36 @@ pub trait Finalize {
fn finalize(&self, realized: Content, styles: StyleChain) -> Content;
}
+/// How a node interacts with other nodes.
+pub trait Behave {
+ /// The node's interaction behaviour.
+ fn behaviour(&self) -> Behaviour;
+
+ /// Whether this weak node is larger than a previous one and thus picked as
+ /// the maximum when the levels are the same.
+ #[allow(unused_variables)]
+ fn larger(&self, prev: &Content) -> bool {
+ false
+ }
+}
+
+/// How a node interacts with other nodes in a stream.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Behaviour {
+ /// A weak node which only survives when a supportive node is before and
+ /// after it. Furthermore, per consecutive run of weak nodes, only one
+ /// survives: The one with the lowest weakness level (or the larger one if
+ /// there is a tie).
+ Weak(usize),
+ /// A node that enables adjacent weak nodes to exist. The default.
+ Supportive,
+ /// A node that destroys adjacent weak nodes.
+ Destructive,
+ /// A node that does not interact at all with other nodes, having the
+ /// same effect as if it didn't exist.
+ Ignorant,
+}
+
/// Guards content against being affected by the same show rule multiple times.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Guard {
diff --git a/src/model/styles.rs b/src/model/styles.rs
index 9a562c75..359f1461 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::iter;
use comemo::Tracked;
-use ecow::{eco_format, EcoString};
+use ecow::{eco_format, EcoString, EcoVec};
use super::{Content, Label, Node, NodeId};
use crate::diag::{SourceResult, Trace, Tracepoint};
@@ -31,8 +31,8 @@ impl StyleMap {
/// If the property needs folding and the value is already contained in the
/// style map, `self` contributes the outer values and `value` is the inner
/// one.
- pub fn set(&mut self, property: Property) {
- self.0.push(Style::Property(property));
+ pub fn set(&mut self, style: impl Into<Style>) {
+ self.0.push(style.into());
}
/// Remove the style that was last set.
@@ -243,6 +243,8 @@ pub enum Selector {
Label(Label),
/// Matches text nodes through a regular expression.
Regex(Regex),
+ /// Matches if any of the subselectors match.
+ Any(EcoVec<Self>),
}
impl Selector {
@@ -271,6 +273,7 @@ impl Selector {
target.id() == item!(text_id)
&& item!(text_str)(target).map_or(false, |text| regex.is_match(&text))
}
+ Self::Any(selectors) => selectors.iter().any(|sel| sel.matches(target)),
}
}
}
@@ -288,6 +291,12 @@ impl Debug for Selector {
}
Self::Label(label) => label.fmt(f),
Self::Regex(regex) => regex.fmt(f),
+ Self::Any(selectors) => {
+ f.write_str("any")?;
+ let pieces: Vec<_> =
+ selectors.iter().map(|sel| eco_format!("{sel:?}")).collect();
+ f.write_str(&pretty_array_like(&pieces, false))
+ }
}
}
}
@@ -659,6 +668,7 @@ impl<T: Debug> Debug for StyleVec<T> {
}
/// Assists in the construction of a [`StyleVec`].
+#[derive(Debug)]
pub struct StyleVecBuilder<'a, T> {
items: Vec<T>,
chains: Vec<(StyleChain<'a>, usize)>,
diff --git a/src/model/typeset.rs b/src/model/typeset.rs
index f68d337d..4c8be135 100644
--- a/src/model/typeset.rs
+++ b/src/model/typeset.rs
@@ -1,15 +1,13 @@
-use std::cell::RefCell;
-use std::collections::HashMap;
use std::hash::Hash;
use std::num::NonZeroUsize;
-use comemo::{Track, Tracked, TrackedMut};
+use comemo::{Constraint, Track, Tracked, TrackedMut};
-use super::{Content, Node, Selector, StyleChain};
+use super::{Content, Selector, StyleChain};
use crate::diag::SourceResult;
use crate::doc::{Document, Element, Frame, Location, Meta};
-use crate::geom::Transform;
-use crate::util::hash128;
+use crate::geom::{Point, Transform};
+use crate::util::NonZeroExt;
use crate::World;
/// Typeset content into a fully layouted document.
@@ -25,17 +23,21 @@ pub fn typeset(world: Tracked<dyn World>, content: &Content) -> SourceResult<Doc
// Relayout until all introspections stabilize.
// If that doesn't happen within five attempts, we give up.
loop {
+ let constraint = Constraint::new();
let mut provider = StabilityProvider::new();
let mut vt = Vt {
world,
provider: provider.track_mut(),
- introspector: introspector.track(),
+ introspector: introspector.track_with(&constraint),
};
document = (library.items.layout)(&mut vt, content, styles)?;
iter += 1;
- if iter >= 5 || introspector.update(&document.pages) {
+ introspector = Introspector::new(&document.pages);
+ introspector.init = true;
+
+ if iter >= 5 || introspector.valid(&constraint) {
break;
}
}
@@ -56,137 +58,86 @@ pub struct Vt<'a> {
pub introspector: Tracked<'a, Introspector>,
}
-impl<'a> Vt<'a> {
- /// Access the underlying world.
- pub fn world(&self) -> Tracked<'a, dyn World> {
- self.world
+/// Provides stable identities to nodes.
+#[derive(Clone)]
+pub struct StabilityProvider {
+ hashes: Vec<u128>,
+ checkpoints: Vec<usize>,
+}
+
+impl StabilityProvider {
+ /// Create a new stability provider.
+ pub fn new() -> Self {
+ Self { hashes: vec![], checkpoints: vec![] }
}
+}
+#[comemo::track]
+impl StabilityProvider {
/// Produce a stable identifier for this call site.
- ///
- /// The key should be something that identifies the call site, but is not
- /// necessarily unique. The stable marker incorporates the key's hash plus
- /// additional disambiguation from other call sites with the same key.
- ///
- /// The returned id can be attached to content as metadata is the then
- /// locatable through [`locate`](Self::locate).
- pub fn identify<T: Hash>(&mut self, key: &T) -> StableId {
- self.provider.identify(hash128(key))
+ pub fn identify(&mut self, hash: u128) -> StableId {
+ let count = self.hashes.iter().filter(|&&prev| prev == hash).count();
+ self.hashes.push(hash);
+ StableId(hash, count, 0)
}
- /// Whether things are locatable already.
- pub fn locatable(&self) -> bool {
- self.introspector.init()
+ /// Create a checkpoint of the state that can be restored.
+ pub fn save(&mut self) {
+ self.checkpoints.push(self.hashes.len());
}
- /// Locate all metadata matches for the given node.
- pub fn query_node<T: Node>(&self) -> impl Iterator<Item = &T> {
- self.introspector
- .query(Selector::node::<T>())
- .into_iter()
- .map(|content| content.to::<T>().unwrap())
+ /// Restore the last checkpoint.
+ pub fn restore(&mut self) {
+ if let Some(checkpoint) = self.checkpoints.pop() {
+ self.hashes.truncate(checkpoint);
+ }
}
}
/// Stably identifies a call site across multiple layout passes.
///
-/// This struct is created by [`Vt::identify`].
+/// This struct is created by [`StabilityProvider::identify`].
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct StableId(u128, u64, u64);
+pub struct StableId(u128, usize, usize);
impl StableId {
/// Produce a variant of this id.
- pub fn variant(self, n: u64) -> Self {
+ pub fn variant(self, n: usize) -> Self {
Self(self.0, self.1, n)
}
}
-/// Provides stable identities to nodes.
-#[derive(Clone)]
-pub struct StabilityProvider(HashMap<u128, u64>);
-
-impl StabilityProvider {
- /// Create a new stability provider.
- fn new() -> Self {
- Self(HashMap::new())
- }
-}
-
-#[comemo::track]
-impl StabilityProvider {
- /// Produce a stable identifier for this call site.
- fn identify(&mut self, hash: u128) -> StableId {
- let slot = self.0.entry(hash).or_default();
- let id = StableId(hash, *slot, 0);
- *slot += 1;
- id
- }
-}
-
/// Provides access to information about the document.
pub struct Introspector {
init: bool,
nodes: Vec<(Content, Location)>,
- queries: RefCell<Vec<(Selector, u128)>>,
}
impl Introspector {
/// Create a new introspector.
pub fn new(frames: &[Frame]) -> Self {
- let mut introspector = Self {
- init: false,
- nodes: vec![],
- queries: RefCell::new(vec![]),
- };
- introspector.extract_from_frames(frames);
- introspector
- }
-
- /// Update the information given new frames and return whether we can stop
- /// layouting.
- pub fn update(&mut self, frames: &[Frame]) -> bool {
- self.nodes.clear();
- self.extract_from_frames(frames);
-
- let was_init = std::mem::replace(&mut self.init, true);
- let queries = std::mem::take(&mut self.queries).into_inner();
-
- for (selector, hash) in &queries {
- let nodes = self.query_impl(selector);
- if hash128(&nodes) != *hash {
- return false;
- }
- }
-
- if !was_init && !queries.is_empty() {
- return false;
+ let mut introspector = Self { init: false, nodes: vec![] };
+ for (i, frame) in frames.iter().enumerate() {
+ let page = NonZeroUsize::new(1 + i).unwrap();
+ introspector.extract(frame, page, Transform::identity());
}
-
- true
+ introspector
}
/// Iterate over all nodes.
- pub fn nodes(&self) -> impl Iterator<Item = &Content> {
+ pub fn all(&self) -> impl Iterator<Item = &Content> {
self.nodes.iter().map(|(node, _)| node)
}
- /// Extract metadata from frames.
- fn extract_from_frames(&mut self, frames: &[Frame]) {
- for (i, frame) in frames.iter().enumerate() {
- let page = NonZeroUsize::new(1 + i).unwrap();
- self.extract_from_frame(frame, page, Transform::identity());
- }
- }
-
/// Extract metadata from a frame.
- fn extract_from_frame(&mut self, frame: &Frame, page: NonZeroUsize, ts: Transform) {
+ fn extract(&mut self, frame: &Frame, page: NonZeroUsize, ts: Transform) {
for (pos, element) in frame.elements() {
match element {
Element::Group(group) => {
let ts = ts
.pre_concat(Transform::translate(pos.x, pos.y))
.pre_concat(group.transform);
- self.extract_from_frame(&group.frame, page, ts);
+ self.extract(&group.frame, page, ts);
}
Element::Meta(Meta::Node(content), _)
if !self
@@ -212,27 +163,20 @@ impl Introspector {
/// Query for all metadata matches for the given selector.
pub fn query(&self, selector: Selector) -> Vec<&Content> {
- let nodes = self.query_impl(&selector);
- let mut queries = self.queries.borrow_mut();
- if !queries.iter().any(|(prev, _)| prev == &selector) {
- queries.push((selector, hash128(&nodes)));
- }
- nodes
+ self.all().filter(|node| selector.matches(node)).collect()
}
/// Find the page number for the given stable id.
- pub fn page(&self, id: StableId) -> Option<NonZeroUsize> {
- Some(self.location(id)?.page)
+ pub fn page(&self, id: StableId) -> NonZeroUsize {
+ self.location(id).page
}
/// Find the location for the given stable id.
- pub fn location(&self, id: StableId) -> Option<Location> {
- Some(self.nodes.iter().find(|(node, _)| node.stable_id() == Some(id))?.1)
- }
-}
-
-impl Introspector {
- fn query_impl(&self, selector: &Selector) -> Vec<&Content> {
- self.nodes().filter(|node| selector.matches(node)).collect()
+ pub fn location(&self, id: StableId) -> Location {
+ self.nodes
+ .iter()
+ .find(|(node, _)| node.stable_id() == Some(id))
+ .map(|(_, loc)| *loc)
+ .unwrap_or(Location { page: NonZeroUsize::ONE, pos: Point::zero() })
}
}
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 8e48358d..760a6499 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -12,6 +12,7 @@ use super::{
is_id_continue, is_id_start, is_newline, split_newlines, Span, SyntaxKind, SyntaxNode,
};
use crate::geom::{AbsUnit, AngleUnit};
+use crate::util::NonZeroExt;
/// A typed AST node.
pub trait AstNode: Sized {
@@ -641,7 +642,7 @@ impl Heading {
.children()
.find(|node| node.kind() == SyntaxKind::HeadingMarker)
.and_then(|node| node.len().try_into().ok())
- .unwrap_or(NonZeroUsize::new(1).unwrap())
+ .unwrap_or(NonZeroUsize::ONE)
}
}
diff --git a/src/util/mod.rs b/src/util/mod.rs
index 1eb19113..596282de 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -8,6 +8,7 @@ pub use buffer::Buffer;
use std::fmt::{self, Debug, Formatter};
use std::hash::Hash;
+use std::num::NonZeroUsize;
use std::path::{Component, Path, PathBuf};
use std::sync::Arc;
@@ -39,6 +40,19 @@ pub fn hash128<T: Hash + ?Sized>(value: &T) -> u128 {
state.finish128().as_u128()
}
+/// Extra methods for [`NonZeroUsize`].
+pub trait NonZeroExt {
+ /// The number `1`.
+ const ONE: Self;
+}
+
+impl NonZeroExt for NonZeroUsize {
+ const ONE: Self = match Self::new(1) {
+ Some(v) => v,
+ None => unreachable!(),
+ };
+}
+
/// Extra methods for [`str`].
pub trait StrExt {
/// The number of code units this string would use if it was encoded in