mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Query
This commit is contained in:
parent
c47e4cb496
commit
6d64d3e8e9
@ -95,6 +95,7 @@ fn global(math: Module, calc: Module) -> Module {
|
|||||||
global.define("counter", meta::counter);
|
global.define("counter", meta::counter);
|
||||||
global.define("numbering", meta::numbering);
|
global.define("numbering", meta::numbering);
|
||||||
global.define("state", meta::state);
|
global.define("state", meta::state);
|
||||||
|
global.define("query", meta::query);
|
||||||
|
|
||||||
// Symbols.
|
// Symbols.
|
||||||
global.define("sym", symbols::sym());
|
global.define("sym", symbols::sym());
|
||||||
|
@ -8,6 +8,7 @@ mod heading;
|
|||||||
mod link;
|
mod link;
|
||||||
mod numbering;
|
mod numbering;
|
||||||
mod outline;
|
mod outline;
|
||||||
|
mod query;
|
||||||
mod reference;
|
mod reference;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ pub use self::heading::*;
|
|||||||
pub use self::link::*;
|
pub use self::link::*;
|
||||||
pub use self::numbering::*;
|
pub use self::numbering::*;
|
||||||
pub use self::outline::*;
|
pub use self::outline::*;
|
||||||
|
pub use self::query::*;
|
||||||
pub use self::reference::*;
|
pub use self::reference::*;
|
||||||
pub use self::state::*;
|
pub use self::state::*;
|
||||||
|
|
||||||
|
69
library/src/meta/query.rs
Normal file
69
library/src/meta/query.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
/// Find elements in the document.
|
||||||
|
///
|
||||||
|
/// Display: Query
|
||||||
|
/// Category: meta
|
||||||
|
/// Returns: content
|
||||||
|
#[func]
|
||||||
|
pub fn query(
|
||||||
|
/// The thing to search for.
|
||||||
|
target: Target,
|
||||||
|
/// A function to format the results with.
|
||||||
|
format: Func,
|
||||||
|
) -> Value {
|
||||||
|
QueryNode::new(target.0, format).pack().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A query target.
|
||||||
|
struct Target(Selector);
|
||||||
|
|
||||||
|
cast_from_value! {
|
||||||
|
Target,
|
||||||
|
label: Label => Self(Selector::Label(label)),
|
||||||
|
func: Func => {
|
||||||
|
let Some(id) = func.id() else {
|
||||||
|
return Err("this function is not selectable".into());
|
||||||
|
};
|
||||||
|
|
||||||
|
if !Content::new(id).can::<dyn Locatable>() {
|
||||||
|
Err(eco_format!("cannot query for {}s", id.name))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self(Selector::Node(id, None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes a query.
|
||||||
|
///
|
||||||
|
/// Display: Query
|
||||||
|
/// Category: special
|
||||||
|
#[node(Locatable, Show)]
|
||||||
|
pub struct QueryNode {
|
||||||
|
/// The thing to search for.
|
||||||
|
#[required]
|
||||||
|
pub target: Selector,
|
||||||
|
|
||||||
|
/// The function to format the results with.
|
||||||
|
#[required]
|
||||||
|
pub format: Func,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Show for QueryNode {
|
||||||
|
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
|
||||||
|
if !vt.introspector.init() {
|
||||||
|
return Ok(Content::empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = self.0.stable_id().unwrap();
|
||||||
|
let target = self.target();
|
||||||
|
let (before, after) = vt.introspector.query_split(target, id);
|
||||||
|
let func = self.format();
|
||||||
|
let args = Args::new(func.span(), [encode(before), encode(after)]);
|
||||||
|
Ok(func.call_detached(vt.world, args)?.display())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(list: Vec<&Content>) -> Value {
|
||||||
|
Value::Array(list.into_iter().cloned().map(Value::Content).collect())
|
||||||
|
}
|
@ -166,6 +166,22 @@ impl Introspector {
|
|||||||
self.all().filter(|node| selector.matches(node)).collect()
|
self.all().filter(|node| selector.matches(node)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Query for all metadata matches before the given id.
|
||||||
|
pub fn query_split(
|
||||||
|
&self,
|
||||||
|
selector: Selector,
|
||||||
|
id: StableId,
|
||||||
|
) -> (Vec<&Content>, Vec<&Content>) {
|
||||||
|
let mut iter = self.all();
|
||||||
|
let before = iter
|
||||||
|
.by_ref()
|
||||||
|
.take_while(|node| node.stable_id() != Some(id))
|
||||||
|
.filter(|node| selector.matches(node))
|
||||||
|
.collect();
|
||||||
|
let after = iter.filter(|node| selector.matches(node)).collect();
|
||||||
|
(before, after)
|
||||||
|
}
|
||||||
|
|
||||||
/// Find the page number for the given stable id.
|
/// Find the page number for the given stable id.
|
||||||
pub fn page(&self, id: StableId) -> NonZeroUsize {
|
pub fn page(&self, id: StableId) -> NonZeroUsize {
|
||||||
self.location(id).page
|
self.location(id).page
|
||||||
|
BIN
tests/ref/meta/query.png
Normal file
BIN
tests/ref/meta/query.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 118 KiB |
30
tests/typ/meta/query.typ
Normal file
30
tests/typ/meta/query.typ
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Test the query function.
|
||||||
|
|
||||||
|
---
|
||||||
|
#set page(
|
||||||
|
paper: "a7",
|
||||||
|
margin: (y: 1cm, x: 0.5cm),
|
||||||
|
header: {
|
||||||
|
smallcaps[Typst Academy]
|
||||||
|
h(1fr)
|
||||||
|
query(heading, (before, after) => {
|
||||||
|
let elem = if before.len() != 0 {
|
||||||
|
before.last()
|
||||||
|
} else if after.len() != 0 {
|
||||||
|
after.first()
|
||||||
|
}
|
||||||
|
emph(elem.body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#outline()
|
||||||
|
|
||||||
|
= Introduction
|
||||||
|
#lorem(35)
|
||||||
|
|
||||||
|
= Background
|
||||||
|
#lorem(35)
|
||||||
|
|
||||||
|
= Approach
|
||||||
|
#lorem(60)
|
Loading…
x
Reference in New Issue
Block a user