Style changes

This commit is contained in:
Laurenz 2022-11-04 09:30:44 +01:00
parent 33928a00dc
commit eb951c008b
65 changed files with 522 additions and 706 deletions

View File

@ -115,10 +115,7 @@ fn parse_args() -> StrResult<Command> {
// Don't allow excess arguments. // Don't allow excess arguments.
let rest = args.finish(); let rest = args.finish();
if !rest.is_empty() { if !rest.is_empty() {
Err(format!( Err(format!("unexpected argument{}", if rest.len() > 1 { "s" } else { "" }))?;
"unexpected argument{}",
if rest.len() > 1 { "s" } else { "" }
))?;
} }
Ok(command) Ok(command)

View File

@ -101,14 +101,12 @@ pub fn mod_(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
(Value::Int(a), Value::Float(b)) => (a as f64, b), (Value::Int(a), Value::Float(b)) => (a as f64, b),
(Value::Float(a), Value::Int(b)) => (a, b as f64), (Value::Float(a), Value::Int(b)) => (a, b as f64),
(Value::Float(a), Value::Float(b)) => (a, b), (Value::Float(a), Value::Float(b)) => (a, b),
(Value::Int(_), b) | (Value::Float(_), b) => bail!( (Value::Int(_), b) | (Value::Float(_), b) => {
span2, bail!(span2, format!("expected integer or float, found {}", b.type_name()))
format!("expected integer or float, found {}", b.type_name()) }
), (a, _) => {
(a, _) => bail!( bail!(span1, format!("expected integer or float, found {}", a.type_name()))
span1, }
format!("expected integer or float, found {}", a.type_name())
),
}; };
if b == 0.0 { if b == 0.0 {

View File

@ -10,8 +10,7 @@ pub fn luma(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
/// Create an RGB(A) color. /// Create an RGB(A) color.
pub fn rgb(_: &mut Vm, args: &mut Args) -> SourceResult<Value> { pub fn rgb(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
Ok(Value::Color( Ok(Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? {
if let Some(string) = args.find::<Spanned<EcoString>>()? {
match RgbaColor::from_str(&string.v) { match RgbaColor::from_str(&string.v) {
Ok(color) => color.into(), Ok(color) => color.into(),
Err(msg) => bail!(string.span, msg), Err(msg) => bail!(string.span, msg),
@ -22,8 +21,7 @@ pub fn rgb(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
let Component(b) = args.expect("blue component")?; let Component(b) = args.expect("blue component")?;
let Component(a) = args.eat()?.unwrap_or(Component(255)); let Component(a) = args.eat()?.unwrap_or(Component(255));
RgbaColor::new(r, g, b, a).into() RgbaColor::new(r, g, b, a).into()
}, }))
))
} }
/// Create a CMYK color. /// Create a CMYK color.

View File

@ -81,10 +81,7 @@ fn convert_json(value: serde_json::Value) -> Value {
/// Format the user-facing JSON error message. /// Format the user-facing JSON error message.
fn format_json_error(error: serde_json::Error) -> String { fn format_json_error(error: serde_json::Error) -> String {
assert!(error.is_syntax() || error.is_eof()); assert!(error.is_syntax() || error.is_eof());
format!( format!("failed to parse json file: syntax error in line {}", error.line())
"failed to parse json file: syntax error in line {}",
error.line()
)
} }
/// Read structured data from an XML file. /// Read structured data from an XML file.

View File

@ -5,10 +5,10 @@ mod color;
mod data; mod data;
mod string; mod string;
pub use calc::*; pub use self::calc::*;
pub use color::*; pub use self::color::*;
pub use data::*; pub use self::data::*;
pub use string::*; pub use self::string::*;
use comemo::Track; use comemo::Track;
use typst::model::{Eval, Route, Scopes, Vm}; use typst::model::{Eval, Route, Scopes, Vm};

View File

@ -55,11 +55,7 @@ impl ContentExt for Content {
let mut seq = vec![]; let mut seq = vec![];
if let Some(above) = above { if let Some(above) = above {
seq.push( seq.push(
layout::VNode { layout::VNode { amount: above.into(), weak: true, generated: true }
amount: above.into(),
weak: true,
generated: true,
}
.pack(), .pack(),
); );
} }
@ -67,11 +63,7 @@ impl ContentExt for Content {
seq.push(self); seq.push(self);
if let Some(below) = below { if let Some(below) = below {
seq.push( seq.push(
layout::VNode { layout::VNode { amount: below.into(), weak: true, generated: true }
amount: below.into(),
weak: true,
generated: true,
}
.pack(), .pack(),
); );
} }

View File

@ -5,7 +5,7 @@ mod image;
mod line; mod line;
mod shape; mod shape;
pub use self::hide::*;
pub use self::image::*; pub use self::image::*;
pub use hide::*; pub use self::line::*;
pub use line::*; pub use self::shape::*;
pub use shape::*;

View File

@ -75,11 +75,7 @@ impl LayoutBlock for ColumnsNode {
let mut cursor = Abs::zero(); let mut cursor = Abs::zero();
for _ in 0..columns { for _ in 0..columns {
let frame = match frames.next() { let Some(frame) = frames.next() else { break };
Some(frame) => frame,
None => break,
};
if !regions.expand.y { if !regions.expand.y {
output.size_mut().y.set_max(frame.height()); output.size_mut().y.set_max(frame.height());
} }

View File

@ -12,17 +12,17 @@ mod spacing;
mod stack; mod stack;
mod transform; mod transform;
pub use align::*; pub use self::align::*;
pub use columns::*; pub use self::columns::*;
pub use container::*; pub use self::container::*;
pub use flow::*; pub use self::flow::*;
pub use grid::*; pub use self::grid::*;
pub use pad::*; pub use self::pad::*;
pub use page::*; pub use self::page::*;
pub use place::*; pub use self::place::*;
pub use spacing::*; pub use self::spacing::*;
pub use stack::*; pub use self::stack::*;
pub use transform::*; pub use self::transform::*;
use std::mem; use std::mem;
@ -357,7 +357,10 @@ impl<'a> Builder<'a> {
content: &'a Content, content: &'a Content,
styles: StyleChain<'a>, styles: StyleChain<'a>,
) -> SourceResult<bool> { ) -> SourceResult<bool> {
if let Some(mut realized) = styles.apply(self.world, Target::Node(content))? { let Some(mut realized) = styles.apply(self.world, Target::Node(content))? else {
return Ok(false);
};
let mut map = StyleMap::new(); let mut map = StyleMap::new();
let barrier = Barrier::new(content.id()); let barrier = Barrier::new(content.id());
map.push(StyleEntry::Barrier(barrier)); map.push(StyleEntry::Barrier(barrier));
@ -365,10 +368,8 @@ impl<'a> Builder<'a> {
realized = realized.styled_with_map(map); realized = realized.styled_with_map(map);
let stored = self.scratch.templates.alloc(realized); let stored = self.scratch.templates.alloc(realized);
self.accept(stored, styles)?; self.accept(stored, styles)?;
Ok(true) Ok(true)
} else {
Ok(false)
}
} }
fn styled( fn styled(
@ -466,10 +467,7 @@ impl<'a> DocBuilder<'a> {
impl Default for DocBuilder<'_> { impl Default for DocBuilder<'_> {
fn default() -> Self { fn default() -> Self {
Self { Self { pages: StyleVecBuilder::new(), keep_next: true }
pages: StyleVecBuilder::new(),
keep_next: true,
}
} }
} }
@ -658,30 +656,25 @@ impl<'a> ListBuilder<'a> {
{ {
self.items.push(item.clone(), styles); self.items.push(item.clone(), styles);
self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakNode>()); self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakNode>());
} else { return true;
return false;
} }
} else if !self.items.is_empty() } else if !self.items.is_empty()
&& (content.is::<SpaceNode>() || content.is::<ParbreakNode>()) && (content.is::<SpaceNode>() || content.is::<ParbreakNode>())
{ {
self.staged.push((content, styles)); self.staged.push((content, styles));
} else { return true;
return false;
} }
true false
} }
fn finish(self, parent: &mut Builder<'a>) -> SourceResult<()> { fn finish(self, parent: &mut Builder<'a>) -> SourceResult<()> {
let (items, shared) = self.items.finish(); let (items, shared) = self.items.finish();
let kind = match items.items().next() {
Some(item) => item.kind(),
None => return Ok(()),
};
let Some(item) = items.items().next() else { return Ok(()) };
let tight = self.tight; let tight = self.tight;
let attached = tight && self.attachable; let attached = tight && self.attachable;
let content = match kind { let content = match item.kind() {
LIST => ListNode::<LIST> { tight, attached, items }.pack(), LIST => ListNode::<LIST> { tight, attached, items }.pack(),
ENUM => ListNode::<ENUM> { tight, attached, items }.pack(), ENUM => ListNode::<ENUM> { tight, attached, items }.pack(),
DESC | _ => ListNode::<DESC> { tight, attached, items }.pack(), DESC | _ => ListNode::<DESC> { tight, attached, items }.pack(),
@ -765,18 +758,15 @@ impl<'a, T> CollapsingBuilder<'a, T> {
} }
if self.last == Last::Weak { if self.last == Last::Weak {
if let Some(i) = let weak = self.staged.iter().position(|(prev_item, _, prev_weakness)| {
self.staged.iter().position(|(prev_item, _, prev_weakness)| {
prev_weakness.map_or(false, |prev_weakness| { prev_weakness.map_or(false, |prev_weakness| {
weakness < prev_weakness weakness < prev_weakness
|| (weakness == prev_weakness && item > *prev_item) || (weakness == prev_weakness && item > *prev_item)
}) })
}) });
{
self.staged.remove(i); let Some(weak) = weak else { return };
} else { self.staged.remove(weak);
return;
}
} }
self.staged.push((item, styles, Some(weakness))); self.staged.push((item, styles, Some(weakness)));

View File

@ -111,12 +111,7 @@ impl PageNode {
let pw = size.x - pad.left - pad.right; let pw = size.x - pad.left - pad.right;
let py = size.y - pad.bottom; let py = size.y - pad.bottom;
for (role, marginal, pos, area) in [ for (role, marginal, pos, area) in [
( (Role::Header, header, Point::with_x(pad.left), Size::new(pw, pad.top)),
Role::Header,
header,
Point::with_x(pad.left),
Size::new(pw, pad.top),
),
( (
Role::Footer, Role::Footer,
footer, footer,

View File

@ -69,12 +69,7 @@ impl LayoutInline for MathNode {
_: &Regions, _: &Regions,
styles: StyleChain, styles: StyleChain,
) -> SourceResult<Vec<Frame>> { ) -> SourceResult<Vec<Frame>> {
Ok(vec![layout_tex( Ok(vec![layout_tex(&self.texify(), self.display, world, styles)?])
&self.texify(),
self.display,
world,
styles,
)?])
} }
} }

View File

@ -95,9 +95,9 @@ impl<const L: ListKind> Show for ListNode<L> {
match name { match name {
"tight" => Some(Value::Bool(self.tight)), "tight" => Some(Value::Bool(self.tight)),
"attached" => Some(Value::Bool(self.attached)), "attached" => Some(Value::Bool(self.attached)),
"items" => Some(Value::Array( "items" => {
self.items.items().map(|item| item.encode()).collect(), Some(Value::Array(self.items.items().map(|item| item.encode()).collect()))
)), }
_ => None, _ => None,
} }
} }
@ -139,11 +139,7 @@ impl<const L: ListKind> Show for ListNode<L> {
ListItem::List(body) => body.as_ref().clone(), ListItem::List(body) => body.as_ref().clone(),
ListItem::Enum(_, body) => body.as_ref().clone(), ListItem::Enum(_, body) => body.as_ref().clone(),
ListItem::Desc(item) => Content::sequence(vec![ ListItem::Desc(item) => Content::sequence(vec![
HNode { HNode { amount: (-body_indent).into(), weak: false }.pack(),
amount: (-body_indent).into(),
weak: false,
}
.pack(),
(item.term.clone() + TextNode(':'.into()).pack()).strong(), (item.term.clone() + TextNode(':'.into()).pack()).strong(),
SpaceNode.pack(), SpaceNode.pack(),
item.body.clone(), item.body.clone(),

View File

@ -6,8 +6,8 @@ mod list;
mod reference; mod reference;
mod table; mod table;
pub use doc::*; pub use self::doc::*;
pub use heading::*; pub use self::heading::*;
pub use list::*; pub use self::list::*;
pub use reference::*; pub use self::reference::*;
pub use table::*; pub use self::table::*;

View File

@ -56,13 +56,16 @@ impl<const L: DecoLine> Show for DecoNode<L> {
_: Tracked<dyn World>, _: Tracked<dyn World>,
styles: StyleChain, styles: StyleChain,
) -> SourceResult<Content> { ) -> SourceResult<Content> {
Ok(self.0.clone().styled(TextNode::DECO, Decoration { Ok(self.0.clone().styled(
TextNode::DECO,
Decoration {
line: L, line: L,
stroke: styles.get(Self::STROKE).unwrap_or_default(), stroke: styles.get(Self::STROKE).unwrap_or_default(),
offset: styles.get(Self::OFFSET), offset: styles.get(Self::OFFSET),
extent: styles.get(Self::EXTENT), extent: styles.get(Self::EXTENT),
evade: styles.get(Self::EVADE), evade: styles.get(Self::EVADE),
})) },
))
} }
} }

View File

@ -8,12 +8,12 @@ mod raw;
mod shaping; mod shaping;
mod shift; mod shift;
pub use deco::*; pub use self::deco::*;
pub use link::*; pub use self::link::*;
pub use par::*; pub use self::par::*;
pub use raw::*; pub use self::raw::*;
pub use shaping::*; pub use self::shaping::*;
pub use shift::*; pub use self::shift::*;
use std::borrow::Cow; use std::borrow::Cow;

View File

@ -500,11 +500,14 @@ fn prepare<'a>(
regions: &Regions, regions: &Regions,
styles: StyleChain<'a>, styles: StyleChain<'a>,
) -> SourceResult<Preparation<'a>> { ) -> SourceResult<Preparation<'a>> {
let bidi = BidiInfo::new(text, match styles.get(TextNode::DIR) { let bidi = BidiInfo::new(
text,
match styles.get(TextNode::DIR) {
Dir::LTR => Some(BidiLevel::ltr()), Dir::LTR => Some(BidiLevel::ltr()),
Dir::RTL => Some(BidiLevel::rtl()), Dir::RTL => Some(BidiLevel::rtl()),
_ => None, _ => None,
}); },
);
let mut cursor = 0; let mut cursor = 0;
let mut items = vec![]; let mut items = vec![];

View File

@ -117,22 +117,38 @@ impl<'s> Quotes<'s> {
/// The opening quote. /// The opening quote.
fn open(&self, double: bool) -> &'s str { fn open(&self, double: bool) -> &'s str {
if double { self.double_open } else { self.single_open } if double {
self.double_open
} else {
self.single_open
}
} }
/// The closing quote. /// The closing quote.
fn close(&self, double: bool) -> &'s str { fn close(&self, double: bool) -> &'s str {
if double { self.double_close } else { self.single_close } if double {
self.double_close
} else {
self.single_close
}
} }
/// Which character should be used as a prime. /// Which character should be used as a prime.
fn prime(&self, double: bool) -> &'static str { fn prime(&self, double: bool) -> &'static str {
if double { "" } else { "" } if double {
""
} else {
""
}
} }
/// Which character should be used as a fallback quote. /// Which character should be used as a fallback quote.
fn fallback(&self, double: bool) -> &'static str { fn fallback(&self, double: bool) -> &'static str {
if double { "\"" } else { "" } if double {
"\""
} else {
""
}
} }
} }

View File

@ -98,7 +98,6 @@ impl<'a> ShapedText<'a> {
self.glyphs.as_ref().group_by_key(|g| (g.font.clone(), g.y_offset)) self.glyphs.as_ref().group_by_key(|g| (g.font.clone(), g.y_offset))
{ {
let pos = Point::new(offset, top + shift + y_offset.at(self.size)); let pos = Point::new(offset, top + shift + y_offset.at(self.size));
let glyphs = group let glyphs = group
.iter() .iter()
.map(|glyph| Glyph { .map(|glyph| Glyph {
@ -115,14 +114,7 @@ impl<'a> ShapedText<'a> {
}) })
.collect(); .collect();
let text = Text { let text = Text { font, size: self.size, lang, fill, glyphs };
font,
size: self.size,
lang,
fill,
glyphs,
};
let text_layer = frame.layer(); let text_layer = frame.layer();
let width = text.width(); let width = text.width();
@ -274,7 +266,11 @@ impl<'a> ShapedText<'a> {
.glyphs .glyphs
.binary_search_by(|g| { .binary_search_by(|g| {
let ordering = g.cluster.cmp(&text_index); let ordering = g.cluster.cmp(&text_index);
if ltr { ordering } else { ordering.reverse() } if ltr {
ordering
} else {
ordering.reverse()
}
}) })
.ok()?; .ok()?;
@ -385,9 +381,7 @@ fn shape_segment<'a>(
} }
// Extract the font id or shape notdef glyphs if we couldn't find any font. // Extract the font id or shape notdef glyphs if we couldn't find any font.
let font = if let Some(font) = selection { let Some(font) = selection else {
font
} else {
if let Some(font) = ctx.used.first().cloned() { if let Some(font) = ctx.used.first().cloned() {
shape_tofus(ctx, base, text, font); shape_tofus(ctx, base, text, font);
} }

View File

@ -78,10 +78,7 @@ fn search_text(content: &Content, mode: ShiftKind) -> Option<EcoString> {
} else if content.is::<SpaceNode>() { } else if content.is::<SpaceNode>() {
Some(' '.into()) Some(' '.into())
} else if let Some(text) = content.downcast::<TextNode>() { } else if let Some(text) = content.downcast::<TextNode>() {
if let Some(sup) = convert_script(&text.0, mode) { convert_script(&text.0, mode)
return Some(sup);
}
None
} else if let Some(seq) = content.downcast::<SequenceNode>() { } else if let Some(seq) = content.downcast::<SequenceNode>() {
let mut full = EcoString::new(); let mut full = EcoString::new();
for item in seq.0.iter() { for item in seq.0.iter() {

View File

@ -18,7 +18,8 @@ pub fn capability(_: TokenStream, item: TokenStream) -> TokenStream {
quote! { quote! {
#item_trait #item_trait
impl ::typst::model::Capability for dyn #name {} impl ::typst::model::Capability for dyn #name {}
}.into() }
.into()
} }
/// Implement `Node` for a struct. /// Implement `Node` for a struct.
@ -349,10 +350,7 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
let span = property.name.span(); let span = property.name.span();
if property.skip && property.shorthand.is_some() { if property.skip && property.shorthand.is_some() {
return Err(Error::new( return Err(Error::new(span, "skip and shorthand are mutually exclusive"));
span,
"skip and shorthand are mutually exclusive",
));
} }
if property.referenced && (property.fold || property.resolve) { if property.referenced && (property.fold || property.resolve) {

View File

@ -1,11 +1,6 @@
unstable_features = true use_small_heuristics = "Max"
max_width = 90
overflow_delimited_expr = true chain_width = 70
spaces_around_ranges = true struct_lit_width = 50
use_field_init_shorthand = true use_field_init_shorthand = true
merge_derives = false merge_derives = false
max_width = 90
struct_lit_width = 40
chain_width = 70
single_line_if_else_max_width = 60

View File

@ -3,5 +3,5 @@
mod pdf; mod pdf;
mod render; mod render;
pub use pdf::pdf; pub use self::pdf::pdf;
pub use render::render; pub use self::render::render;

View File

@ -312,10 +312,7 @@ fn render_shape(
if let Some(Stroke { paint, thickness }) = shape.stroke { if let Some(Stroke { paint, thickness }) = shape.stroke {
let paint = paint.into(); let paint = paint.into();
let stroke = sk::Stroke { let stroke = sk::Stroke { width: thickness.to_f32(), ..Default::default() };
width: thickness.to_f32(),
..Default::default()
};
canvas.stroke_path(&path, &paint, &stroke, ts, mask); canvas.stroke_path(&path, &paint, &stroke, ts, mask);
} }
@ -342,11 +339,8 @@ fn render_image(
match image.decode().unwrap() { match image.decode().unwrap() {
DecodedImage::Raster(dynamic, _) => { DecodedImage::Raster(dynamic, _) => {
let downscale = w < image.width(); let downscale = w < image.width();
let filter = if downscale { let filter =
FilterType::Lanczos3 if downscale { FilterType::Lanczos3 } else { FilterType::CatmullRom };
} else {
FilterType::CatmullRom
};
let buf = dynamic.resize(w, h, filter); let buf = dynamic.resize(w, h, filter);
for ((_, _, src), dest) in buf.pixels().zip(pixmap.pixels_mut()) { for ((_, _, src), dest) in buf.pixels().zip(pixmap.pixels_mut()) {
let Rgba([r, g, b, a]) = src; let Rgba([r, g, b, a]) = src;

View File

@ -305,9 +305,8 @@ fn typographic_family(mut family: &str) -> &str {
const SEPARATORS: [char; 3] = [' ', '-', '_']; const SEPARATORS: [char; 3] = [' ', '-', '_'];
// Modifiers that can appear in combination with suffixes. // Modifiers that can appear in combination with suffixes.
const MODIFIERS: &[&str] = &[ const MODIFIERS: &[&str] =
"extra", "ext", "ex", "x", "semi", "sem", "sm", "demi", "dem", "ultra", &["extra", "ext", "ex", "x", "semi", "sem", "sm", "demi", "dem", "ultra"];
];
// Style suffixes. // Style suffixes.
#[rustfmt::skip] #[rustfmt::skip]
@ -331,9 +330,8 @@ fn typographic_family(mut family: &str) -> &str {
len = trimmed.len(); len = trimmed.len();
// Find style suffix. // Find style suffix.
let mut t = match SUFFIXES.iter().find_map(|s| trimmed.strip_suffix(s)) { let Some(mut t) = SUFFIXES.iter().find_map(|s| trimmed.strip_suffix(s)) else {
Some(t) => t, break;
None => break,
}; };
// Strip optional separator. // Strip optional separator.
@ -432,19 +430,13 @@ mod tests {
assert_eq!(typographic_family("eras bold"), "eras"); assert_eq!(typographic_family("eras bold"), "eras");
assert_eq!(typographic_family("footlight mt light"), "footlight mt"); assert_eq!(typographic_family("footlight mt light"), "footlight mt");
assert_eq!(typographic_family("times new roman"), "times new roman"); assert_eq!(typographic_family("times new roman"), "times new roman");
assert_eq!( assert_eq!(typographic_family("noto sans mono cond sembd"), "noto sans mono");
typographic_family("noto sans mono cond sembd"),
"noto sans mono"
);
assert_eq!(typographic_family("noto serif SEMCOND sembd"), "noto serif"); assert_eq!(typographic_family("noto serif SEMCOND sembd"), "noto serif");
assert_eq!(typographic_family("crimson text"), "crimson text"); assert_eq!(typographic_family("crimson text"), "crimson text");
assert_eq!(typographic_family("footlight light"), "footlight"); assert_eq!(typographic_family("footlight light"), "footlight");
assert_eq!(typographic_family("Noto Sans"), "Noto Sans"); assert_eq!(typographic_family("Noto Sans"), "Noto Sans");
assert_eq!(typographic_family("Noto Sans Light"), "Noto Sans"); assert_eq!(typographic_family("Noto Sans Light"), "Noto Sans");
assert_eq!( assert_eq!(typographic_family("Noto Sans Semicondensed Heavy"), "Noto Sans");
typographic_family("Noto Sans Semicondensed Heavy"),
"Noto Sans"
);
assert_eq!(typographic_family("Familx"), "Familx"); assert_eq!(typographic_family("Familx"), "Familx");
assert_eq!(typographic_family("Font Ultra"), "Font Ultra"); assert_eq!(typographic_family("Font Ultra"), "Font Ultra");
assert_eq!(typographic_family("Font Ultra Bold"), "Font"); assert_eq!(typographic_family("Font Ultra Bold"), "Font");

View File

@ -3,8 +3,8 @@
mod book; mod book;
mod variant; mod variant;
pub use book::*; pub use self::book::*;
pub use variant::*; pub use self::variant::*;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};

View File

@ -538,9 +538,6 @@ impl Role {
pub fn is_weak(self) -> bool { pub fn is_weak(self) -> bool {
// In Typst, all text is in a paragraph, so paragraph isn't very // In Typst, all text is in a paragraph, so paragraph isn't very
// descriptive. // descriptive.
matches!( matches!(self, Self::Paragraph | Self::GenericBlock | Self::GenericInline)
self,
Self::Paragraph | Self::GenericBlock | Self::GenericInline
)
} }
} }

View File

@ -54,10 +54,7 @@ impl<T> Axes<T> {
/// Zip two instances into an instance over a tuple. /// Zip two instances into an instance over a tuple.
pub fn zip<U>(self, other: Axes<U>) -> Axes<(T, U)> { pub fn zip<U>(self, other: Axes<U>) -> Axes<(T, U)> {
Axes { Axes { x: (self.x, other.x), y: (self.y, other.y) }
x: (self.x, other.x),
y: (self.y, other.y),
}
} }
/// Whether a condition is true for at least one of fields. /// Whether a condition is true for at least one of fields.
@ -100,18 +97,12 @@ impl<T: Default> Axes<T> {
impl<T: Ord> Axes<T> { impl<T: Ord> Axes<T> {
/// The component-wise minimum of this and another instance. /// The component-wise minimum of this and another instance.
pub fn min(self, other: Self) -> Self { pub fn min(self, other: Self) -> Self {
Self { Self { x: self.x.min(other.x), y: self.y.min(other.y) }
x: self.x.min(other.x),
y: self.y.min(other.y),
}
} }
/// The component-wise minimum of this and another instance. /// The component-wise minimum of this and another instance.
pub fn max(self, other: Self) -> Self { pub fn max(self, other: Self) -> Self {
Self { Self { x: self.x.max(other.x), y: self.y.max(other.y) }
x: self.x.max(other.x),
y: self.y.max(other.y),
}
} }
} }

View File

@ -16,12 +16,7 @@ pub struct Corners<T> {
impl<T> Corners<T> { impl<T> Corners<T> {
/// Create a new instance from the four components. /// Create a new instance from the four components.
pub const fn new(top_left: T, top_right: T, bottom_right: T, bottom_left: T) -> Self { pub const fn new(top_left: T, top_right: T, bottom_right: T, bottom_left: T) -> Self {
Self { Self { top_left, top_right, bottom_right, bottom_left }
top_left,
top_right,
bottom_right,
bottom_left,
}
} }
/// Create an instance with four equal components. /// Create an instance with four equal components.
@ -66,12 +61,7 @@ impl<T> Corners<T> {
/// An iterator over the corners, starting with the top left corner, /// An iterator over the corners, starting with the top left corner,
/// clockwise. /// clockwise.
pub fn iter(&self) -> impl Iterator<Item = &T> { pub fn iter(&self) -> impl Iterator<Item = &T> {
[ [&self.top_left, &self.top_right, &self.bottom_right, &self.bottom_left]
&self.top_left,
&self.top_right,
&self.bottom_right,
&self.bottom_left,
]
.into_iter() .into_iter()
} }

View File

@ -18,9 +18,5 @@ pub fn ellipse(size: Size, fill: Option<Paint>, stroke: Option<Stroke>) -> Shape
path.cubic_to(point(rx, my), point(mx, ry), point(z, ry)); path.cubic_to(point(rx, my), point(mx, ry), point(z, ry));
path.cubic_to(point(-mx, ry), point(-rx, my), point(-rx, z)); path.cubic_to(point(-mx, ry), point(-rx, my), point(-rx, z));
Shape { Shape { geometry: Geometry::Path(path), stroke, fill }
geometry: Geometry::Path(path),
stroke,
fill,
}
} }

View File

@ -45,7 +45,11 @@ impl Em {
/// Convert to an absolute length at the given font size. /// Convert to an absolute length at the given font size.
pub fn at(self, font_size: Abs) -> Abs { pub fn at(self, font_size: Abs) -> Abs {
let resolved = font_size * self.get(); let resolved = font_size * self.get();
if resolved.is_finite() { resolved } else { Abs::zero() } if resolved.is_finite() {
resolved
} else {
Abs::zero()
}
} }
} }

View File

@ -92,10 +92,7 @@ impl Add for Length {
type Output = Self; type Output = Self;
fn add(self, rhs: Self) -> Self::Output { fn add(self, rhs: Self) -> Self::Output {
Self { Self { abs: self.abs + rhs.abs, em: self.em + rhs.em }
abs: self.abs + rhs.abs,
em: self.em + rhs.em,
}
} }
} }

View File

@ -24,27 +24,27 @@ mod size;
mod stroke; mod stroke;
mod transform; mod transform;
pub use abs::*; pub use self::abs::*;
pub use align::*; pub use self::align::*;
pub use angle::*; pub use self::angle::*;
pub use axes::*; pub use self::axes::*;
pub use corners::*; pub use self::corners::*;
pub use dir::*; pub use self::dir::*;
pub use ellipse::*; pub use self::ellipse::*;
pub use em::*; pub use self::em::*;
pub use fr::*; pub use self::fr::*;
pub use length::*; pub use self::length::*;
pub use paint::*; pub use self::paint::*;
pub use path::*; pub use self::path::*;
pub use point::*; pub use self::point::*;
pub use ratio::*; pub use self::ratio::*;
pub use rel::*; pub use self::rel::*;
pub use rounded::*; pub use self::rounded::*;
pub use scalar::*; pub use self::scalar::*;
pub use sides::*; pub use self::sides::*;
pub use size::*; pub use self::size::*;
pub use stroke::*; pub use self::stroke::*;
pub use transform::*; pub use self::transform::*;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::f64::consts::PI; use std::f64::consts::PI;
@ -95,20 +95,12 @@ pub enum Geometry {
impl Geometry { impl Geometry {
/// Fill the geometry without a stroke. /// Fill the geometry without a stroke.
pub fn filled(self, fill: Paint) -> Shape { pub fn filled(self, fill: Paint) -> Shape {
Shape { Shape { geometry: self, fill: Some(fill), stroke: None }
geometry: self,
fill: Some(fill),
stroke: None,
}
} }
/// Stroke the geometry without a fill. /// Stroke the geometry without a fill.
pub fn stroked(self, stroke: Stroke) -> Shape { pub fn stroked(self, stroke: Stroke) -> Shape {
Shape { Shape { geometry: self, fill: None, stroke: Some(stroke) }
geometry: self,
fill: None,
stroke: Some(stroke),
}
} }
} }

View File

@ -324,12 +324,7 @@ impl CmykColor {
round_u8(255.0 * (1.0 - c) * (1.0 - k)) round_u8(255.0 * (1.0 - c) * (1.0 - k))
}; };
RgbaColor { RgbaColor { r: f(self.c), g: f(self.m), b: f(self.y), a: 255 }
r: f(self.c),
g: f(self.m),
b: f(self.y),
a: 255,
}
} }
/// Lighten this color by a factor. /// Lighten this color by a factor.

View File

@ -46,7 +46,11 @@ impl Ratio {
/// Return the ratio of the given `whole`. /// Return the ratio of the given `whole`.
pub fn of<T: Numeric>(self, whole: T) -> T { pub fn of<T: Numeric>(self, whole: T) -> T {
let resolved = whole * self.get(); let resolved = whole * self.get();
if resolved.is_finite() { resolved } else { T::zero() } if resolved.is_finite() {
resolved
} else {
T::zero()
}
} }
} }

View File

@ -128,10 +128,7 @@ impl<T: Numeric> Mul<f64> for Rel<T> {
type Output = Self; type Output = Self;
fn mul(self, other: f64) -> Self::Output { fn mul(self, other: f64) -> Self::Output {
Self { Self { rel: self.rel * other, abs: self.abs * other }
rel: self.rel * other,
abs: self.abs * other,
}
} }
} }
@ -147,10 +144,7 @@ impl<T: Numeric> Div<f64> for Rel<T> {
type Output = Self; type Output = Self;
fn div(self, other: f64) -> Self::Output { fn div(self, other: f64) -> Self::Output {
Self { Self { rel: self.rel / other, abs: self.abs / other }
rel: self.rel / other,
abs: self.abs / other,
}
} }
} }

View File

@ -21,11 +21,7 @@ pub fn rounded_rect(
if !stroke.is_uniform() { if !stroke.is_uniform() {
for (path, stroke) in stroke_segments(size, radius, stroke) { for (path, stroke) in stroke_segments(size, radius, stroke) {
if stroke.is_some() { if stroke.is_some() {
res.push(Shape { res.push(Shape { geometry: Geometry::Path(path), fill: None, stroke });
geometry: Geometry::Path(path),
fill: None,
stroke,
});
} }
} }
} }

View File

@ -29,11 +29,7 @@ impl Args {
pub fn new(span: Span, values: impl IntoIterator<Item = Value>) -> Self { pub fn new(span: Span, values: impl IntoIterator<Item = Value>) -> Self {
let items = values let items = values
.into_iter() .into_iter()
.map(|value| Arg { .map(|value| Arg { span, name: None, value: Spanned::new(value, span) })
span,
name: None,
value: Spanned::new(value, span),
})
.collect(); .collect();
Self { span, items } Self { span, items }
} }

View File

@ -287,11 +287,7 @@ impl Array {
/// Resolve an index. /// Resolve an index.
fn locate(&self, index: i64) -> Option<usize> { fn locate(&self, index: i64) -> Option<usize> {
usize::try_from(if index >= 0 { usize::try_from(if index >= 0 { index } else { self.len().checked_add(index)? })
index
} else {
self.len().checked_add(index)?
})
.ok() .ok()
} }
} }

View File

@ -161,10 +161,7 @@ impl Add for Content {
return lhs; return lhs;
} }
let seq = match ( let seq = match (lhs.downcast::<SequenceNode>(), rhs.downcast::<SequenceNode>()) {
lhs.downcast::<SequenceNode>(),
rhs.downcast::<SequenceNode>(),
) {
(Some(lhs), Some(rhs)) => lhs.0.iter().chain(&rhs.0).cloned().collect(), (Some(lhs), Some(rhs)) => lhs.0.iter().chain(&rhs.0).cloned().collect(),
(Some(lhs), None) => lhs.0.iter().cloned().chain(iter::once(rhs)).collect(), (Some(lhs), None) => lhs.0.iter().cloned().chain(iter::once(rhs)).collect(),
(None, Some(rhs)) => iter::once(lhs).chain(rhs.0.iter().cloned()).collect(), (None, Some(rhs)) => iter::once(lhs).chain(rhs.0.iter().cloned()).collect(),

View File

@ -140,7 +140,6 @@ fn eval_markup(
vm.scopes.top.define(wrap.binding().take(), tail); vm.scopes.top.define(wrap.binding().take(), tail);
wrap.body().eval(vm)?.display(vm.world) wrap.body().eval(vm)?.display(vm.world)
} }
_ => node.eval(vm)?, _ => node.eval(vm)?,
}); });
@ -369,10 +368,7 @@ impl Eval for ast::Frac {
type Output = Content; type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> { fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
Ok((vm.items.math_frac)( Ok((vm.items.math_frac)(self.num().eval(vm)?, self.denom().eval(vm)?))
self.num().eval(vm)?,
self.denom().eval(vm)?,
))
} }
} }
@ -501,7 +497,6 @@ fn eval_code(
vm.scopes.top.define(wrap.binding().take(), tail); vm.scopes.top.define(wrap.binding().take(), tail);
wrap.body().eval(vm)? wrap.body().eval(vm)?
} }
_ => expr.eval(vm)?, _ => expr.eval(vm)?,
}; };
@ -676,18 +671,12 @@ impl Eval for ast::FieldAccess {
Ok(match object { Ok(match object {
Value::Dict(dict) => dict.get(&field).at(span)?.clone(), Value::Dict(dict) => dict.get(&field).at(span)?.clone(),
Value::Content(node) => node Value::Content(node) => node
.to::<dyn Show>() .to::<dyn Show>()
.and_then(|node| node.field(&field)) .and_then(|node| node.field(&field))
.ok_or_else(|| format!("unknown field {field:?}")) .ok_or_else(|| format!("unknown field {field:?}"))
.at(span)?, .at(span)?,
v => bail!(self.target().span(), "cannot access field on {}", v.type_name()),
v => bail!(
self.target().span(),
"cannot access field on {}",
v.type_name()
),
}) })
} }
} }
@ -706,7 +695,6 @@ impl Eval for ast::FuncCall {
let point = || Tracepoint::Call(func.name().map(Into::into)); let point = || Tracepoint::Call(func.name().map(Into::into));
func.call(vm, args).trace(vm.world, point, self.span())? func.call(vm, args).trace(vm.world, point, self.span())?
} }
v => bail!( v => bail!(
self.callee().span(), self.callee().span(),
"expected callable or collection, found {}", "expected callable or collection, found {}",

View File

@ -32,12 +32,7 @@ impl Func {
name: &'static str, name: &'static str,
func: fn(&mut Vm, &mut Args) -> SourceResult<Value>, func: fn(&mut Vm, &mut Args) -> SourceResult<Value>,
) -> Self { ) -> Self {
Self(Arc::new(Repr::Native(Native { Self(Arc::new(Repr::Native(Native { name, func, set: None, node: None })))
name,
func,
set: None,
node: None,
})))
} }
/// Create a new function from a native rust node. /// Create a new function from a native rust node.
@ -194,12 +189,15 @@ impl Closure {
// Parse the arguments according to the parameter list. // Parse the arguments according to the parameter list.
for (param, default) in &self.params { for (param, default) in &self.params {
scopes.top.define(param.clone(), match default { scopes.top.define(
None => args.expect::<Value>(param)?, param.clone(),
match default {
Some(default) => { Some(default) => {
args.named::<Value>(param)?.unwrap_or_else(|| default.clone()) args.named::<Value>(param)?.unwrap_or_else(|| default.clone())
} }
}); None => args.expect::<Value>(param)?,
},
);
} }
// Put the remaining arguments into the sink. // Put the remaining arguments into the sink.

View File

@ -1,4 +1,4 @@
//! Layout and computation model. //! Document and computation model.
#[macro_use] #[macro_use]
mod items; mod items;
@ -23,18 +23,18 @@ mod ops;
mod scope; mod scope;
mod vm; mod vm;
pub use self::str::*;
pub use args::*;
pub use array::*;
pub use cast::*;
pub use content::*;
pub use dict::*;
pub use eval::*;
pub use func::*;
pub use items::*;
pub use scope::*;
pub use styles::*;
pub use value::*;
pub use vm::*;
pub use typst_macros::{capability, node}; pub use typst_macros::{capability, node};
pub use self::args::*;
pub use self::array::*;
pub use self::cast::*;
pub use self::content::*;
pub use self::dict::*;
pub use self::eval::*;
pub use self::func::*;
pub use self::items::*;
pub use self::scope::*;
pub use self::str::*;
pub use self::styles::*;
pub use self::value::*;
pub use self::vm::*;

View File

@ -103,18 +103,18 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
if let (Some(&a), Some(&b)) = if let (Some(&a), Some(&b)) =
(a.downcast::<GenAlign>(), b.downcast::<GenAlign>()) (a.downcast::<GenAlign>(), b.downcast::<GenAlign>())
{ {
if a.axis() != b.axis() { if a.axis() == b.axis() {
Value::dynamic(match a.axis() {
Axis::X => Axes { x: a, y: b },
Axis::Y => Axes { x: b, y: a },
})
} else {
return Err(format!("cannot add two {:?} alignments", a.axis())); return Err(format!("cannot add two {:?} alignments", a.axis()));
} }
} else {
return Ok(Value::dynamic(match a.axis() {
Axis::X => Axes { x: a, y: b },
Axis::Y => Axes { x: b, y: a },
}));
};
mismatch!("cannot add {} and {}", a, b); mismatch!("cannot add {} and {}", a, b);
} }
}
(a, b) => mismatch!("cannot add {} and {}", a, b), (a, b) => mismatch!("cannot add {} and {}", a, b),
}) })
@ -370,17 +370,11 @@ pub fn not_in(lhs: Value, rhs: Value) -> StrResult<Value> {
/// Test for containment. /// Test for containment.
pub fn contains(lhs: &Value, rhs: &Value) -> Option<bool> { pub fn contains(lhs: &Value, rhs: &Value) -> Option<bool> {
Some(match (lhs, rhs) { match (lhs, rhs) {
(Str(a), Str(b)) => b.as_str().contains(a.as_str()), (Str(a), Str(b)) => Some(b.as_str().contains(a.as_str())),
(Dyn(a), Str(b)) => { (Dyn(a), Str(b)) => a.downcast::<Regex>().map(|regex| regex.is_match(b)),
if let Some(regex) = a.downcast::<Regex>() { (Str(a), Dict(b)) => Some(b.contains(a)),
regex.is_match(b) (a, Array(b)) => Some(b.contains(a)),
} else { _ => Option::None,
return Option::None;
} }
} }
(Str(a), Dict(b)) => b.contains(a),
(a, Array(b)) => b.contains(a),
_ => return Option::None,
})
}

View File

@ -72,11 +72,7 @@ impl Str {
/// Resolve an index. /// Resolve an index.
fn locate(&self, index: i64) -> Option<usize> { fn locate(&self, index: i64) -> Option<usize> {
usize::try_from(if index >= 0 { usize::try_from(if index >= 0 { index } else { self.len().checked_add(index)? })
index
} else {
self.len().checked_add(index)?
})
.ok() .ok()
} }
@ -271,10 +267,7 @@ impl Str {
/// The out of bounds access error message. /// The out of bounds access error message.
#[cold] #[cold]
fn out_of_bounds(index: i64, len: i64) -> String { fn out_of_bounds(index: i64, len: i64) -> String {
format!( format!("string index out of bounds (index: {}, len: {})", index, len)
"string index out of bounds (index: {}, len: {})",
index, len
)
} }
/// Convert an item of std's `match_indices` to a dictionary. /// Convert an item of std's `match_indices` to a dictionary.

View File

@ -159,10 +159,7 @@ impl StyleEntry {
} }
} }
StyleChain { StyleChain { head: std::slice::from_ref(self), tail: Some(tail) }
head: std::slice::from_ref(self),
tail: Some(tail),
}
} }
/// If this is a property, return it. /// If this is a property, return it.
@ -344,10 +341,7 @@ impl<'a> StyleChain<'a> {
/// Iterate over the entries of the chain. /// Iterate over the entries of the chain.
fn entries(self) -> Entries<'a> { fn entries(self) -> Entries<'a> {
Entries { Entries { inner: [].as_slice().iter(), links: self.links() }
inner: [].as_slice().iter(),
links: self.links(),
}
} }
/// Iterate over the links of the chain. /// Iterate over the links of the chain.
@ -1066,11 +1060,7 @@ impl Recipe {
impl Debug for Recipe { impl Debug for Recipe {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!( write!(f, "Recipe matching {:?} from {:?}", self.pattern, self.func.span)
f,
"Recipe matching {:?} from {:?}",
self.pattern, self.func.span
)
} }
} }

View File

@ -294,11 +294,8 @@ where
} }
fn dyn_eq(&self, other: &Dynamic) -> bool { fn dyn_eq(&self, other: &Dynamic) -> bool {
if let Some(other) = other.downcast::<Self>() { let Some(other) = other.downcast::<Self>() else { return false };
self == other self == other
} else {
false
}
} }
fn dyn_type_name(&self) -> &'static str { fn dyn_type_name(&self) -> &'static str {
@ -411,15 +408,9 @@ mod tests {
test(Abs::pt(5.5), "5.5pt"); test(Abs::pt(5.5), "5.5pt");
test(Angle::deg(90.0), "90deg"); test(Angle::deg(90.0), "90deg");
test(Ratio::one() / 2.0, "50%"); test(Ratio::one() / 2.0, "50%");
test( test(Ratio::new(0.3) + Length::from(Abs::cm(2.0)), "30% + 56.69pt");
Ratio::new(0.3) + Length::from(Abs::cm(2.0)),
"30% + 56.69pt",
);
test(Fr::one() * 7.55, "7.55fr"); test(Fr::one() * 7.55, "7.55fr");
test( test(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "rgb(\"#010101\")");
Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)),
"rgb(\"#010101\")",
);
// Collections. // Collections.
test("hello", r#""hello""#); test("hello", r#""hello""#);

View File

@ -1471,7 +1471,11 @@ impl ForPattern {
pub fn key(&self) -> Option<Ident> { pub fn key(&self) -> Option<Ident> {
let mut children = self.0.children().filter_map(SyntaxNode::cast); let mut children = self.0.children().filter_map(SyntaxNode::cast);
let key = children.next(); let key = children.next();
if children.next().is_some() { key } else { None } if children.next().is_some() {
key
} else {
None
}
} }
/// The value part of the pattern. /// The value part of the pattern.

View File

@ -421,20 +421,26 @@ mod tests {
test("= *AB*", &[(0..6, Heading), (2..6, Strong)]); test("= *AB*", &[(0..6, Heading), (2..6, Strong)]);
test("#f(x + 1)", &[ test(
"#f(x + 1)",
&[
(0..2, Function), (0..2, Function),
(2..3, Bracket), (2..3, Bracket),
(5..6, Operator), (5..6, Operator),
(7..8, Number), (7..8, Number),
(8..9, Bracket), (8..9, Bracket),
]); ],
);
test("#let f(x) = x", &[ test(
"#let f(x) = x",
&[
(0..4, Keyword), (0..4, Keyword),
(5..6, Function), (5..6, Function),
(6..7, Bracket), (6..7, Bracket),
(8..9, Bracket), (8..9, Bracket),
(10..11, Operator), (10..11, Operator),
]); ],
);
} }
} }

View File

@ -126,10 +126,13 @@ fn try_reparse(
// If we were looking for a non-whitespace element and hit the end of // If we were looking for a non-whitespace element and hit the end of
// the file here, we instead use EOF as the end of the span. // the file here, we instead use EOF as the end of the span.
if let SearchState::RequireNonTrivia(start) = search { if let SearchState::RequireNonTrivia(start) = search {
search = SearchState::SpanFound(start, NodePos { search = SearchState::SpanFound(
start,
NodePos {
idx: node.children().len() - 1, idx: node.children().len() - 1,
offset: offset - node.children().last().unwrap().len(), offset: offset - node.children().last().unwrap().len(),
}) },
)
} }
if let SearchState::Contained(pos) = search { if let SearchState::Contained(pos) = search {
@ -351,11 +354,7 @@ impl Ahead {
Self { Self {
pos, pos,
at_start, at_start,
kind: if bounded { kind: if bounded { AheadKind::Normal } else { AheadKind::Unbounded(true) },
AheadKind::Normal
} else {
AheadKind::Unbounded(true)
},
} }
} }

View File

@ -2,6 +2,7 @@
pub mod ast; pub mod ast;
pub mod highlight; pub mod highlight;
mod incremental; mod incremental;
mod kind; mod kind;
mod node; mod node;
@ -12,12 +13,12 @@ mod source;
mod span; mod span;
mod tokens; mod tokens;
pub use kind::*; pub use self::kind::*;
pub use node::*; pub use self::node::*;
pub use parsing::*; pub use self::parsing::*;
pub use source::*; pub use self::source::*;
pub use span::*; pub use self::span::*;
pub use tokens::*; pub use self::tokens::*;
use incremental::reparse; use incremental::reparse;
use parser::*; use parser::*;

View File

@ -177,7 +177,11 @@ impl<'s> Parser<'s> {
/// Peek at the current token without consuming it. /// Peek at the current token without consuming it.
pub fn peek(&self) -> Option<&NodeKind> { pub fn peek(&self) -> Option<&NodeKind> {
if self.eof { None } else { self.current.as_ref() } if self.eof {
None
} else {
self.current.as_ref()
}
} }
/// Peek at the current token, but only if it follows immediately after the /// Peek at the current token, but only if it follows immediately after the

View File

@ -210,11 +210,7 @@ where
/// Parse a markup node. /// Parse a markup node.
fn markup_node(p: &mut Parser, at_start: &mut bool) { fn markup_node(p: &mut Parser, at_start: &mut bool) {
let token = match p.peek() { let Some(token) = p.peek() else { return };
Some(t) => t,
None => return,
};
match token { match token {
// Whitespace. // Whitespace.
NodeKind::Space { newlines } => { NodeKind::Space { newlines } => {
@ -420,12 +416,9 @@ fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option<NodeKind>) {
Some(NodeKind::Underscore) => { Some(NodeKind::Underscore) => {
(NodeKind::Script, 2, Assoc::Right, Some(NodeKind::Hat)) (NodeKind::Script, 2, Assoc::Right, Some(NodeKind::Hat))
} }
Some(NodeKind::Hat) => ( Some(NodeKind::Hat) => {
NodeKind::Script, (NodeKind::Script, 2, Assoc::Right, Some(NodeKind::Underscore))
2, }
Assoc::Right,
Some(NodeKind::Underscore),
),
Some(NodeKind::Slash) => (NodeKind::Frac, 1, Assoc::Left, None), Some(NodeKind::Slash) => (NodeKind::Frac, 1, Assoc::Left, None),
_ => break, _ => break,
}; };
@ -454,11 +447,7 @@ fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option<NodeKind>) {
/// Parse a primary math node. /// Parse a primary math node.
fn math_primary(p: &mut Parser) { fn math_primary(p: &mut Parser) {
let token = match p.peek() { let Some(token) = p.peek() else { return };
Some(t) => t,
None => return,
};
match token { match token {
// Spaces, atoms and expressions. // Spaces, atoms and expressions.
NodeKind::Space { .. } NodeKind::Space { .. }
@ -652,7 +641,6 @@ fn literal(p: &mut Parser) -> bool {
p.eat(); p.eat();
true true
} }
_ => false, _ => false,
} }
} }
@ -724,21 +712,26 @@ enum CollectionKind {
/// Returns the length of the collection and whether the literal contained any /// Returns the length of the collection and whether the literal contained any
/// commas. /// commas.
fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) { fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) {
let mut kind = None; let mut collection_kind = None;
let mut items = 0; let mut items = 0;
let mut can_group = true; let mut can_group = true;
let mut missing_coma: Option<Marker> = None; let mut missing_coma: Option<Marker> = None;
while !p.eof() { while !p.eof() {
if let Ok(item_kind) = item(p, keyed) { let Ok(item_kind) = item(p, keyed) else {
p.eat_if(NodeKind::Comma);
collection_kind = Some(CollectionKind::Group);
continue;
};
match item_kind { match item_kind {
NodeKind::Spread => can_group = false, NodeKind::Spread => can_group = false,
NodeKind::Named if kind.is_none() => { NodeKind::Named if collection_kind.is_none() => {
kind = Some(CollectionKind::Named); collection_kind = Some(CollectionKind::Named);
can_group = false; can_group = false;
} }
_ if kind.is_none() => { _ if collection_kind.is_none() => {
kind = Some(CollectionKind::Positional); collection_kind = Some(CollectionKind::Positional);
} }
_ => {} _ => {}
} }
@ -758,16 +751,12 @@ fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) {
} else { } else {
missing_coma = Some(p.trivia_start()); missing_coma = Some(p.trivia_start());
} }
} else {
p.eat_if(NodeKind::Comma);
kind = Some(CollectionKind::Group);
}
} }
let kind = if can_group && items == 1 { let kind = if can_group && items == 1 {
CollectionKind::Group CollectionKind::Group
} else { } else {
kind.unwrap_or(CollectionKind::Positional) collection_kind.unwrap_or(CollectionKind::Positional)
}; };
(kind, items) (kind, items)

View File

@ -30,7 +30,6 @@ pub fn resolve_string(string: &str) -> EcoString {
None => out.push_str(s.from(start)), None => out.push_str(s.from(start)),
} }
} }
_ => out.push_str(s.from(start)), _ => out.push_str(s.from(start)),
} }
} }
@ -66,10 +65,7 @@ pub fn resolve_raw(column: usize, backticks: usize, text: &str) -> RawKind {
/// Parse the lang tag and return it alongside the remaining inner raw text. /// Parse the lang tag and return it alongside the remaining inner raw text.
fn split_at_lang_tag(raw: &str) -> (&str, &str) { fn split_at_lang_tag(raw: &str) -> (&str, &str) {
let mut s = Scanner::new(raw); let mut s = Scanner::new(raw);
( (s.eat_until(|c: char| c == '`' || c.is_whitespace() || is_newline(c)), s.after())
s.eat_until(|c: char| c == '`' || c.is_whitespace() || is_newline(c)),
s.after(),
)
} }
/// Trim raw text and splits it into lines. /// Trim raw text and splits it into lines.

View File

@ -212,7 +212,6 @@ impl Source {
(k == utf16_idx).then(|| self.text.len()) (k == utf16_idx).then(|| self.text.len())
} }
/// Return the byte position at which the given line starts. /// Return the byte position at which the given line starts.
pub fn line_to_byte(&self, line_idx: usize) -> Option<usize> { pub fn line_to_byte(&self, line_idx: usize) -> Option<usize> {
self.lines.get(line_idx).map(|line| line.byte_idx) self.lines.get(line_idx).map(|line| line.byte_idx)
@ -312,10 +311,7 @@ fn lines(
utf16_idx += 1; utf16_idx += 1;
} }
Some(Line { Some(Line { byte_idx: byte_offset + s.cursor(), utf16_idx })
byte_idx: byte_offset + s.cursor(),
utf16_idx,
})
}) })
} }
@ -328,12 +324,15 @@ mod tests {
#[test] #[test]
fn test_source_file_new() { fn test_source_file_new() {
let source = Source::detached(TEST); let source = Source::detached(TEST);
assert_eq!(source.lines, [ assert_eq!(
source.lines,
[
Line { byte_idx: 0, utf16_idx: 0 }, Line { byte_idx: 0, utf16_idx: 0 },
Line { byte_idx: 7, utf16_idx: 6 }, Line { byte_idx: 7, utf16_idx: 6 },
Line { byte_idx: 15, utf16_idx: 12 }, Line { byte_idx: 15, utf16_idx: 12 },
Line { byte_idx: 18, utf16_idx: 15 }, Line { byte_idx: 18, utf16_idx: 15 },
]); ]
);
} }
#[test] #[test]

View File

@ -558,9 +558,8 @@ impl<'s> Tokens<'s> {
} }
} }
let v = match number.parse::<f64>() { let Ok(v) = number.parse::<f64>() else {
Ok(v) => v, return NodeKind::Error(ErrorPos::Full, "invalid number".into());
Err(_) => return NodeKind::Error(ErrorPos::Full, "invalid number".into()),
}; };
match suffix { match suffix {
@ -653,7 +652,11 @@ fn column(string: &str, index: usize, offset: usize) -> usize {
apply_offset = true; apply_offset = true;
} }
if apply_offset { res + offset } else { res } if apply_offset {
res + offset
} else {
res
}
} }
/// Whether this character denotes a newline. /// Whether this character denotes a newline.
@ -767,8 +770,8 @@ mod tests {
// - mode in which the suffix is applicable // - mode in which the suffix is applicable
// - the suffix string // - the suffix string
// - the resulting suffix NodeKind // - the resulting suffix NodeKind
fn suffixes() fn suffixes(
-> impl Iterator<Item = (char, Option<TokenMode>, &'static str, NodeKind)> { ) -> impl Iterator<Item = (char, Option<TokenMode>, &'static str, NodeKind)> {
[ [
// Whitespace suffixes. // Whitespace suffixes.
(' ', None, " ", Space(0)), (' ', None, " ", Space(0)),

View File

@ -5,7 +5,7 @@
//! pointer metadata APIs are stable, we should definitely move to them: //! pointer metadata APIs are stable, we should definitely move to them:
//! <https://github.com/rust-lang/rust/issues/81513> //! <https://github.com/rust-lang/rust/issues/81513>
use std::alloc; use std::alloc::Layout;
use std::mem; use std::mem;
/// Create a fat pointer from a data address and a vtable address. /// Create a fat pointer from a data address and a vtable address.
@ -15,12 +15,8 @@ use std::mem;
/// to a value whose type implements the trait of `T` and the `vtable` must have /// to a value whose type implements the trait of `T` and the `vtable` must have
/// been extracted with [`vtable`]. /// been extracted with [`vtable`].
pub unsafe fn from_raw_parts<T: ?Sized>(data: *const (), vtable: *const ()) -> *const T { pub unsafe fn from_raw_parts<T: ?Sized>(data: *const (), vtable: *const ()) -> *const T {
debug_assert_eq!(
alloc::Layout::new::<*const T>(),
alloc::Layout::new::<FatPointer>(),
);
let fat = FatPointer { data, vtable }; let fat = FatPointer { data, vtable };
debug_assert_eq!(Layout::new::<*const T>(), Layout::new::<FatPointer>());
mem::transmute_copy::<FatPointer, *const T>(&fat) mem::transmute_copy::<FatPointer, *const T>(&fat)
} }
@ -31,12 +27,8 @@ pub unsafe fn from_raw_parts<T: ?Sized>(data: *const (), vtable: *const ()) -> *
/// to a value whose type implements the trait of `T` and the `vtable` must have /// to a value whose type implements the trait of `T` and the `vtable` must have
/// been extracted with [`vtable`]. /// been extracted with [`vtable`].
pub unsafe fn from_raw_parts_mut<T: ?Sized>(data: *mut (), vtable: *const ()) -> *mut T { pub unsafe fn from_raw_parts_mut<T: ?Sized>(data: *mut (), vtable: *const ()) -> *mut T {
debug_assert_eq!(
alloc::Layout::new::<*mut T>(),
alloc::Layout::new::<FatPointer>(),
);
let fat = FatPointer { data, vtable }; let fat = FatPointer { data, vtable };
debug_assert_eq!(Layout::new::<*mut T>(), Layout::new::<FatPointer>());
mem::transmute_copy::<FatPointer, *mut T>(&fat) mem::transmute_copy::<FatPointer, *mut T>(&fat)
} }
@ -45,11 +37,7 @@ pub unsafe fn from_raw_parts_mut<T: ?Sized>(data: *mut (), vtable: *const ()) ->
/// # Safety /// # Safety
/// Must only be called when `T` is a `dyn Trait`. /// Must only be called when `T` is a `dyn Trait`.
pub unsafe fn vtable<T: ?Sized>(ptr: *const T) -> *const () { pub unsafe fn vtable<T: ?Sized>(ptr: *const T) -> *const () {
debug_assert_eq!( debug_assert_eq!(Layout::new::<*const T>(), Layout::new::<FatPointer>());
alloc::Layout::new::<*const T>(),
alloc::Layout::new::<FatPointer>(),
);
mem::transmute_copy::<*const T, FatPointer>(&ptr).vtable mem::transmute_copy::<*const T, FatPointer>(&ptr).vtable
} }

View File

@ -2,13 +2,13 @@
pub mod fat; pub mod fat;
pub use buffer::Buffer;
pub use eco::{format_eco, EcoString};
#[macro_use] #[macro_use]
mod eco; mod eco;
mod buffer; mod buffer;
pub use buffer::Buffer;
pub use eco::{format_eco, EcoString};
use std::any::TypeId; use std::any::TypeId;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::hash::Hash; use std::hash::Hash;

View File

@ -75,13 +75,8 @@ fn main() {
let pdf_path = let pdf_path =
args.pdf.then(|| Path::new(PDF_DIR).join(path).with_extension("pdf")); args.pdf.then(|| Path::new(PDF_DIR).join(path).with_extension("pdf"));
ok += test( ok += test(&mut world, &src_path, &png_path, &ref_path, pdf_path.as_deref())
&mut world, as usize;
&src_path,
&png_path,
&ref_path,
pdf_path.as_deref(),
) as usize;
} }
if len > 1 { if len > 1 {
@ -153,10 +148,8 @@ fn config() -> Config {
let mut styles = typst_library::styles(); let mut styles = typst_library::styles();
styles.set(PageNode::WIDTH, Smart::Custom(Abs::pt(120.0).into())); styles.set(PageNode::WIDTH, Smart::Custom(Abs::pt(120.0).into()));
styles.set(PageNode::HEIGHT, Smart::Auto); styles.set(PageNode::HEIGHT, Smart::Auto);
styles.set( styles
PageNode::MARGINS, .set(PageNode::MARGINS, Sides::splat(Some(Smart::Custom(Abs::pt(10.0).into()))));
Sides::splat(Some(Smart::Custom(Abs::pt(10.0).into()))),
);
styles.set(TextNode::SIZE, TextSize(Abs::pt(10.0).into())); styles.set(TextNode::SIZE, TextSize(Abs::pt(10.0).into()));
// Hook up helpers into the global scope. // Hook up helpers into the global scope.
@ -480,12 +473,6 @@ fn parse_metadata(source: &Source) -> (Option<bool>, Vec<(Range<usize>, String)>
compare_ref = Some(true); compare_ref = Some(true);
} }
let rest = if let Some(rest) = line.strip_prefix("// Error: ") {
rest
} else {
continue;
};
fn num(s: &mut Scanner) -> usize { fn num(s: &mut Scanner) -> usize {
s.eat_while(char::is_numeric).parse().unwrap() s.eat_while(char::is_numeric).parse().unwrap()
} }
@ -501,6 +488,7 @@ fn parse_metadata(source: &Source) -> (Option<bool>, Vec<(Range<usize>, String)>
source.line_column_to_byte(line, column).unwrap() source.line_column_to_byte(line, column).unwrap()
}; };
let Some(rest) = line.strip_prefix("// Error: ") else { continue };
let mut s = Scanner::new(rest); let mut s = Scanner::new(rest);
let start = pos(&mut s); let start = pos(&mut s);
let end = if s.eat_if('-') { pos(&mut s) } else { start }; let end = if s.eat_if('-') { pos(&mut s) } else { start };
@ -582,10 +570,7 @@ fn test_reparse(text: &str, i: usize, rng: &mut LinearShift) -> bool {
); );
println!(" Expected reference tree:\n{ref_root:#?}\n"); println!(" Expected reference tree:\n{ref_root:#?}\n");
println!(" Found incremental tree:\n{incr_root:#?}"); println!(" Found incremental tree:\n{incr_root:#?}");
println!( println!(" Full source ({}):\n\"{edited_src:?}\"", edited_src.len());
" Full source ({}):\n\"{edited_src:?}\"",
edited_src.len()
);
} }
ok &= test_spans(ref_root); ok &= test_spans(ref_root);
@ -630,10 +615,7 @@ fn test_spans(root: &SyntaxNode) -> bool {
fn test_spans_impl(node: &SyntaxNode, within: Range<u64>) -> bool { fn test_spans_impl(node: &SyntaxNode, within: Range<u64>) -> bool {
if !within.contains(&node.span().number()) { if !within.contains(&node.span().number()) {
eprintln!(" Node: {node:#?}"); eprintln!(" Node: {node:#?}");
eprintln!( eprintln!(" Wrong span order: {} not in {within:?}", node.span().number(),);
" Wrong span order: {} not in {within:?} ❌",
node.span().number(),
);
} }
let start = node.span().number() + 1; let start = node.span().number() + 1;