Compare commits
No commits in common. "46d57b00b58afefa8ed93e74655e43268571dcce" and "98034903e4184708dbaf3cf6b23a4f4de5e910ec" have entirely different histories.
46d57b00b5
...
98034903e4
4
Cargo.lock
generated
@ -413,7 +413,8 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "codex"
|
name = "codex"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/typst/codex?rev=56eb217#56eb2172fc0670f4c1c8b79a63d11f9354e5babe"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "724d27a0ee38b700e5e164350e79aba601a0db673ac47fce1cb74c3e38864036"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "color-print"
|
name = "color-print"
|
||||||
@ -2920,7 +2921,6 @@ name = "typst-docs"
|
|||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"codex",
|
|
||||||
"ecow",
|
"ecow",
|
||||||
"heck",
|
"heck",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
|
@ -47,7 +47,7 @@ clap = { version = "4.4", features = ["derive", "env", "wrap_help"] }
|
|||||||
clap_complete = "4.2.1"
|
clap_complete = "4.2.1"
|
||||||
clap_mangen = "0.2.10"
|
clap_mangen = "0.2.10"
|
||||||
codespan-reporting = "0.11"
|
codespan-reporting = "0.11"
|
||||||
codex = { git = "https://github.com/typst/codex", rev = "56eb217" }
|
codex = "0.1.1"
|
||||||
color-print = "0.3.6"
|
color-print = "0.3.6"
|
||||||
comemo = "0.4"
|
comemo = "0.4"
|
||||||
csv = "1"
|
csv = "1"
|
||||||
|
@ -10,12 +10,11 @@ use codespan_reporting::term::{self, termcolor};
|
|||||||
use ecow::eco_format;
|
use ecow::eco_format;
|
||||||
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher as _};
|
use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher as _};
|
||||||
use same_file::is_same_file;
|
use same_file::is_same_file;
|
||||||
use typst::diag::{bail, warning, StrResult};
|
use typst::diag::{bail, StrResult};
|
||||||
use typst::syntax::Span;
|
|
||||||
use typst::utils::format_duration;
|
use typst::utils::format_duration;
|
||||||
|
|
||||||
use crate::args::{Input, Output, WatchCommand};
|
use crate::args::{Input, Output, WatchCommand};
|
||||||
use crate::compile::{compile_once, print_diagnostics, CompileConfig};
|
use crate::compile::{compile_once, CompileConfig};
|
||||||
use crate::timings::Timer;
|
use crate::timings::Timer;
|
||||||
use crate::world::{SystemWorld, WorldCreationError};
|
use crate::world::{SystemWorld, WorldCreationError};
|
||||||
use crate::{print_error, terminal};
|
use crate::{print_error, terminal};
|
||||||
@ -56,11 +55,6 @@ pub fn watch(timer: &mut Timer, command: &WatchCommand) -> StrResult<()> {
|
|||||||
// Perform initial compilation.
|
// Perform initial compilation.
|
||||||
timer.record(&mut world, |world| compile_once(world, &mut config))??;
|
timer.record(&mut world, |world| compile_once(world, &mut config))??;
|
||||||
|
|
||||||
// Print warning when trying to watch stdin.
|
|
||||||
if matches!(&config.input, Input::Stdin) {
|
|
||||||
warn_watching_std(&world, &config)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recompile whenever something relevant happens.
|
// Recompile whenever something relevant happens.
|
||||||
loop {
|
loop {
|
||||||
// Watch all dependencies of the most recent compilation.
|
// Watch all dependencies of the most recent compilation.
|
||||||
@ -338,15 +332,3 @@ impl Status {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits a warning when trying to watch stdin.
|
|
||||||
fn warn_watching_std(world: &SystemWorld, config: &CompileConfig) -> StrResult<()> {
|
|
||||||
let warning = warning!(
|
|
||||||
Span::detached(),
|
|
||||||
"cannot watch changes for stdin";
|
|
||||||
hint: "to recompile on changes, watch a regular file instead";
|
|
||||||
hint: "to compile once and exit, please use `typst compile` instead"
|
|
||||||
);
|
|
||||||
print_diagnostics(world, &[], &[warning], config.diagnostic_format)
|
|
||||||
.map_err(|err| eco_format!("failed to print diagnostics ({err})"))
|
|
||||||
}
|
|
||||||
|
@ -45,12 +45,12 @@ pub fn layout_lr(
|
|||||||
|
|
||||||
// Scale up fragments at both ends.
|
// Scale up fragments at both ends.
|
||||||
match inner_fragments {
|
match inner_fragments {
|
||||||
[one] => scale_if_delimiter(ctx, one, relative_to, height, None),
|
[one] => scale(ctx, one, relative_to, height, None),
|
||||||
[first, .., last] => {
|
[first, .., last] => {
|
||||||
scale_if_delimiter(ctx, first, relative_to, height, Some(MathClass::Opening));
|
scale(ctx, first, relative_to, height, Some(MathClass::Opening));
|
||||||
scale_if_delimiter(ctx, last, relative_to, height, Some(MathClass::Closing));
|
scale(ctx, last, relative_to, height, Some(MathClass::Closing));
|
||||||
}
|
}
|
||||||
[] => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle MathFragment::Glyph fragments that should be scaled up.
|
// Handle MathFragment::Glyph fragments that should be scaled up.
|
||||||
@ -58,7 +58,7 @@ pub fn layout_lr(
|
|||||||
if let MathFragment::Glyph(ref mut glyph) = fragment {
|
if let MathFragment::Glyph(ref mut glyph) = fragment {
|
||||||
if glyph.mid_stretched == Some(false) {
|
if glyph.mid_stretched == Some(false) {
|
||||||
glyph.mid_stretched = Some(true);
|
glyph.mid_stretched = Some(true);
|
||||||
scale(ctx, fragment, relative_to, height);
|
scale(ctx, fragment, relative_to, height, Some(MathClass::Large));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ pub fn layout_mid(
|
|||||||
for fragment in &mut fragments {
|
for fragment in &mut fragments {
|
||||||
if let MathFragment::Glyph(ref mut glyph) = fragment {
|
if let MathFragment::Glyph(ref mut glyph) = fragment {
|
||||||
glyph.mid_stretched = Some(false);
|
glyph.mid_stretched = Some(false);
|
||||||
glyph.class = MathClass::Relation;
|
glyph.class = MathClass::Fence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,12 +105,8 @@ pub fn layout_mid(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scales a math fragment to a height if it has the class Opening, Closing, or
|
/// Scale a math fragment to a height.
|
||||||
/// Fence.
|
fn scale(
|
||||||
///
|
|
||||||
/// In case `apply` is `Some(class)`, `class` will be applied to the fragment if
|
|
||||||
/// it is a delimiter, in a way that cannot be overridden by the user.
|
|
||||||
fn scale_if_delimiter(
|
|
||||||
ctx: &mut MathContext,
|
ctx: &mut MathContext,
|
||||||
fragment: &mut MathFragment,
|
fragment: &mut MathFragment,
|
||||||
relative_to: Abs,
|
relative_to: Abs,
|
||||||
@ -121,23 +117,20 @@ fn scale_if_delimiter(
|
|||||||
fragment.class(),
|
fragment.class(),
|
||||||
MathClass::Opening | MathClass::Closing | MathClass::Fence
|
MathClass::Opening | MathClass::Closing | MathClass::Fence
|
||||||
) {
|
) {
|
||||||
scale(ctx, fragment, relative_to, height);
|
// This unwrap doesn't really matter. If it is None, then the fragment
|
||||||
|
// won't be stretchable anyways.
|
||||||
|
let short_fall = DELIM_SHORT_FALL.at(fragment.font_size().unwrap_or_default());
|
||||||
|
stretch_fragment(
|
||||||
|
ctx,
|
||||||
|
fragment,
|
||||||
|
Some(Axis::Y),
|
||||||
|
Some(relative_to),
|
||||||
|
height,
|
||||||
|
short_fall,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(class) = apply {
|
if let Some(class) = apply {
|
||||||
fragment.set_class(class);
|
fragment.set_class(class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scales a math fragment to a height.
|
|
||||||
fn scale(
|
|
||||||
ctx: &mut MathContext,
|
|
||||||
fragment: &mut MathFragment,
|
|
||||||
relative_to: Abs,
|
|
||||||
height: Rel<Abs>,
|
|
||||||
) {
|
|
||||||
// This unwrap doesn't really matter. If it is None, then the fragment
|
|
||||||
// won't be stretchable anyways.
|
|
||||||
let short_fall = DELIM_SHORT_FALL.at(fragment.font_size().unwrap_or_default());
|
|
||||||
stretch_fragment(ctx, fragment, Some(Axis::Y), Some(relative_to), height, short_fall);
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
use std::cmp::Reverse;
|
||||||
use std::collections::{BTreeSet, HashMap};
|
use std::collections::{BTreeSet, HashMap};
|
||||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use codex::ModifierSet;
|
|
||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use typst_syntax::{is_ident, Span, Spanned};
|
use typst_syntax::{is_ident, Span, Spanned};
|
||||||
@ -54,18 +54,18 @@ enum Repr {
|
|||||||
/// A native symbol that has no named variant.
|
/// A native symbol that has no named variant.
|
||||||
Single(char),
|
Single(char),
|
||||||
/// A native symbol with multiple named variants.
|
/// A native symbol with multiple named variants.
|
||||||
Complex(&'static [(ModifierSet<&'static str>, char)]),
|
Complex(&'static [(&'static str, char)]),
|
||||||
/// A symbol with multiple named variants, where some modifiers may have
|
/// A symbol with multiple named variants, where some modifiers may have
|
||||||
/// been applied. Also used for symbols defined at runtime by the user with
|
/// been applied. Also used for symbols defined at runtime by the user with
|
||||||
/// no modifier applied.
|
/// no modifier applied.
|
||||||
Modified(Arc<(List, ModifierSet<EcoString>)>),
|
Modified(Arc<(List, EcoString)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A collection of symbols.
|
/// A collection of symbols.
|
||||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||||
enum List {
|
enum List {
|
||||||
Static(&'static [(ModifierSet<&'static str>, char)]),
|
Static(&'static [(&'static str, char)]),
|
||||||
Runtime(Box<[(ModifierSet<EcoString>, char)]>),
|
Runtime(Box<[(EcoString, char)]>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Symbol {
|
impl Symbol {
|
||||||
@ -76,26 +76,24 @@ impl Symbol {
|
|||||||
|
|
||||||
/// Create a symbol with a static variant list.
|
/// Create a symbol with a static variant list.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub const fn list(list: &'static [(ModifierSet<&'static str>, char)]) -> Self {
|
pub const fn list(list: &'static [(&'static str, char)]) -> Self {
|
||||||
debug_assert!(!list.is_empty());
|
debug_assert!(!list.is_empty());
|
||||||
Self(Repr::Complex(list))
|
Self(Repr::Complex(list))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a symbol with a runtime variant list.
|
/// Create a symbol with a runtime variant list.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn runtime(list: Box<[(ModifierSet<EcoString>, char)]>) -> Self {
|
pub fn runtime(list: Box<[(EcoString, char)]>) -> Self {
|
||||||
debug_assert!(!list.is_empty());
|
debug_assert!(!list.is_empty());
|
||||||
Self(Repr::Modified(Arc::new((List::Runtime(list), ModifierSet::default()))))
|
Self(Repr::Modified(Arc::new((List::Runtime(list), EcoString::new()))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the symbol's character.
|
/// Get the symbol's character.
|
||||||
pub fn get(&self) -> char {
|
pub fn get(&self) -> char {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Repr::Single(c) => *c,
|
Repr::Single(c) => *c,
|
||||||
Repr::Complex(_) => ModifierSet::<&'static str>::default()
|
Repr::Complex(_) => find(self.variants(), "").unwrap(),
|
||||||
.best_match_in(self.variants())
|
Repr::Modified(arc) => find(self.variants(), &arc.1).unwrap(),
|
||||||
.unwrap(),
|
|
||||||
Repr::Modified(arc) => arc.1.best_match_in(self.variants()).unwrap(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,14 +128,16 @@ impl Symbol {
|
|||||||
/// Apply a modifier to the symbol.
|
/// Apply a modifier to the symbol.
|
||||||
pub fn modified(mut self, modifier: &str) -> StrResult<Self> {
|
pub fn modified(mut self, modifier: &str) -> StrResult<Self> {
|
||||||
if let Repr::Complex(list) = self.0 {
|
if let Repr::Complex(list) = self.0 {
|
||||||
self.0 =
|
self.0 = Repr::Modified(Arc::new((List::Static(list), EcoString::new())));
|
||||||
Repr::Modified(Arc::new((List::Static(list), ModifierSet::default())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Repr::Modified(arc) = &mut self.0 {
|
if let Repr::Modified(arc) = &mut self.0 {
|
||||||
let (list, modifiers) = Arc::make_mut(arc);
|
let (list, modifiers) = Arc::make_mut(arc);
|
||||||
modifiers.insert_raw(modifier);
|
if !modifiers.is_empty() {
|
||||||
if modifiers.best_match_in(list.variants()).is_some() {
|
modifiers.push('.');
|
||||||
|
}
|
||||||
|
modifiers.push_str(modifier);
|
||||||
|
if find(list.variants(), modifiers).is_some() {
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ impl Symbol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The characters that are covered by this symbol.
|
/// The characters that are covered by this symbol.
|
||||||
pub fn variants(&self) -> impl Iterator<Item = (ModifierSet<&str>, char)> {
|
pub fn variants(&self) -> impl Iterator<Item = (&str, char)> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Repr::Single(c) => Variants::Single(Some(*c).into_iter()),
|
Repr::Single(c) => Variants::Single(Some(*c).into_iter()),
|
||||||
Repr::Complex(list) => Variants::Static(list.iter()),
|
Repr::Complex(list) => Variants::Static(list.iter()),
|
||||||
@ -156,15 +156,17 @@ impl Symbol {
|
|||||||
|
|
||||||
/// Possible modifiers.
|
/// Possible modifiers.
|
||||||
pub fn modifiers(&self) -> impl Iterator<Item = &str> + '_ {
|
pub fn modifiers(&self) -> impl Iterator<Item = &str> + '_ {
|
||||||
|
let mut set = BTreeSet::new();
|
||||||
let modifiers = match &self.0 {
|
let modifiers = match &self.0 {
|
||||||
Repr::Modified(arc) => arc.1.as_deref(),
|
Repr::Modified(arc) => arc.1.as_str(),
|
||||||
_ => ModifierSet::default(),
|
_ => "",
|
||||||
};
|
};
|
||||||
self.variants()
|
for modifier in self.variants().flat_map(|(name, _)| name.split('.')) {
|
||||||
.flat_map(|(m, _)| m)
|
if !modifier.is_empty() && !contained(modifiers, modifier) {
|
||||||
.filter(|modifier| !modifier.is_empty() && !modifiers.contains(modifier))
|
set.insert(modifier);
|
||||||
.collect::<BTreeSet<_>>()
|
}
|
||||||
.into_iter()
|
}
|
||||||
|
set.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,10 +256,7 @@ impl Symbol {
|
|||||||
seen.insert(hash, i);
|
seen.insert(hash, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
let list = variants
|
let list = variants.into_iter().map(|s| (s.v.0, s.v.1)).collect();
|
||||||
.into_iter()
|
|
||||||
.map(|s| (ModifierSet::from_raw_dotted(s.v.0), s.v.1))
|
|
||||||
.collect();
|
|
||||||
Ok(Symbol::runtime(list))
|
Ok(Symbol::runtime(list))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,23 +291,14 @@ impl crate::foundations::Repr for Symbol {
|
|||||||
match &self.0 {
|
match &self.0 {
|
||||||
Repr::Single(c) => eco_format!("symbol(\"{}\")", *c),
|
Repr::Single(c) => eco_format!("symbol(\"{}\")", *c),
|
||||||
Repr::Complex(variants) => {
|
Repr::Complex(variants) => {
|
||||||
eco_format!(
|
eco_format!("symbol{}", repr_variants(variants.iter().copied(), ""))
|
||||||
"symbol{}",
|
|
||||||
repr_variants(variants.iter().copied(), ModifierSet::default())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Repr::Modified(arc) => {
|
Repr::Modified(arc) => {
|
||||||
let (list, modifiers) = arc.as_ref();
|
let (list, modifiers) = arc.as_ref();
|
||||||
if modifiers.is_empty() {
|
if modifiers.is_empty() {
|
||||||
eco_format!(
|
eco_format!("symbol{}", repr_variants(list.variants(), ""))
|
||||||
"symbol{}",
|
|
||||||
repr_variants(list.variants(), ModifierSet::default())
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
eco_format!(
|
eco_format!("symbol{}", repr_variants(list.variants(), modifiers))
|
||||||
"symbol{}",
|
|
||||||
repr_variants(list.variants(), modifiers.as_deref())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,24 +306,24 @@ impl crate::foundations::Repr for Symbol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn repr_variants<'a>(
|
fn repr_variants<'a>(
|
||||||
variants: impl Iterator<Item = (ModifierSet<&'a str>, char)>,
|
variants: impl Iterator<Item = (&'a str, char)>,
|
||||||
applied_modifiers: ModifierSet<&str>,
|
applied_modifiers: &str,
|
||||||
) -> String {
|
) -> String {
|
||||||
crate::foundations::repr::pretty_array_like(
|
crate::foundations::repr::pretty_array_like(
|
||||||
&variants
|
&variants
|
||||||
.filter(|(modifiers, _)| {
|
.filter(|(variant, _)| {
|
||||||
// Only keep variants that can still be accessed, i.e., variants
|
// Only keep variants that can still be accessed, i.e., variants
|
||||||
// that contain all applied modifiers.
|
// that contain all applied modifiers.
|
||||||
applied_modifiers.iter().all(|am| modifiers.contains(am))
|
parts(applied_modifiers).all(|am| variant.split('.').any(|m| m == am))
|
||||||
})
|
})
|
||||||
.map(|(modifiers, c)| {
|
.map(|(variant, c)| {
|
||||||
let trimmed_modifiers =
|
let trimmed_variant = variant
|
||||||
modifiers.into_iter().filter(|&m| !applied_modifiers.contains(m));
|
.split('.')
|
||||||
if trimmed_modifiers.clone().all(|m| m.is_empty()) {
|
.filter(|&m| parts(applied_modifiers).all(|am| m != am));
|
||||||
|
if trimmed_variant.clone().all(|m| m.is_empty()) {
|
||||||
eco_format!("\"{c}\"")
|
eco_format!("\"{c}\"")
|
||||||
} else {
|
} else {
|
||||||
let trimmed_modifiers =
|
let trimmed_modifiers = trimmed_variant.collect::<Vec<_>>().join(".");
|
||||||
trimmed_modifiers.collect::<Vec<_>>().join(".");
|
|
||||||
eco_format!("(\"{}\", \"{}\")", trimmed_modifiers, c)
|
eco_format!("(\"{}\", \"{}\")", trimmed_modifiers, c)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -379,22 +369,67 @@ cast! {
|
|||||||
/// Iterator over variants.
|
/// Iterator over variants.
|
||||||
enum Variants<'a> {
|
enum Variants<'a> {
|
||||||
Single(std::option::IntoIter<char>),
|
Single(std::option::IntoIter<char>),
|
||||||
Static(std::slice::Iter<'static, (ModifierSet<&'static str>, char)>),
|
Static(std::slice::Iter<'static, (&'static str, char)>),
|
||||||
Runtime(std::slice::Iter<'a, (ModifierSet<EcoString>, char)>),
|
Runtime(std::slice::Iter<'a, (EcoString, char)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Variants<'a> {
|
impl<'a> Iterator for Variants<'a> {
|
||||||
type Item = (ModifierSet<&'a str>, char);
|
type Item = (&'a str, char);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self {
|
match self {
|
||||||
Self::Single(iter) => Some((ModifierSet::default(), iter.next()?)),
|
Self::Single(iter) => Some(("", iter.next()?)),
|
||||||
Self::Static(list) => list.next().copied(),
|
Self::Static(list) => list.next().copied(),
|
||||||
Self::Runtime(list) => list.next().map(|(m, c)| (m.as_deref(), *c)),
|
Self::Runtime(list) => list.next().map(|(s, c)| (s.as_str(), *c)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the best symbol from the list.
|
||||||
|
fn find<'a>(
|
||||||
|
variants: impl Iterator<Item = (&'a str, char)>,
|
||||||
|
modifiers: &str,
|
||||||
|
) -> Option<char> {
|
||||||
|
let mut best = None;
|
||||||
|
let mut best_score = None;
|
||||||
|
|
||||||
|
// Find the best table entry with this name.
|
||||||
|
'outer: for candidate in variants {
|
||||||
|
for modifier in parts(modifiers) {
|
||||||
|
if !contained(candidate.0, modifier) {
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut matching = 0;
|
||||||
|
let mut total = 0;
|
||||||
|
for modifier in parts(candidate.0) {
|
||||||
|
if contained(modifiers, modifier) {
|
||||||
|
matching += 1;
|
||||||
|
}
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let score = (matching, Reverse(total));
|
||||||
|
if best_score.is_none_or(|b| score > b) {
|
||||||
|
best = Some(candidate.1);
|
||||||
|
best_score = Some(score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
best
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split a modifier list into its parts.
|
||||||
|
fn parts(modifiers: &str) -> impl Iterator<Item = &str> {
|
||||||
|
modifiers.split('.').filter(|s| !s.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the modifier string contains the modifier `m`.
|
||||||
|
fn contained(modifiers: &str, m: &str) -> bool {
|
||||||
|
parts(modifiers).any(|part| part == m)
|
||||||
|
}
|
||||||
|
|
||||||
/// A single character.
|
/// A single character.
|
||||||
#[elem(Repr, PlainText)]
|
#[elem(Repr, PlainText)]
|
||||||
pub struct SymbolElem {
|
pub struct SymbolElem {
|
||||||
|
@ -51,8 +51,8 @@ use crate::World;
|
|||||||
/// You can create a new bibliography by calling this function with a path
|
/// You can create a new bibliography by calling this function with a path
|
||||||
/// to a bibliography file in either one of two formats:
|
/// to a bibliography file in either one of two formats:
|
||||||
///
|
///
|
||||||
/// - A Hayagriva `.yaml`/`.yml` file. Hayagriva is a new bibliography
|
/// - A Hayagriva `.yml` file. Hayagriva is a new bibliography file format
|
||||||
/// file format designed for use with Typst. Visit its
|
/// designed for use with Typst. Visit its
|
||||||
/// [documentation](https://github.com/typst/hayagriva/blob/main/docs/file-format.md)
|
/// [documentation](https://github.com/typst/hayagriva/blob/main/docs/file-format.md)
|
||||||
/// for more details.
|
/// for more details.
|
||||||
/// - A BibLaTeX `.bib` file.
|
/// - A BibLaTeX `.bib` file.
|
||||||
|
@ -196,8 +196,6 @@ bitflags::bitflags! {
|
|||||||
const SERIF = 1 << 1;
|
const SERIF = 1 << 1;
|
||||||
/// Font face has a MATH table
|
/// Font face has a MATH table
|
||||||
const MATH = 1 << 2;
|
const MATH = 1 << 2;
|
||||||
/// Font face has an fvar table
|
|
||||||
const VARIABLE = 1 << 3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,7 +275,6 @@ impl FontInfo {
|
|||||||
let mut flags = FontFlags::empty();
|
let mut flags = FontFlags::empty();
|
||||||
flags.set(FontFlags::MONOSPACE, ttf.is_monospaced());
|
flags.set(FontFlags::MONOSPACE, ttf.is_monospaced());
|
||||||
flags.set(FontFlags::MATH, ttf.tables().math.is_some());
|
flags.set(FontFlags::MATH, ttf.tables().math.is_some());
|
||||||
flags.set(FontFlags::VARIABLE, ttf.is_variable());
|
|
||||||
|
|
||||||
// Determine whether this is a serif or sans-serif font.
|
// Determine whether this is a serif or sans-serif font.
|
||||||
if let Some(panose) = ttf
|
if let Some(panose) = ttf
|
||||||
|
@ -1412,24 +1412,12 @@ pub fn is_default_ignorable(c: char) -> bool {
|
|||||||
fn check_font_list(engine: &mut Engine, list: &Spanned<FontList>) {
|
fn check_font_list(engine: &mut Engine, list: &Spanned<FontList>) {
|
||||||
let book = engine.world.book();
|
let book = engine.world.book();
|
||||||
for family in &list.v {
|
for family in &list.v {
|
||||||
match book.select_family(family.as_str()).next() {
|
if !book.contains_family(family.as_str()) {
|
||||||
Some(index) => {
|
|
||||||
if book
|
|
||||||
.info(index)
|
|
||||||
.is_some_and(|x| x.flags.contains(FontFlags::VARIABLE))
|
|
||||||
{
|
|
||||||
engine.sink.warn(warning!(
|
engine.sink.warn(warning!(
|
||||||
list.span,
|
|
||||||
"variable fonts are not currently supported and may render incorrectly";
|
|
||||||
hint: "try installing a static version of \"{}\" instead", family.as_str()
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => engine.sink.warn(warning!(
|
|
||||||
list.span,
|
list.span,
|
||||||
"unknown font family: {}",
|
"unknown font family: {}",
|
||||||
family.as_str(),
|
family.as_str(),
|
||||||
)),
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,7 @@ use ecow::EcoString;
|
|||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{elem, Content, Packed, SequenceElem, Show, StyleChain};
|
||||||
elem, Content, NativeElement, Packed, SequenceElem, Show, StyleChain, TargetElem,
|
|
||||||
};
|
|
||||||
use crate::html::{tag, HtmlElem};
|
|
||||||
use crate::layout::{Em, Length};
|
use crate::layout::{Em, Length};
|
||||||
use crate::text::{variant, SpaceElem, TextElem, TextSize};
|
use crate::text::{variant, SpaceElem, TextElem, TextSize};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
@ -55,13 +52,6 @@ impl Show for Packed<SubElem> {
|
|||||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let body = self.body.clone();
|
let body = self.body.clone();
|
||||||
|
|
||||||
if TargetElem::target_in(styles).is_html() {
|
|
||||||
return Ok(HtmlElem::new(tag::sub)
|
|
||||||
.with_body(Some(body))
|
|
||||||
.pack()
|
|
||||||
.spanned(self.span()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.typographic(styles) {
|
if self.typographic(styles) {
|
||||||
if let Some(text) = convert_script(&body, true) {
|
if let Some(text) = convert_script(&body, true) {
|
||||||
if is_shapable(engine, &text, styles) {
|
if is_shapable(engine, &text, styles) {
|
||||||
@ -121,13 +111,6 @@ impl Show for Packed<SuperElem> {
|
|||||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
let body = self.body.clone();
|
let body = self.body.clone();
|
||||||
|
|
||||||
if TargetElem::target_in(styles).is_html() {
|
|
||||||
return Ok(HtmlElem::new(tag::sup)
|
|
||||||
.with_body(Some(body))
|
|
||||||
.pack()
|
|
||||||
.spanned(self.span()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.typographic(styles) {
|
if self.typographic(styles) {
|
||||||
if let Some(text) = convert_script(&body, false) {
|
if let Some(text) = convert_script(&body, false) {
|
||||||
if is_shapable(engine, &text, styles) {
|
if is_shapable(engine, &text, styles) {
|
||||||
|
@ -22,7 +22,6 @@ typst-utils = { workspace = true }
|
|||||||
typst-assets = { workspace = true, features = ["fonts"] }
|
typst-assets = { workspace = true, features = ["fonts"] }
|
||||||
typst-dev-assets = { workspace = true }
|
typst-dev-assets = { workspace = true }
|
||||||
clap = { workspace = true, optional = true }
|
clap = { workspace = true, optional = true }
|
||||||
codex = { workspace = true }
|
|
||||||
ecow = { workspace = true }
|
ecow = { workspace = true }
|
||||||
heck = { workspace = true }
|
heck = { workspace = true }
|
||||||
pulldown-cmark = { workspace = true }
|
pulldown-cmark = { workspace = true }
|
||||||
|
@ -712,11 +712,11 @@ fn symbols_model(resolver: &dyn Resolver, group: &GroupData) -> SymbolsModel {
|
|||||||
let mut list = vec![];
|
let mut list = vec![];
|
||||||
for (name, binding) in group.module().scope().iter() {
|
for (name, binding) in group.module().scope().iter() {
|
||||||
let Value::Symbol(symbol) = binding.read() else { continue };
|
let Value::Symbol(symbol) = binding.read() else { continue };
|
||||||
let complete = |variant: codex::ModifierSet<&str>| {
|
let complete = |variant: &str| {
|
||||||
if variant.is_empty() {
|
if variant.is_empty() {
|
||||||
name.clone()
|
name.clone()
|
||||||
} else {
|
} else {
|
||||||
eco_format!("{}.{}", name, variant.as_str())
|
eco_format!("{}.{}", name, variant)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>1<sup>st</sup>, 2<sup>nd</sup>, 3<sup>rd</sup>.</p>
|
|
||||||
<p>log<sub>2</sub>, log<sub>3</sub>, log<sub>variable</sub>.</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 556 B |
Before Width: | Height: | Size: 920 B After Width: | Height: | Size: 900 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -10,7 +10,7 @@ use typst::layout::{Abs, Frame, FrameItem, PagedDocument, Transform};
|
|||||||
use typst::visualize::Color;
|
use typst::visualize::Color;
|
||||||
use typst::{Document, WorldExt};
|
use typst::{Document, WorldExt};
|
||||||
use typst_pdf::PdfOptions;
|
use typst_pdf::PdfOptions;
|
||||||
use typst_syntax::{FileId, Lines};
|
use typst_syntax::FileId;
|
||||||
|
|
||||||
use crate::collect::{Attr, FileSize, NoteKind, Test};
|
use crate::collect::{Attr, FileSize, NoteKind, Test};
|
||||||
use crate::logger::TestResult;
|
use crate::logger::TestResult;
|
||||||
@ -292,7 +292,7 @@ impl<'a> Runner<'a> {
|
|||||||
return "(empty)".into();
|
return "(empty)".into();
|
||||||
}
|
}
|
||||||
|
|
||||||
let lines = self.lookup(file);
|
let lines = self.world.lookup(file);
|
||||||
lines.text()[range.clone()].replace('\n', "\\n").replace('\r', "\\r")
|
lines.text()[range.clone()].replace('\n', "\\n").replace('\r', "\\r")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +318,7 @@ impl<'a> Runner<'a> {
|
|||||||
|
|
||||||
/// Display a position as a line:column pair.
|
/// Display a position as a line:column pair.
|
||||||
fn format_pos(&self, file: FileId, pos: usize) -> String {
|
fn format_pos(&self, file: FileId, pos: usize) -> String {
|
||||||
let lines = self.lookup(file);
|
let lines = self.world.lookup(file);
|
||||||
|
|
||||||
let res = lines.byte_to_line_column(pos).map(|(line, col)| (line + 1, col + 1));
|
let res = lines.byte_to_line_column(pos).map(|(line, col)| (line + 1, col + 1));
|
||||||
let Some((line, col)) = res else {
|
let Some((line, col)) = res else {
|
||||||
@ -331,15 +331,6 @@ impl<'a> Runner<'a> {
|
|||||||
format!("{line}:{col}")
|
format!("{line}:{col}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn lookup(&self, file: FileId) -> Lines<String> {
|
|
||||||
if self.test.source.id() == file {
|
|
||||||
self.test.source.lines().clone()
|
|
||||||
} else {
|
|
||||||
self.world.lookup(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An output type we can test.
|
/// An output type we can test.
|
||||||
|
@ -88,7 +88,7 @@ impl TestWorld {
|
|||||||
|
|
||||||
/// Lookup line metadata for a file by id.
|
/// Lookup line metadata for a file by id.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn lookup(&self, id: FileId) -> Lines<String> {
|
pub fn lookup(&self, id: FileId) -> Lines<String> {
|
||||||
self.slot(id, |slot| {
|
self.slot(id, |slot| {
|
||||||
if let Some(source) = slot.source.get() {
|
if let Some(source) = slot.source.get() {
|
||||||
let source = source.as_ref().expect("file is not valid");
|
let source = source.as_ref().expect("file is not valid");
|
||||||
|
@ -77,14 +77,6 @@ $ lr(body) quad
|
|||||||
lr(size: #1em, body) quad
|
lr(size: #1em, body) quad
|
||||||
lr(size: #(1em+20%), body) $
|
lr(size: #(1em+20%), body) $
|
||||||
|
|
||||||
--- math-lr-mid-class ---
|
|
||||||
// Test that `mid` creates a Relation, but that can be overridden.
|
|
||||||
$ (a | b) $
|
|
||||||
$ (a mid(|) b) $
|
|
||||||
$ (a class("unary", |) b) $
|
|
||||||
$ (a class("unary", mid(|)) b) $
|
|
||||||
$ (a mid(class("unary", |)) b) $
|
|
||||||
|
|
||||||
--- math-lr-unbalanced ---
|
--- math-lr-unbalanced ---
|
||||||
// Test unbalanced delimiters.
|
// Test unbalanced delimiters.
|
||||||
$ 1/(2 (x) $
|
$ 1/(2 (x) $
|
||||||
|
@ -104,11 +104,9 @@
|
|||||||
("long", "⟹"),
|
("long", "⟹"),
|
||||||
("long.bar", "⟾"),
|
("long.bar", "⟾"),
|
||||||
("not", "⇏"),
|
("not", "⇏"),
|
||||||
("struck", "⤃"),
|
|
||||||
("l", "⇔"),
|
("l", "⇔"),
|
||||||
("l.long", "⟺"),
|
("l.long", "⟺"),
|
||||||
("l.not", "⇎"),
|
("l.not", "⇎"),
|
||||||
("l.struck", "⤄"),
|
|
||||||
)
|
)
|
||||||
```.text,
|
```.text,
|
||||||
)
|
)
|
||||||
|
@ -17,8 +17,3 @@ n#super[1], n#sub[2], ... n#super[N]
|
|||||||
#underline[The claim#super[\[4\]]] has been disputed. \
|
#underline[The claim#super[\[4\]]] has been disputed. \
|
||||||
The claim#super[#underline[\[4\]]] has been disputed. \
|
The claim#super[#underline[\[4\]]] has been disputed. \
|
||||||
It really has been#super(box(text(baseline: 0pt, underline[\[4\]]))) \
|
It really has been#super(box(text(baseline: 0pt, underline[\[4\]]))) \
|
||||||
|
|
||||||
--- basic-sup-sub html ---
|
|
||||||
1#super[st], 2#super[nd], 3#super[rd].
|
|
||||||
|
|
||||||
log#sub[2], log#sub[3], log#sub[variable].
|
|
||||||
|