mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Separate state and constraints 🧶
This commit is contained in:
parent
6672f8f7df
commit
0f7c70fd93
@ -5,6 +5,7 @@ use criterion::{criterion_group, criterion_main, Criterion};
|
|||||||
use fontdock::fs::{FsIndex, FsProvider};
|
use fontdock::fs::{FsIndex, FsProvider};
|
||||||
use futures_executor::block_on;
|
use futures_executor::block_on;
|
||||||
|
|
||||||
|
use typstc::eval::State;
|
||||||
use typstc::font::FontLoader;
|
use typstc::font::FontLoader;
|
||||||
use typstc::parse::parse;
|
use typstc::parse::parse;
|
||||||
use typstc::typeset;
|
use typstc::typeset;
|
||||||
@ -25,10 +26,9 @@ fn typesetting_benchmark(c: &mut Criterion) {
|
|||||||
let loader = FontLoader::new(Box::new(provider), descriptors);
|
let loader = FontLoader::new(Box::new(provider), descriptors);
|
||||||
let loader = Rc::new(RefCell::new(loader));
|
let loader = Rc::new(RefCell::new(loader));
|
||||||
|
|
||||||
let style = Default::default();
|
let state = State::default();
|
||||||
let scope = typstc::library::_std();
|
|
||||||
c.bench_function("typeset-coma", |b| {
|
c.bench_function("typeset-coma", |b| {
|
||||||
b.iter(|| block_on(typeset(COMA, &style, &scope, Rc::clone(&loader))))
|
b.iter(|| block_on(typeset(COMA, state.clone(), Rc::clone(&loader))))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ use std::rc::Rc;
|
|||||||
use fontdock::fs::{FsIndex, FsProvider};
|
use fontdock::fs::{FsIndex, FsProvider};
|
||||||
use futures_executor::block_on;
|
use futures_executor::block_on;
|
||||||
|
|
||||||
|
use typstc::eval::State;
|
||||||
use typstc::export::pdf;
|
use typstc::export::pdf;
|
||||||
use typstc::font::FontLoader;
|
use typstc::font::FontLoader;
|
||||||
use typstc::parse::LineMap;
|
use typstc::parse::LineMap;
|
||||||
@ -41,12 +42,11 @@ fn main() {
|
|||||||
let loader = FontLoader::new(Box::new(provider), descriptors);
|
let loader = FontLoader::new(Box::new(provider), descriptors);
|
||||||
let loader = Rc::new(RefCell::new(loader));
|
let loader = Rc::new(RefCell::new(loader));
|
||||||
|
|
||||||
let style = Default::default();
|
let state = State::default();
|
||||||
let scope = typstc::library::_std();
|
|
||||||
let Pass {
|
let Pass {
|
||||||
output: layouts,
|
output: layouts,
|
||||||
feedback: Feedback { mut diagnostics, .. },
|
feedback: Feedback { mut diagnostics, .. },
|
||||||
} = block_on(typeset(&src, &style, &scope, Rc::clone(&loader)));
|
} = block_on(typeset(&src, state, Rc::clone(&loader)));
|
||||||
|
|
||||||
if !diagnostics.is_empty() {
|
if !diagnostics.is_empty() {
|
||||||
diagnostics.sort();
|
diagnostics.sort();
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
mod dict;
|
mod dict;
|
||||||
mod scope;
|
mod scope;
|
||||||
|
mod state;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub use dict::*;
|
pub use dict::*;
|
||||||
pub use scope::*;
|
pub use scope::*;
|
||||||
|
pub use state::*;
|
||||||
pub use value::*;
|
pub use value::*;
|
||||||
|
@ -6,6 +6,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use super::value::FuncValue;
|
use super::value::FuncValue;
|
||||||
|
|
||||||
/// A map from identifiers to functions.
|
/// A map from identifiers to functions.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
functions: HashMap<String, FuncValue>,
|
functions: HashMap<String, FuncValue>,
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,43 @@
|
|||||||
//! Styles for text and pages.
|
//! State.
|
||||||
|
|
||||||
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
|
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
|
||||||
|
|
||||||
|
use super::Scope;
|
||||||
use crate::geom::{Insets, Linear, Sides, Size};
|
use crate::geom::{Insets, Linear, Sides, Size};
|
||||||
|
use crate::layout::{Dir, GenAlign, LayoutAlign, LayoutSystem};
|
||||||
use crate::length::Length;
|
use crate::length::Length;
|
||||||
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
||||||
|
|
||||||
/// Defines properties of pages and text.
|
/// The active evaluation state.
|
||||||
#[derive(Debug, Default, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct LayoutStyle {
|
pub struct State {
|
||||||
/// The style for text.
|
/// The scope that contains function definitions.
|
||||||
pub text: TextStyle,
|
pub scope: Scope,
|
||||||
/// The style for pages.
|
/// The text state.
|
||||||
pub page: PageStyle,
|
pub text: TextState,
|
||||||
|
/// The page state.
|
||||||
|
pub page: PageState,
|
||||||
|
/// The active layouting system.
|
||||||
|
pub sys: LayoutSystem,
|
||||||
|
/// The active alignments.
|
||||||
|
pub align: LayoutAlign,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for State {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
scope: crate::library::_std(),
|
||||||
|
text: TextState::default(),
|
||||||
|
page: PageState::default(),
|
||||||
|
sys: LayoutSystem::new(Dir::LTR, Dir::TTB),
|
||||||
|
align: LayoutAlign::new(GenAlign::Start, GenAlign::Start),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines which fonts to use and how to space text.
|
/// Defines which fonts to use and how to space text.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct TextStyle {
|
pub struct TextState {
|
||||||
/// A tree of font family names and generic class names.
|
/// A tree of font family names and generic class names.
|
||||||
pub fallback: FallbackTree,
|
pub fallback: FallbackTree,
|
||||||
/// The selected font variant.
|
/// The selected font variant.
|
||||||
@ -38,7 +58,7 @@ pub struct TextStyle {
|
|||||||
pub par_spacing: Linear,
|
pub par_spacing: Linear,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextStyle {
|
impl TextState {
|
||||||
/// The absolute font size.
|
/// The absolute font size.
|
||||||
pub fn font_size(&self) -> f64 {
|
pub fn font_size(&self) -> f64 {
|
||||||
self.font_size.eval()
|
self.font_size.eval()
|
||||||
@ -60,7 +80,7 @@ impl TextStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextStyle {
|
impl Default for TextState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fallback: fallback! {
|
fallback: fallback! {
|
||||||
@ -120,7 +140,7 @@ impl FontSize {
|
|||||||
|
|
||||||
/// Defines the size and margins of a page.
|
/// Defines the size and margins of a page.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct PageStyle {
|
pub struct PageState {
|
||||||
/// The class of this page.
|
/// The class of this page.
|
||||||
pub class: PaperClass,
|
pub class: PaperClass,
|
||||||
/// The width and height of the page.
|
/// The width and height of the page.
|
||||||
@ -130,7 +150,7 @@ pub struct PageStyle {
|
|||||||
pub margins: Sides<Option<Linear>>,
|
pub margins: Sides<Option<Linear>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PageStyle {
|
impl PageState {
|
||||||
/// The default page style for the given paper.
|
/// The default page style for the given paper.
|
||||||
pub fn new(paper: Paper) -> Self {
|
pub fn new(paper: Paper) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -153,7 +173,7 @@ impl PageStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PageStyle {
|
impl Default for PageState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(PAPER_A4)
|
Self::new(PAPER_A4)
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ use crate::geom::Linear;
|
|||||||
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
|
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
|
||||||
use crate::paper::Paper;
|
use crate::paper::Paper;
|
||||||
use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree};
|
use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree};
|
||||||
use crate::{DynFuture, Feedback, Pass};
|
use crate::{DynFuture, Feedback};
|
||||||
|
|
||||||
/// A computational value.
|
/// A computational value.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
@ -149,13 +149,13 @@ impl Debug for Value {
|
|||||||
pub struct FuncValue(pub Rc<FuncType>);
|
pub struct FuncValue(pub Rc<FuncType>);
|
||||||
|
|
||||||
/// The signature of executable functions.
|
/// The signature of executable functions.
|
||||||
type FuncType = dyn Fn(Span, DictValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>;
|
type FuncType = dyn Fn(DictValue, &mut LayoutContext) -> DynFuture<Value>;
|
||||||
|
|
||||||
impl FuncValue {
|
impl FuncValue {
|
||||||
/// Create a new function value from a rust function or closure.
|
/// Create a new function value from a rust function or closure.
|
||||||
pub fn new<F: 'static>(f: F) -> Self
|
pub fn new<F: 'static>(f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(Span, DictValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>,
|
F: Fn(DictValue, &mut LayoutContext) -> DynFuture<Value>,
|
||||||
{
|
{
|
||||||
Self(Rc::new(f))
|
Self(Rc::new(f))
|
||||||
}
|
}
|
||||||
|
@ -15,36 +15,40 @@ pub use tree::*;
|
|||||||
|
|
||||||
use crate::geom::{Insets, Point, Rect, RectExt, Sides, Size, SizeExt};
|
use crate::geom::{Insets, Point, Rect, RectExt, Sides, Size, SizeExt};
|
||||||
|
|
||||||
use crate::eval::Scope;
|
use crate::eval::{PageState, State, TextState};
|
||||||
use crate::font::SharedFontLoader;
|
use crate::font::SharedFontLoader;
|
||||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
|
||||||
use crate::syntax::SynTree;
|
use crate::syntax::SynTree;
|
||||||
use crate::Pass;
|
use crate::{Feedback, Pass};
|
||||||
|
|
||||||
/// Layout a syntax tree and return the produced layout.
|
/// Layout a syntax tree and return the produced layout.
|
||||||
pub async fn layout(
|
pub async fn layout(
|
||||||
tree: &SynTree,
|
tree: &SynTree,
|
||||||
style: &LayoutStyle,
|
state: State,
|
||||||
scope: &Scope,
|
|
||||||
loader: SharedFontLoader,
|
loader: SharedFontLoader,
|
||||||
) -> Pass<MultiLayout> {
|
) -> Pass<MultiLayout> {
|
||||||
let space = LayoutSpace {
|
let space = LayoutSpace {
|
||||||
size: style.page.size,
|
size: state.page.size,
|
||||||
insets: style.page.insets(),
|
insets: state.page.insets(),
|
||||||
expansion: LayoutExpansion::new(true, true),
|
expansion: LayoutExpansion::new(true, true),
|
||||||
};
|
};
|
||||||
tree::layout_tree(&tree, LayoutContext {
|
|
||||||
loader,
|
let constraints = LayoutConstraints {
|
||||||
scope,
|
root: true,
|
||||||
style,
|
|
||||||
base: space.usable(),
|
base: space.usable(),
|
||||||
spaces: vec![space],
|
spaces: vec![space],
|
||||||
repeat: true,
|
repeat: true,
|
||||||
sys: LayoutSystem::new(Dir::LTR, Dir::TTB),
|
};
|
||||||
align: LayoutAlign::new(GenAlign::Start, GenAlign::Start),
|
|
||||||
root: true,
|
let mut ctx = LayoutContext {
|
||||||
})
|
loader,
|
||||||
.await
|
state,
|
||||||
|
constraints,
|
||||||
|
f: Feedback::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let layouts = layout_tree(&tree, &mut ctx).await;
|
||||||
|
|
||||||
|
Pass::new(layouts, ctx.f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A collection of layouts.
|
/// A collection of layouts.
|
||||||
@ -63,13 +67,22 @@ pub struct BoxLayout {
|
|||||||
|
|
||||||
/// The context for layouting.
|
/// The context for layouting.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LayoutContext<'a> {
|
pub struct LayoutContext {
|
||||||
/// The font loader to query fonts from when typesetting text.
|
/// The font loader to query fonts from when typesetting text.
|
||||||
pub loader: SharedFontLoader,
|
pub loader: SharedFontLoader,
|
||||||
/// The function scope.
|
/// The active state.
|
||||||
pub scope: &'a Scope,
|
pub state: State,
|
||||||
/// The style for pages and text.
|
/// The active constraints.
|
||||||
pub style: &'a LayoutStyle,
|
pub constraints: LayoutConstraints,
|
||||||
|
/// The accumulated feedback.
|
||||||
|
pub f: Feedback,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The constraints for layouting a single node.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LayoutConstraints {
|
||||||
|
/// Whether this layouting process is the root page-building process.
|
||||||
|
pub root: bool,
|
||||||
/// The unpadded size of this container (the base 100% for relative sizes).
|
/// The unpadded size of this container (the base 100% for relative sizes).
|
||||||
pub base: Size,
|
pub base: Size,
|
||||||
/// The spaces to layout into.
|
/// The spaces to layout into.
|
||||||
@ -77,14 +90,6 @@ pub struct LayoutContext<'a> {
|
|||||||
/// Whether to spill over into copies of the last space or finish layouting
|
/// Whether to spill over into copies of the last space or finish layouting
|
||||||
/// when the last space is used up.
|
/// when the last space is used up.
|
||||||
pub repeat: bool,
|
pub repeat: bool,
|
||||||
/// The system into which content is laid out.
|
|
||||||
pub sys: LayoutSystem,
|
|
||||||
/// The alignment of the _resulting_ layout. This does not effect the line
|
|
||||||
/// layouting itself, but rather how the finished layout will be positioned
|
|
||||||
/// in a parent layout.
|
|
||||||
pub align: LayoutAlign,
|
|
||||||
/// Whether this layouting process is the root page-building process.
|
|
||||||
pub root: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A collection of layout spaces.
|
/// A collection of layout spaces.
|
||||||
@ -156,9 +161,9 @@ pub enum Command {
|
|||||||
BreakPage,
|
BreakPage,
|
||||||
|
|
||||||
/// Update the text style.
|
/// Update the text style.
|
||||||
SetTextStyle(TextStyle),
|
SetTextState(TextState),
|
||||||
/// Update the page style.
|
/// Update the page style.
|
||||||
SetPageStyle(PageStyle),
|
SetPageState(PageState),
|
||||||
|
|
||||||
/// Update the alignment for future boxes added to this layouting process.
|
/// Update the alignment for future boxes added to this layouting process.
|
||||||
SetAlignment(LayoutAlign),
|
SetAlignment(LayoutAlign),
|
||||||
|
@ -1,48 +1,45 @@
|
|||||||
//! Layouting of syntax trees.
|
//! Layouting of syntax trees.
|
||||||
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::shaping;
|
use crate::shaping;
|
||||||
use crate::style::LayoutStyle;
|
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
Decoration, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree,
|
Decoration, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree,
|
||||||
};
|
};
|
||||||
use crate::{DynFuture, Feedback, Pass};
|
use crate::DynFuture;
|
||||||
|
|
||||||
/// Layout a syntax tree in a given context.
|
/// Layout a syntax tree in a given context.
|
||||||
pub async fn layout_tree(tree: &SynTree, ctx: LayoutContext<'_>) -> Pass<MultiLayout> {
|
pub async fn layout_tree(tree: &SynTree, ctx: &mut LayoutContext) -> MultiLayout {
|
||||||
let mut layouter = TreeLayouter::new(ctx);
|
let mut layouter = TreeLayouter::new(ctx);
|
||||||
layouter.layout_tree(tree).await;
|
layouter.layout_tree(tree).await;
|
||||||
layouter.finish()
|
layouter.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the tree layouting.
|
/// Layouts trees.
|
||||||
struct TreeLayouter<'a> {
|
struct TreeLayouter<'a> {
|
||||||
ctx: LayoutContext<'a>,
|
ctx: &'a mut LayoutContext,
|
||||||
|
constraints: LayoutConstraints,
|
||||||
layouter: LineLayouter,
|
layouter: LineLayouter,
|
||||||
style: LayoutStyle,
|
|
||||||
feedback: Feedback,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TreeLayouter<'a> {
|
impl<'a> TreeLayouter<'a> {
|
||||||
fn new(ctx: LayoutContext<'a>) -> Self {
|
fn new(ctx: &'a mut LayoutContext) -> Self {
|
||||||
|
let layouter = LineLayouter::new(LineContext {
|
||||||
|
spaces: ctx.constraints.spaces.clone(),
|
||||||
|
sys: ctx.state.sys,
|
||||||
|
align: ctx.state.align,
|
||||||
|
repeat: ctx.constraints.repeat,
|
||||||
|
line_spacing: ctx.state.text.line_spacing(),
|
||||||
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
layouter: LineLayouter::new(LineContext {
|
layouter,
|
||||||
spaces: ctx.spaces.clone(),
|
constraints: ctx.constraints.clone(),
|
||||||
sys: ctx.sys,
|
|
||||||
align: ctx.align,
|
|
||||||
repeat: ctx.repeat,
|
|
||||||
line_spacing: ctx.style.text.line_spacing(),
|
|
||||||
}),
|
|
||||||
style: ctx.style.clone(),
|
|
||||||
ctx,
|
ctx,
|
||||||
feedback: Feedback::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self) -> Pass<MultiLayout> {
|
fn finish(self) -> MultiLayout {
|
||||||
Pass::new(self.layouter.finish(), self.feedback)
|
self.layouter.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_tree<'t>(&'t mut self, tree: &'t SynTree) -> DynFuture<'t, ()> {
|
fn layout_tree<'t>(&'t mut self, tree: &'t SynTree) -> DynFuture<'t, ()> {
|
||||||
@ -55,16 +52,16 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
|
|
||||||
async fn layout_node(&mut self, node: &Spanned<SynNode>) {
|
async fn layout_node(&mut self, node: &Spanned<SynNode>) {
|
||||||
let decorate = |this: &mut Self, deco: Decoration| {
|
let decorate = |this: &mut Self, deco: Decoration| {
|
||||||
this.feedback.decorations.push(deco.span_with(node.span));
|
this.ctx.f.decorations.push(deco.span_with(node.span));
|
||||||
};
|
};
|
||||||
|
|
||||||
match &node.v {
|
match &node.v {
|
||||||
SynNode::Space => self.layout_space(),
|
SynNode::Space => self.layout_space(),
|
||||||
SynNode::Text(text) => {
|
SynNode::Text(text) => {
|
||||||
if self.style.text.emph {
|
if self.ctx.state.text.emph {
|
||||||
decorate(self, Decoration::Emph);
|
decorate(self, Decoration::Emph);
|
||||||
}
|
}
|
||||||
if self.style.text.strong {
|
if self.ctx.state.text.strong {
|
||||||
decorate(self, Decoration::Strong);
|
decorate(self, Decoration::Strong);
|
||||||
}
|
}
|
||||||
self.layout_text(text).await;
|
self.layout_text(text).await;
|
||||||
@ -73,11 +70,11 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
SynNode::Linebreak => self.layouter.finish_line(),
|
SynNode::Linebreak => self.layouter.finish_line(),
|
||||||
SynNode::Parbreak => self.layout_parbreak(),
|
SynNode::Parbreak => self.layout_parbreak(),
|
||||||
SynNode::Emph => {
|
SynNode::Emph => {
|
||||||
self.style.text.emph = !self.style.text.emph;
|
self.ctx.state.text.emph ^= true;
|
||||||
decorate(self, Decoration::Emph);
|
decorate(self, Decoration::Emph);
|
||||||
}
|
}
|
||||||
SynNode::Strong => {
|
SynNode::Strong => {
|
||||||
self.style.text.strong = !self.style.text.strong;
|
self.ctx.state.text.strong ^= true;
|
||||||
decorate(self, Decoration::Strong);
|
decorate(self, Decoration::Strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,12 +89,12 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
|
|
||||||
fn layout_space(&mut self) {
|
fn layout_space(&mut self) {
|
||||||
self.layouter
|
self.layouter
|
||||||
.add_primary_spacing(self.style.text.word_spacing(), SpacingKind::WORD);
|
.add_primary_spacing(self.ctx.state.text.word_spacing(), SpacingKind::WORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_parbreak(&mut self) {
|
fn layout_parbreak(&mut self) {
|
||||||
self.layouter.add_secondary_spacing(
|
self.layouter.add_secondary_spacing(
|
||||||
self.style.text.paragraph_spacing(),
|
self.ctx.state.text.paragraph_spacing(),
|
||||||
SpacingKind::PARAGRAPH,
|
SpacingKind::PARAGRAPH,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -106,9 +103,9 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
self.layouter.add(
|
self.layouter.add(
|
||||||
shaping::shape(
|
shaping::shape(
|
||||||
text,
|
text,
|
||||||
self.ctx.sys.primary,
|
self.ctx.state.sys.primary,
|
||||||
self.ctx.align,
|
self.ctx.state.align,
|
||||||
&self.style.text,
|
&self.ctx.state.text,
|
||||||
&mut self.ctx.loader.borrow_mut(),
|
&mut self.ctx.loader.borrow_mut(),
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
@ -116,17 +113,17 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn layout_heading(&mut self, heading: &NodeHeading) {
|
async fn layout_heading(&mut self, heading: &NodeHeading) {
|
||||||
let style = self.style.text.clone();
|
let style = self.ctx.state.text.clone();
|
||||||
|
|
||||||
let factor = 1.5 - 0.1 * heading.level.v as f64;
|
let factor = 1.5 - 0.1 * heading.level.v as f64;
|
||||||
self.style.text.font_size.scale *= factor;
|
self.ctx.state.text.font_size.scale *= factor;
|
||||||
self.style.text.strong = true;
|
self.ctx.state.text.strong = true;
|
||||||
|
|
||||||
self.layout_parbreak();
|
self.layout_parbreak();
|
||||||
self.layout_tree(&heading.contents).await;
|
self.layout_tree(&heading.contents).await;
|
||||||
self.layout_parbreak();
|
self.layout_parbreak();
|
||||||
|
|
||||||
self.style.text = style;
|
self.ctx.state.text = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn layout_raw(&mut self, raw: &NodeRaw) {
|
async fn layout_raw(&mut self, raw: &NodeRaw) {
|
||||||
@ -135,9 +132,9 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make this more efficient.
|
// TODO: Make this more efficient.
|
||||||
let fallback = self.style.text.fallback.clone();
|
let fallback = self.ctx.state.text.fallback.clone();
|
||||||
self.style.text.fallback.list.insert(0, "monospace".to_string());
|
self.ctx.state.text.fallback.list.insert(0, "monospace".to_string());
|
||||||
self.style.text.fallback.flatten();
|
self.ctx.state.text.fallback.flatten();
|
||||||
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for line in &raw.lines {
|
for line in &raw.lines {
|
||||||
@ -148,7 +145,7 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
self.layout_text(line).await;
|
self.layout_text(line).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.style.text.fallback = fallback;
|
self.ctx.state.text.fallback = fallback;
|
||||||
|
|
||||||
if !raw.inline {
|
if !raw.inline {
|
||||||
self.layout_parbreak();
|
self.layout_parbreak();
|
||||||
@ -156,15 +153,15 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn layout_expr(&mut self, expr: Spanned<&Expr>) {
|
async fn layout_expr(&mut self, expr: Spanned<&Expr>) {
|
||||||
let ctx = LayoutContext {
|
self.ctx.constraints = LayoutConstraints {
|
||||||
style: &self.style,
|
|
||||||
spaces: self.layouter.remaining(),
|
|
||||||
root: false,
|
root: false,
|
||||||
loader: Rc::clone(&self.ctx.loader),
|
base: self.constraints.base,
|
||||||
..self.ctx
|
spaces: self.layouter.remaining(),
|
||||||
|
repeat: self.constraints.repeat,
|
||||||
};
|
};
|
||||||
|
|
||||||
let val = expr.v.eval(&ctx, &mut self.feedback).await;
|
let val = expr.v.eval(self.ctx).await;
|
||||||
|
|
||||||
let commands = val.span_with(expr.span).into_commands();
|
let commands = val.span_with(expr.span).into_commands();
|
||||||
|
|
||||||
for command in commands {
|
for command in commands {
|
||||||
@ -186,23 +183,23 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
|
|
||||||
BreakLine => self.layouter.finish_line(),
|
BreakLine => self.layouter.finish_line(),
|
||||||
BreakPage => {
|
BreakPage => {
|
||||||
if self.ctx.root {
|
if self.constraints.root {
|
||||||
self.layouter.finish_space(true)
|
self.layouter.finish_space(true)
|
||||||
} else {
|
} else {
|
||||||
error!(
|
error!(
|
||||||
@self.feedback, span,
|
@self.ctx.f, span,
|
||||||
"page break cannot only be issued from root context",
|
"page break cannot only be issued from root context",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetTextStyle(style) => {
|
SetTextState(style) => {
|
||||||
self.layouter.set_line_spacing(style.line_spacing());
|
self.layouter.set_line_spacing(style.line_spacing());
|
||||||
self.style.text = style;
|
self.ctx.state.text = style;
|
||||||
}
|
}
|
||||||
SetPageStyle(style) => {
|
SetPageState(style) => {
|
||||||
if self.ctx.root {
|
if self.constraints.root {
|
||||||
self.style.page = style;
|
self.ctx.state.page = style;
|
||||||
|
|
||||||
// The line layouter has no idea of page styles and thus we
|
// The line layouter has no idea of page styles and thus we
|
||||||
// need to recompute the layouting space resulting of the
|
// need to recompute the layouting space resulting of the
|
||||||
@ -212,20 +209,20 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
insets: style.insets(),
|
insets: style.insets(),
|
||||||
expansion: LayoutExpansion::new(true, true),
|
expansion: LayoutExpansion::new(true, true),
|
||||||
};
|
};
|
||||||
self.ctx.base = space.usable();
|
self.constraints.base = space.usable();
|
||||||
self.layouter.set_spaces(vec![space], true);
|
self.layouter.set_spaces(vec![space], true);
|
||||||
} else {
|
} else {
|
||||||
error!(
|
error!(
|
||||||
@self.feedback, span,
|
@self.ctx.f, span,
|
||||||
"page style cannot only be changed from root context",
|
"page style cannot only be changed from root context",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetAlignment(align) => self.ctx.align = align,
|
SetAlignment(align) => self.ctx.state.align = align,
|
||||||
SetSystem(sys) => {
|
SetSystem(sys) => {
|
||||||
self.layouter.set_sys(sys);
|
self.layouter.set_sys(sys);
|
||||||
self.ctx.sys = sys;
|
self.ctx.state.sys = sys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ pub mod paper;
|
|||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod shaping;
|
pub mod shaping;
|
||||||
pub mod style;
|
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@ -44,21 +43,19 @@ use std::future::Future;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::diagnostic::Diagnostic;
|
use crate::diagnostic::Diagnostic;
|
||||||
use crate::eval::{Scope, Value};
|
use crate::eval::{State, Value};
|
||||||
use crate::font::SharedFontLoader;
|
use crate::font::SharedFontLoader;
|
||||||
use crate::layout::{Commands, MultiLayout};
|
use crate::layout::{Commands, MultiLayout};
|
||||||
use crate::style::LayoutStyle;
|
|
||||||
use crate::syntax::{Decoration, Offset, Pos, SpanVec};
|
use crate::syntax::{Decoration, Offset, Pos, SpanVec};
|
||||||
|
|
||||||
/// Process source code directly into a collection of layouts.
|
/// Process source code directly into a collection of layouts.
|
||||||
pub async fn typeset(
|
pub async fn typeset(
|
||||||
src: &str,
|
src: &str,
|
||||||
style: &LayoutStyle,
|
state: State,
|
||||||
scope: &Scope,
|
|
||||||
loader: SharedFontLoader,
|
loader: SharedFontLoader,
|
||||||
) -> Pass<MultiLayout> {
|
) -> Pass<MultiLayout> {
|
||||||
let parsed = parse::parse(src);
|
let parsed = parse::parse(src);
|
||||||
let layouted = layout::layout(&parsed.output, style, scope, loader).await;
|
let layouted = layout::layout(&parsed.output, state, loader).await;
|
||||||
let feedback = Feedback::merge(parsed.feedback, layouted.feedback);
|
let feedback = Feedback::merge(parsed.feedback, layouted.feedback);
|
||||||
Pass::new(layouted.output, feedback)
|
Pass::new(layouted.output, feedback)
|
||||||
}
|
}
|
||||||
|
@ -14,19 +14,17 @@ use super::*;
|
|||||||
/// - `vertical`: Any of `top`, `bottom` or `center`.
|
/// - `vertical`: Any of `top`, `bottom` or `center`.
|
||||||
///
|
///
|
||||||
/// There may not be two alignment specifications for the same axis.
|
/// There may not be two alignment specifications for the same axis.
|
||||||
pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn align(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
|
||||||
let mut f = Feedback::new();
|
|
||||||
|
|
||||||
let content = args.take::<SynTree>();
|
let content = args.take::<SynTree>();
|
||||||
let h = args.take_key::<Spanned<SpecAlign>>("horizontal", &mut f);
|
let h = args.take_key::<Spanned<SpecAlign>>("horizontal", &mut ctx.f);
|
||||||
let v = args.take_key::<Spanned<SpecAlign>>("vertical", &mut f);
|
let v = args.take_key::<Spanned<SpecAlign>>("vertical", &mut ctx.f);
|
||||||
let all = args
|
let all = args
|
||||||
.take_all_num_vals::<Spanned<SpecAlign>>()
|
.take_all_num_vals::<Spanned<SpecAlign>>()
|
||||||
.map(|align| (align.v.axis(), align))
|
.map(|align| (align.v.axis(), align))
|
||||||
.chain(h.into_iter().map(|align| (Some(SpecAxis::Horizontal), align)))
|
.chain(h.into_iter().map(|align| (Some(SpecAxis::Horizontal), align)))
|
||||||
.chain(v.into_iter().map(|align| (Some(SpecAxis::Vertical), align)));
|
.chain(v.into_iter().map(|align| (Some(SpecAxis::Vertical), align)));
|
||||||
|
|
||||||
let mut aligns = ctx.align;
|
let mut aligns = ctx.state.align;
|
||||||
let mut had = [false; 2];
|
let mut had = [false; 2];
|
||||||
let mut deferred_center = false;
|
let mut deferred_center = false;
|
||||||
|
|
||||||
@ -37,19 +35,19 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
|
|||||||
if let Some(axis) = axis {
|
if let Some(axis) = axis {
|
||||||
if align.v.axis().map_or(false, |a| a != axis) {
|
if align.v.axis().map_or(false, |a| a != axis) {
|
||||||
error!(
|
error!(
|
||||||
@f, align.span,
|
@ctx.f, align.span,
|
||||||
"invalid alignment {} for {} axis", align.v, axis,
|
"invalid alignment {} for {} axis", align.v, axis,
|
||||||
);
|
);
|
||||||
} else if had[axis as usize] {
|
} else if had[axis as usize] {
|
||||||
error!(@f, align.span, "duplicate alignment for {} axis", axis);
|
error!(@ctx.f, align.span, "duplicate alignment for {} axis", axis);
|
||||||
} else {
|
} else {
|
||||||
let gen_align = align.v.to_gen(ctx.sys);
|
let gen_align = align.v.to_gen(ctx.state.sys);
|
||||||
*aligns.get_mut(axis.to_gen(ctx.sys)) = gen_align;
|
*aligns.get_mut(axis.to_gen(ctx.state.sys)) = gen_align;
|
||||||
had[axis as usize] = true;
|
had[axis as usize] = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if had == [true, true] {
|
if had == [true, true] {
|
||||||
error!(@f, align.span, "duplicate alignment");
|
error!(@ctx.f, align.span, "duplicate alignment");
|
||||||
} else if deferred_center {
|
} else if deferred_center {
|
||||||
// We have two unflushed centers, meaning we know that both axes
|
// We have two unflushed centers, meaning we know that both axes
|
||||||
// are to be centered.
|
// are to be centered.
|
||||||
@ -69,7 +67,7 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
|
|||||||
SpecAxis::Vertical
|
SpecAxis::Vertical
|
||||||
};
|
};
|
||||||
|
|
||||||
*aligns.get_mut(axis.to_gen(ctx.sys)) = GenAlign::Center;
|
*aligns.get_mut(axis.to_gen(ctx.state.sys)) = GenAlign::Center;
|
||||||
|
|
||||||
had[axis as usize] = true;
|
had[axis as usize] = true;
|
||||||
deferred_center = false;
|
deferred_center = false;
|
||||||
@ -82,15 +80,13 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
|
|||||||
aligns.primary = GenAlign::Center;
|
aligns.primary = GenAlign::Center;
|
||||||
}
|
}
|
||||||
|
|
||||||
let commands = match content {
|
args.unexpected(&mut ctx.f);
|
||||||
|
Value::Commands(match content {
|
||||||
Some(tree) => vec![
|
Some(tree) => vec![
|
||||||
SetAlignment(aligns),
|
SetAlignment(aligns),
|
||||||
LayoutSyntaxTree(tree),
|
LayoutSyntaxTree(tree),
|
||||||
SetAlignment(ctx.align),
|
SetAlignment(ctx.state.align),
|
||||||
],
|
],
|
||||||
None => vec![SetAlignment(aligns)],
|
None => vec![SetAlignment(aligns)],
|
||||||
};
|
})
|
||||||
|
|
||||||
args.unexpected(&mut f);
|
|
||||||
Pass::commands(commands, f)
|
|
||||||
}
|
}
|
||||||
|
@ -6,37 +6,32 @@ use crate::geom::Linear;
|
|||||||
/// # Keyword arguments
|
/// # Keyword arguments
|
||||||
/// - `width`: The width of the box (length or relative to parent's width).
|
/// - `width`: The width of the box (length or relative to parent's width).
|
||||||
/// - `height`: The height of the box (length or relative to parent's height).
|
/// - `height`: The height of the box (length or relative to parent's height).
|
||||||
pub async fn boxed(
|
pub async fn boxed(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
|
||||||
_: Span,
|
|
||||||
mut args: DictValue,
|
|
||||||
mut ctx: LayoutContext<'_>,
|
|
||||||
) -> Pass<Value> {
|
|
||||||
let mut f = Feedback::new();
|
|
||||||
|
|
||||||
let content = args.take::<SynTree>().unwrap_or_default();
|
let content = args.take::<SynTree>().unwrap_or_default();
|
||||||
|
|
||||||
ctx.base = ctx.spaces[0].size;
|
let constraints = &mut ctx.constraints;
|
||||||
ctx.spaces.truncate(1);
|
constraints.base = constraints.spaces[0].size;
|
||||||
ctx.repeat = false;
|
constraints.spaces.truncate(1);
|
||||||
|
constraints.repeat = false;
|
||||||
|
|
||||||
if let Some(width) = args.take_key::<Linear>("width", &mut f) {
|
if let Some(width) = args.take_key::<Linear>("width", &mut ctx.f) {
|
||||||
let abs = width.eval(ctx.base.width);
|
let abs = width.eval(constraints.base.width);
|
||||||
ctx.base.width = abs;
|
constraints.base.width = abs;
|
||||||
ctx.spaces[0].size.width = abs;
|
constraints.spaces[0].size.width = abs;
|
||||||
ctx.spaces[0].expansion.horizontal = true;
|
constraints.spaces[0].expansion.horizontal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(height) = args.take_key::<Linear>("height", &mut f) {
|
if let Some(height) = args.take_key::<Linear>("height", &mut ctx.f) {
|
||||||
let abs = height.eval(ctx.base.height);
|
let abs = height.eval(constraints.base.height);
|
||||||
ctx.base.height = abs;
|
constraints.base.height = abs;
|
||||||
ctx.spaces[0].size.height = abs;
|
constraints.spaces[0].size.height = abs;
|
||||||
ctx.spaces[0].expansion.vertical = true;
|
constraints.spaces[0].expansion.vertical = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args.unexpected(&mut ctx.f);
|
||||||
|
|
||||||
let layouted = layout_tree(&content, ctx).await;
|
let layouted = layout_tree(&content, ctx).await;
|
||||||
let layout = layouted.output.into_iter().next().unwrap();
|
let layout = layouted.into_iter().next().unwrap();
|
||||||
f.extend(layouted.feedback);
|
|
||||||
|
|
||||||
args.unexpected(&mut f);
|
Value::Commands(vec![Add(layout)])
|
||||||
Pass::commands(vec![Add(layout)], f)
|
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,12 @@ use super::*;
|
|||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
|
|
||||||
/// `rgb`: Create an RGB(A) color.
|
/// `rgb`: Create an RGB(A) color.
|
||||||
pub async fn rgb(span: Span, mut args: DictValue, _: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn rgb(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
|
|
||||||
let r = args.expect::<Spanned<i64>>("red value", span, &mut f);
|
let r = args.expect::<Spanned<i64>>("red value", Span::ZERO, &mut f);
|
||||||
let g = args.expect::<Spanned<i64>>("green value", span, &mut f);
|
let g = args.expect::<Spanned<i64>>("green value", Span::ZERO, &mut f);
|
||||||
let b = args.expect::<Spanned<i64>>("blue value", span, &mut f);
|
let b = args.expect::<Spanned<i64>>("blue value", Span::ZERO, &mut f);
|
||||||
let a = args.take::<Spanned<i64>>();
|
let a = args.take::<Spanned<i64>>();
|
||||||
|
|
||||||
let mut clamp = |component: Option<Spanned<i64>>, default| {
|
let mut clamp = |component: Option<Spanned<i64>>, default| {
|
||||||
@ -19,8 +19,11 @@ pub async fn rgb(span: Span, mut args: DictValue, _: LayoutContext<'_>) -> Pass<
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let color = RgbaColor::new(clamp(r, 0), clamp(g, 0), clamp(b, 0), clamp(a, 255));
|
args.unexpected(&mut ctx.f);
|
||||||
|
Value::Color(RgbaColor::new(
|
||||||
args.unexpected(&mut f);
|
clamp(r, 0),
|
||||||
Pass::new(Value::Color(color), f)
|
clamp(g, 0),
|
||||||
|
clamp(b, 0),
|
||||||
|
clamp(a, 255),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
@ -49,9 +49,8 @@ use crate::geom::Linear;
|
|||||||
/// ```typst
|
/// ```typst
|
||||||
/// [font: "My Serif", serif]
|
/// [font: "My Serif", serif]
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn font(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
|
||||||
let mut f = Feedback::new();
|
let mut text = ctx.state.text.clone();
|
||||||
let mut text = ctx.style.text.clone();
|
|
||||||
let mut updated_fallback = false;
|
let mut updated_fallback = false;
|
||||||
|
|
||||||
let content = args.take::<SynTree>();
|
let content = args.take::<SynTree>();
|
||||||
@ -75,15 +74,15 @@ pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<
|
|||||||
updated_fallback = true;
|
updated_fallback = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(style) = args.take_key::<FontStyle>("style", &mut f) {
|
if let Some(style) = args.take_key::<FontStyle>("style", &mut ctx.f) {
|
||||||
text.variant.style = style;
|
text.variant.style = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(weight) = args.take_key::<FontWeight>("weight", &mut f) {
|
if let Some(weight) = args.take_key::<FontWeight>("weight", &mut ctx.f) {
|
||||||
text.variant.weight = weight;
|
text.variant.weight = weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(stretch) = args.take_key::<FontStretch>("stretch", &mut f) {
|
if let Some(stretch) = args.take_key::<FontStretch>("stretch", &mut ctx.f) {
|
||||||
text.variant.stretch = stretch;
|
text.variant.stretch = stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,15 +100,13 @@ pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<
|
|||||||
text.fallback.flatten();
|
text.fallback.flatten();
|
||||||
}
|
}
|
||||||
|
|
||||||
let commands = match content {
|
args.unexpected(&mut ctx.f);
|
||||||
|
Value::Commands(match content {
|
||||||
Some(tree) => vec![
|
Some(tree) => vec![
|
||||||
SetTextStyle(text),
|
SetTextState(text),
|
||||||
LayoutSyntaxTree(tree),
|
LayoutSyntaxTree(tree),
|
||||||
SetTextStyle(ctx.style.text.clone()),
|
SetTextState(ctx.state.text.clone()),
|
||||||
],
|
],
|
||||||
None => vec![SetTextStyle(text)],
|
None => vec![SetTextState(text)],
|
||||||
};
|
})
|
||||||
|
|
||||||
args.unexpected(&mut f);
|
|
||||||
Pass::commands(commands, f)
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ macro_rules! std {
|
|||||||
|
|
||||||
macro_rules! wrap {
|
macro_rules! wrap {
|
||||||
($func:expr) => {
|
($func:expr) => {
|
||||||
FuncValue::new(|name, args, ctx| Box::pin($func(name, args, ctx)))
|
FuncValue::new(|args, ctx| Box::pin($func(args, ctx)))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,56 +19,54 @@ use crate::paper::{Paper, PaperClass};
|
|||||||
/// - `top`: The top margin (length or relative to height).
|
/// - `top`: The top margin (length or relative to height).
|
||||||
/// - `bottom`: The bottom margin (length or relative to height).
|
/// - `bottom`: The bottom margin (length or relative to height).
|
||||||
/// - `flip`: Flips custom or paper-defined width and height (boolean).
|
/// - `flip`: Flips custom or paper-defined width and height (boolean).
|
||||||
pub async fn page(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn page(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
|
||||||
let mut f = Feedback::new();
|
let mut page = ctx.state.page.clone();
|
||||||
let mut style = ctx.style.page;
|
|
||||||
|
|
||||||
if let Some(paper) = args.take::<Paper>() {
|
if let Some(paper) = args.take::<Paper>() {
|
||||||
style.class = paper.class;
|
page.class = paper.class;
|
||||||
style.size = paper.size();
|
page.size = paper.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Abs(width)) = args.take_key::<Abs>("width", &mut f) {
|
if let Some(Abs(width)) = args.take_key::<Abs>("width", &mut ctx.f) {
|
||||||
style.class = PaperClass::Custom;
|
page.class = PaperClass::Custom;
|
||||||
style.size.width = width;
|
page.size.width = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Abs(height)) = args.take_key::<Abs>("height", &mut f) {
|
if let Some(Abs(height)) = args.take_key::<Abs>("height", &mut ctx.f) {
|
||||||
style.class = PaperClass::Custom;
|
page.class = PaperClass::Custom;
|
||||||
style.size.height = height;
|
page.size.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(margins) = args.take_key::<Linear>("margins", &mut f) {
|
if let Some(margins) = args.take_key::<Linear>("margins", &mut ctx.f) {
|
||||||
style.margins = Sides::uniform(Some(margins));
|
page.margins = Sides::uniform(Some(margins));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(left) = args.take_key::<Linear>("left", &mut f) {
|
if let Some(left) = args.take_key::<Linear>("left", &mut ctx.f) {
|
||||||
style.margins.left = Some(left);
|
page.margins.left = Some(left);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(top) = args.take_key::<Linear>("top", &mut f) {
|
if let Some(top) = args.take_key::<Linear>("top", &mut ctx.f) {
|
||||||
style.margins.top = Some(top);
|
page.margins.top = Some(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(right) = args.take_key::<Linear>("right", &mut f) {
|
if let Some(right) = args.take_key::<Linear>("right", &mut ctx.f) {
|
||||||
style.margins.right = Some(right);
|
page.margins.right = Some(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(bottom) = args.take_key::<Linear>("bottom", &mut f) {
|
if let Some(bottom) = args.take_key::<Linear>("bottom", &mut ctx.f) {
|
||||||
style.margins.bottom = Some(bottom);
|
page.margins.bottom = Some(bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.take_key::<bool>("flip", &mut f).unwrap_or(false) {
|
if args.take_key::<bool>("flip", &mut ctx.f).unwrap_or(false) {
|
||||||
mem::swap(&mut style.size.width, &mut style.size.height);
|
mem::swap(&mut page.size.width, &mut page.size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.unexpected(&mut f);
|
args.unexpected(&mut ctx.f);
|
||||||
Pass::commands(vec![SetPageStyle(style)], f)
|
Value::Commands(vec![SetPageState(page)])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `pagebreak`: Ends the current page.
|
/// `pagebreak`: Ends the current page.
|
||||||
pub async fn pagebreak(_: Span, args: DictValue, _: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn pagebreak(args: DictValue, ctx: &mut LayoutContext) -> Value {
|
||||||
let mut f = Feedback::new();
|
args.unexpected(&mut ctx.f);
|
||||||
args.unexpected(&mut f);
|
Value::Commands(vec![BreakPage])
|
||||||
Pass::commands(vec![BreakPage], f)
|
|
||||||
}
|
}
|
||||||
|
@ -6,35 +6,26 @@ use crate::layout::SpacingKind;
|
|||||||
///
|
///
|
||||||
/// # Positional arguments
|
/// # Positional arguments
|
||||||
/// - The spacing (length or relative to font size).
|
/// - The spacing (length or relative to font size).
|
||||||
pub async fn h(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn h(args: DictValue, ctx: &mut LayoutContext) -> Value {
|
||||||
spacing(name, args, ctx, SpecAxis::Horizontal)
|
spacing(args, ctx, SpecAxis::Horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `v`: Add vertical spacing.
|
/// `v`: Add vertical spacing.
|
||||||
///
|
///
|
||||||
/// # Positional arguments
|
/// # Positional arguments
|
||||||
/// - The spacing (length or relative to font size).
|
/// - The spacing (length or relative to font size).
|
||||||
pub async fn v(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn v(args: DictValue, ctx: &mut LayoutContext) -> Value {
|
||||||
spacing(name, args, ctx, SpecAxis::Vertical)
|
spacing(args, ctx, SpecAxis::Vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spacing(
|
fn spacing(mut args: DictValue, ctx: &mut LayoutContext, axis: SpecAxis) -> Value {
|
||||||
name: Span,
|
let spacing = args.expect::<Linear>("spacing", Span::ZERO, &mut ctx.f);
|
||||||
mut args: DictValue,
|
args.unexpected(&mut ctx.f);
|
||||||
ctx: LayoutContext<'_>,
|
Value::Commands(if let Some(spacing) = spacing {
|
||||||
axis: SpecAxis,
|
let axis = axis.to_gen(ctx.state.sys);
|
||||||
) -> Pass<Value> {
|
let spacing = spacing.eval(ctx.state.text.font_size());
|
||||||
let mut f = Feedback::new();
|
|
||||||
|
|
||||||
let spacing = args.expect::<Linear>("spacing", name, &mut f);
|
|
||||||
let commands = if let Some(spacing) = spacing {
|
|
||||||
let axis = axis.to_gen(ctx.sys);
|
|
||||||
let spacing = spacing.eval(ctx.style.text.font_size());
|
|
||||||
vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
|
vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
})
|
||||||
|
|
||||||
args.unexpected(&mut f);
|
|
||||||
Pass::commands(commands, f)
|
|
||||||
}
|
}
|
||||||
|
@ -7,20 +7,20 @@
|
|||||||
use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant};
|
use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant};
|
||||||
use ttf_parser::GlyphId;
|
use ttf_parser::GlyphId;
|
||||||
|
|
||||||
|
use crate::eval::TextState;
|
||||||
use crate::font::FontLoader;
|
use crate::font::FontLoader;
|
||||||
use crate::geom::{Point, Size};
|
use crate::geom::{Point, Size};
|
||||||
use crate::layout::{BoxLayout, Dir, LayoutAlign, LayoutElement, LayoutElements, Shaped};
|
use crate::layout::{BoxLayout, Dir, LayoutAlign, LayoutElement, LayoutElements, Shaped};
|
||||||
use crate::style::TextStyle;
|
|
||||||
|
|
||||||
/// Shape text into a box.
|
/// Shape text into a box.
|
||||||
pub async fn shape(
|
pub async fn shape(
|
||||||
text: &str,
|
text: &str,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
align: LayoutAlign,
|
align: LayoutAlign,
|
||||||
style: &TextStyle,
|
state: &TextState,
|
||||||
loader: &mut FontLoader,
|
loader: &mut FontLoader,
|
||||||
) -> BoxLayout {
|
) -> BoxLayout {
|
||||||
Shaper::new(text, dir, align, style, loader).shape().await
|
Shaper::new(text, dir, align, state, loader).shape().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs super-basic text shaping.
|
/// Performs super-basic text shaping.
|
||||||
@ -40,16 +40,16 @@ impl<'a> Shaper<'a> {
|
|||||||
text: &'a str,
|
text: &'a str,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
align: LayoutAlign,
|
align: LayoutAlign,
|
||||||
style: &'a TextStyle,
|
state: &'a TextState,
|
||||||
loader: &'a mut FontLoader,
|
loader: &'a mut FontLoader,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut variant = style.variant;
|
let mut variant = state.variant;
|
||||||
|
|
||||||
if style.strong {
|
if state.strong {
|
||||||
variant.weight = variant.weight.thicken(300);
|
variant.weight = variant.weight.thicken(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
if style.emph {
|
if state.emph {
|
||||||
variant.style = match variant.style {
|
variant.style = match variant.style {
|
||||||
FontStyle::Normal => FontStyle::Italic,
|
FontStyle::Normal => FontStyle::Italic,
|
||||||
FontStyle::Italic => FontStyle::Normal,
|
FontStyle::Italic => FontStyle::Normal,
|
||||||
@ -61,11 +61,11 @@ impl<'a> Shaper<'a> {
|
|||||||
text,
|
text,
|
||||||
dir,
|
dir,
|
||||||
variant,
|
variant,
|
||||||
fallback: &style.fallback,
|
fallback: &state.fallback,
|
||||||
loader,
|
loader,
|
||||||
shaped: Shaped::new(FaceId::MAX, style.font_size()),
|
shaped: Shaped::new(FaceId::MAX, state.font_size()),
|
||||||
layout: BoxLayout {
|
layout: BoxLayout {
|
||||||
size: Size::new(0.0, style.font_size()),
|
size: Size::new(0.0, state.font_size()),
|
||||||
align,
|
align,
|
||||||
elements: LayoutElements::new(),
|
elements: LayoutElements::new(),
|
||||||
},
|
},
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use crate::eval::Value;
|
use crate::eval::Value;
|
||||||
use crate::layout::LayoutContext;
|
use crate::layout::LayoutContext;
|
||||||
use crate::syntax::{Decoration, Ident, Lit, LitDict, SpanWith, Spanned};
|
use crate::syntax::{Decoration, Ident, Lit, LitDict, SpanWith, Spanned};
|
||||||
use crate::{DynFuture, Feedback};
|
use crate::DynFuture;
|
||||||
|
|
||||||
/// An expression.
|
/// An expression.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -20,17 +20,13 @@ pub enum Expr {
|
|||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Evaluate the expression to a value.
|
/// Evaluate the expression to a value.
|
||||||
pub fn eval<'a>(
|
pub fn eval<'a>(&'a self, ctx: &'a mut LayoutContext) -> DynFuture<'a, Value> {
|
||||||
&'a self,
|
|
||||||
ctx: &'a LayoutContext<'a>,
|
|
||||||
f: &'a mut Feedback,
|
|
||||||
) -> DynFuture<'a, Value> {
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
match self {
|
match self {
|
||||||
Self::Lit(lit) => lit.eval(ctx, f).await,
|
Self::Lit(lit) => lit.eval(ctx).await,
|
||||||
Self::Unary(unary) => unary.eval(ctx, f).await,
|
Self::Unary(unary) => unary.eval(ctx).await,
|
||||||
Self::Binary(binary) => binary.eval(ctx, f).await,
|
Self::Binary(binary) => binary.eval(ctx).await,
|
||||||
Self::Call(call) => call.eval(ctx, f).await,
|
Self::Call(call) => call.eval(ctx).await,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -47,10 +43,10 @@ pub struct ExprUnary {
|
|||||||
|
|
||||||
impl ExprUnary {
|
impl ExprUnary {
|
||||||
/// Evaluate the expression to a value.
|
/// Evaluate the expression to a value.
|
||||||
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
|
pub async fn eval(&self, ctx: &mut LayoutContext) -> Value {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
|
|
||||||
let value = self.expr.v.eval(ctx, f).await;
|
let value = self.expr.v.eval(ctx).await;
|
||||||
if value == Error {
|
if value == Error {
|
||||||
return Error;
|
return Error;
|
||||||
}
|
}
|
||||||
@ -64,7 +60,7 @@ impl ExprUnary {
|
|||||||
Relative(x) => Relative(-x),
|
Relative(x) => Relative(-x),
|
||||||
Linear(x) => Linear(-x),
|
Linear(x) => Linear(-x),
|
||||||
v => {
|
v => {
|
||||||
error!(@f, span, "cannot negate {}", v.name());
|
error!(@ctx.f, span, "cannot negate {}", v.name());
|
||||||
Value::Error
|
Value::Error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -92,12 +88,12 @@ pub struct ExprBinary {
|
|||||||
|
|
||||||
impl ExprBinary {
|
impl ExprBinary {
|
||||||
/// Evaluate the expression to a value.
|
/// Evaluate the expression to a value.
|
||||||
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
|
pub async fn eval(&self, ctx: &mut LayoutContext) -> Value {
|
||||||
use crate::geom::Linear as Lin;
|
use crate::geom::Linear as Lin;
|
||||||
use Value::*;
|
use Value::*;
|
||||||
|
|
||||||
let lhs = self.lhs.v.eval(ctx, f).await;
|
let lhs = self.lhs.v.eval(ctx).await;
|
||||||
let rhs = self.rhs.v.eval(ctx, f).await;
|
let rhs = self.rhs.v.eval(ctx).await;
|
||||||
|
|
||||||
if lhs == Error || rhs == Error {
|
if lhs == Error || rhs == Error {
|
||||||
return Error;
|
return Error;
|
||||||
@ -131,7 +127,7 @@ impl ExprBinary {
|
|||||||
(Commands(a), Commands(b)) => Commands(concat(a, b)),
|
(Commands(a), Commands(b)) => Commands(concat(a, b)),
|
||||||
|
|
||||||
(a, b) => {
|
(a, b) => {
|
||||||
error!(@f, span, "cannot add {} and {}", a.name(), b.name());
|
error!(@ctx.f, span, "cannot add {} and {}", a.name(), b.name());
|
||||||
Value::Error
|
Value::Error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -155,7 +151,7 @@ impl ExprBinary {
|
|||||||
(Linear(a), Linear(b)) => Linear(a - b),
|
(Linear(a), Linear(b)) => Linear(a - b),
|
||||||
|
|
||||||
(a, b) => {
|
(a, b) => {
|
||||||
error!(@f, span, "cannot subtract {1} from {0}", a.name(), b.name());
|
error!(@ctx.f, span, "cannot subtract {1} from {0}", a.name(), b.name());
|
||||||
Value::Error
|
Value::Error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -186,7 +182,7 @@ impl ExprBinary {
|
|||||||
(Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)),
|
(Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)),
|
||||||
|
|
||||||
(a, b) => {
|
(a, b) => {
|
||||||
error!(@f, span, "cannot multiply {} with {}", a.name(), b.name());
|
error!(@ctx.f, span, "cannot multiply {} with {}", a.name(), b.name());
|
||||||
Value::Error
|
Value::Error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -207,7 +203,7 @@ impl ExprBinary {
|
|||||||
(Linear(a), Float(b)) => Linear(a / b),
|
(Linear(a), Float(b)) => Linear(a / b),
|
||||||
|
|
||||||
(a, b) => {
|
(a, b) => {
|
||||||
error!(@f, span, "cannot divide {} by {}", a.name(), b.name());
|
error!(@ctx.f, span, "cannot divide {} by {}", a.name(), b.name());
|
||||||
Value::Error
|
Value::Error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -248,20 +244,18 @@ pub struct ExprCall {
|
|||||||
|
|
||||||
impl ExprCall {
|
impl ExprCall {
|
||||||
/// Evaluate the call expression to a value.
|
/// Evaluate the call expression to a value.
|
||||||
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
|
pub async fn eval(&self, ctx: &mut LayoutContext) -> Value {
|
||||||
let name = &self.name.v;
|
let name = &self.name.v;
|
||||||
let span = self.name.span;
|
let span = self.name.span;
|
||||||
let args = self.args.eval(ctx, f).await;
|
let args = self.args.eval(ctx).await;
|
||||||
|
|
||||||
if let Some(func) = ctx.scope.func(name) {
|
if let Some(func) = ctx.state.scope.func(name) {
|
||||||
let pass = func(span, args, ctx.clone()).await;
|
ctx.f.decorations.push(Decoration::Resolved.span_with(span));
|
||||||
f.extend(pass.feedback);
|
(func.clone())(args, ctx).await
|
||||||
f.decorations.push(Decoration::Resolved.span_with(span));
|
|
||||||
pass.output
|
|
||||||
} else {
|
} else {
|
||||||
if !name.is_empty() {
|
if !name.is_empty() {
|
||||||
error!(@f, span, "unknown function");
|
error!(@ctx.f, span, "unknown function");
|
||||||
f.decorations.push(Decoration::Unresolved.span_with(span));
|
ctx.f.decorations.push(Decoration::Unresolved.span_with(span));
|
||||||
}
|
}
|
||||||
Value::Dict(args)
|
Value::Dict(args)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use crate::eval::{DictKey, DictValue, SpannedEntry, Value};
|
|||||||
use crate::layout::LayoutContext;
|
use crate::layout::LayoutContext;
|
||||||
use crate::length::Length;
|
use crate::length::Length;
|
||||||
use crate::syntax::{Expr, Ident, SpanWith, Spanned, SynTree};
|
use crate::syntax::{Expr, Ident, SpanWith, Spanned, SynTree};
|
||||||
use crate::{DynFuture, Feedback};
|
use crate::DynFuture;
|
||||||
|
|
||||||
/// A literal.
|
/// A literal.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -39,7 +39,7 @@ pub enum Lit {
|
|||||||
|
|
||||||
impl Lit {
|
impl Lit {
|
||||||
/// Evaluate the dictionary literal to a dictionary value.
|
/// Evaluate the dictionary literal to a dictionary value.
|
||||||
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
|
pub async fn eval(&self, ctx: &mut LayoutContext) -> Value {
|
||||||
match *self {
|
match *self {
|
||||||
Lit::Ident(ref i) => Value::Ident(i.clone()),
|
Lit::Ident(ref i) => Value::Ident(i.clone()),
|
||||||
Lit::Bool(b) => Value::Bool(b),
|
Lit::Bool(b) => Value::Bool(b),
|
||||||
@ -49,7 +49,7 @@ impl Lit {
|
|||||||
Lit::Percent(p) => Value::Relative(p / 100.0),
|
Lit::Percent(p) => Value::Relative(p / 100.0),
|
||||||
Lit::Color(c) => Value::Color(c),
|
Lit::Color(c) => Value::Color(c),
|
||||||
Lit::Str(ref s) => Value::Str(s.clone()),
|
Lit::Str(ref s) => Value::Str(s.clone()),
|
||||||
Lit::Dict(ref d) => Value::Dict(d.eval(ctx, f).await),
|
Lit::Dict(ref d) => Value::Dict(d.eval(ctx).await),
|
||||||
Lit::Content(ref c) => Value::Tree(c.clone()),
|
Lit::Content(ref c) => Value::Tree(c.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,16 +66,12 @@ impl LitDict {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the dictionary literal to a dictionary value.
|
/// Evaluate the dictionary literal to a dictionary value.
|
||||||
pub fn eval<'a>(
|
pub fn eval<'a>(&'a self, ctx: &'a mut LayoutContext) -> DynFuture<'a, DictValue> {
|
||||||
&'a self,
|
|
||||||
ctx: &'a LayoutContext<'a>,
|
|
||||||
f: &'a mut Feedback,
|
|
||||||
) -> DynFuture<'a, DictValue> {
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut dict = DictValue::new();
|
let mut dict = DictValue::new();
|
||||||
|
|
||||||
for entry in &self.0 {
|
for entry in &self.0 {
|
||||||
let val = entry.expr.v.eval(ctx, f).await;
|
let val = entry.expr.v.eval(ctx).await;
|
||||||
let spanned = val.span_with(entry.expr.span);
|
let spanned = val.span_with(entry.expr.span);
|
||||||
if let Some(key) = &entry.key {
|
if let Some(key) = &entry.key {
|
||||||
dict.insert(&key.v, SpannedEntry::new(key.span, spanned));
|
dict.insert(&key.v, SpannedEntry::new(key.span, spanned));
|
||||||
|
@ -11,6 +11,7 @@ use futures_executor::block_on;
|
|||||||
use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
|
use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
|
||||||
use ttf_parser::OutlineBuilder;
|
use ttf_parser::OutlineBuilder;
|
||||||
|
|
||||||
|
use typstc::eval::State;
|
||||||
use typstc::export::pdf;
|
use typstc::export::pdf;
|
||||||
use typstc::font::{FontLoader, SharedFontLoader};
|
use typstc::font::{FontLoader, SharedFontLoader};
|
||||||
use typstc::geom::{Point, Vec2};
|
use typstc::geom::{Point, Vec2};
|
||||||
@ -69,12 +70,11 @@ fn main() {
|
|||||||
fn test(name: &str, src: &str, src_path: &Path, loader: &SharedFontLoader) {
|
fn test(name: &str, src: &str, src_path: &Path, loader: &SharedFontLoader) {
|
||||||
println!("Testing {}.", name);
|
println!("Testing {}.", name);
|
||||||
|
|
||||||
let style = Default::default();
|
let state = State::default();
|
||||||
let scope = typstc::library::_std();
|
|
||||||
let Pass {
|
let Pass {
|
||||||
output: layouts,
|
output: layouts,
|
||||||
feedback: Feedback { mut diagnostics, .. },
|
feedback: Feedback { mut diagnostics, .. },
|
||||||
} = block_on(typeset(&src, &style, &scope, Rc::clone(loader)));
|
} = block_on(typeset(&src, state, Rc::clone(loader)));
|
||||||
|
|
||||||
if !diagnostics.is_empty() {
|
if !diagnostics.is_empty() {
|
||||||
diagnostics.sort();
|
diagnostics.sort();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user