feat: adjust builtin types for typst v0.13.0 (#1416)

* feat: adjust builtin types for typst v0.13.0

* todo

* test: update snapshot

* test: update snapshot
This commit is contained in:
Myriad-Dreamin 2025-02-27 13:28:35 +08:00 committed by GitHub
parent 7faef58186
commit a191b7c852
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 112 additions and 20 deletions

View file

@ -610,7 +610,9 @@ impl CompletionPair<'_, '_, '_> {
if let Some(ty) = ty {
let filter = |ty: &Ty| match surrounding_syntax {
SurroundingSyntax::StringContent => match ty {
Ty::Builtin(BuiltinTy::Path(..) | BuiltinTy::TextFont) => true,
Ty::Builtin(
BuiltinTy::Path(..) | BuiltinTy::TextFont | BuiltinTy::TextFeature,
) => true,
Ty::Value(val) => matches!(val.val, Value::Str(..)),
Ty::Builtin(BuiltinTy::Type(ty)) => {
*ty == Type::of::<typst::foundations::Str>()

View file

@ -164,6 +164,7 @@ impl FnCompletionFeat {
| BuiltinTy::Color
| BuiltinTy::TextSize
| BuiltinTy::TextFont
| BuiltinTy::TextFeature
| BuiltinTy::TextLang
| BuiltinTy::TextRegion
| BuiltinTy::Label

View file

@ -237,6 +237,9 @@ impl TypeCompletionWorker<'_, '_, '_, '_> {
BuiltinTy::TextFont => {
self.base.font_completions();
}
BuiltinTy::TextFeature => {
self.base.font_feature_completions();
}
BuiltinTy::Margin => {
self.snippet_completion("()", "(${})", "Margin dictionary.");
self.type_completion(&Ty::Builtin(BuiltinTy::Length), docs);

View file

@ -19,6 +19,11 @@ impl CompletionPair<'_, '_, '_> {
}
}
/// Add completions for current font features.
pub fn font_feature_completions(&mut self) {
// todo: add me
}
/// Add completions for all available packages.
pub fn package_completions(&mut self, all_versions: bool) {
let w = self.worker.world().clone();

View file

@ -253,6 +253,8 @@ impl TypeChecker<'_> {
LazyLock::new(|| Ty::Dict(FLOW_OUTSET_DICT.clone()));
static FLOW_RADIUS_DICT_TYPE: LazyLock<Ty> =
LazyLock::new(|| Ty::Dict(FLOW_RADIUS_DICT.clone()));
static FLOW_TEXT_FONT_DICT_TYPE: LazyLock<Ty> =
LazyLock::new(|| Ty::Dict(FLOW_TEXT_FONT_DICT.clone()));
fn is_ty(ty: &Ty) -> bool {
match ty {
@ -362,6 +364,16 @@ impl TypeChecker<'_> {
self.constrain(&FLOW_RADIUS_DICT_TYPE, rhs);
}
}
(lhs, Ty::Builtin(BuiltinTy::TextFont)) => {
if lhs.is_dict() {
self.constrain(lhs, &FLOW_TEXT_FONT_DICT_TYPE);
}
}
(Ty::Builtin(BuiltinTy::TextFont), rhs) => {
if rhs.is_dict() {
self.constrain(&FLOW_TEXT_FONT_DICT_TYPE, rhs);
}
}
(Ty::Dict(lhs), Ty::Dict(rhs)) => {
for (key, lhs, rhs) in lhs.common_iface_fields(rhs) {
crate::log_debug_ct!("constrain record item {key} {lhs:?} ⪯ {rhs:?}");

View file

@ -3,6 +3,5 @@ source: crates/tinymist-query/src/analysis.rs
description: "Check on \"\\\"Test\\\"\" (34)"
expression: post_ty
input_file: crates/tinymist-query/src/fixtures/post_type_check/text_font_element.typ
snapshot_kind: text
---
( ⪰ TextFont | (TextFont | Array<TextFont>))
( ⪰ TextFont | "covers": Any | "name": Any | (TextFont | Array<TextFont>))

View file

@ -3,6 +3,5 @@ source: crates/tinymist-query/src/analysis.rs
description: "Check on \"\\\"Test\\\"\" (31)"
expression: post_ty
input_file: crates/tinymist-query/src/fixtures/post_type_check/text_font_element2.typ
snapshot_kind: text
---
( ⪰ ( ⪰ "Test" ⪯ (TextFont | Array<TextFont>)) ⪯ TextFont)
( ⪰ ( ⪰ "Test" ⪯ (TextFont | Array<TextFont>)) ⪯ TextFont & "covers": Any & "name": Any)

View file

@ -1,8 +1,7 @@
---
source: crates/tinymist-query/src/analysis.rs
description: "Check on \"(\" (18)"
expression: literal_type
expression: post_ty
input_file: crates/tinymist-query/src/fixtures/post_type_check/with_element.typ
snapshot_kind: text
---
( ⪰ "alternates": Type(bool) | "baseline": Type(length) | "body": Type(content) | "bottom-edge": (Type(length) | "baseline" | "bounds" | "descender") | "cjk-latin-spacing": (Type(auto) | Type(none)) | "costs": Type(dictionary) | "dir": Dir | "discretionary-ligatures": Type(bool) | "fallback": Type(bool) | "features": (Type(array) | Type(dictionary)) | "fill": Color | "font": (TextFont | Array<TextFont>) | "fractions": Type(bool) | "historical-ligatures": Type(bool) | "hyphenate": (Type(auto) | Type(bool)) | "kerning": Type(bool) | "lang": TextLang | "ligatures": Type(bool) | "number-type": (Type(auto) | "lining" | "old-style") | "number-width": (Type(auto) | "proportional" | "tabular") | "overhang": Type(bool) | "region": TextRegion | "script": (Type(auto) | Type(str)) | "size": TextSize | "slashed-zero": Type(bool) | "spacing": Type(relative) | "stretch": Type(ratio) | "stroke": Stroke | "style": ("italic" | "normal" | "oblique") | "stylistic-set": (Type(array) | Type(int) | Type(none)) | "top-edge": (Type(length) | "ascender" | "baseline" | "bounds" | "cap-height" | "x-height") | "tracking": Type(length) | "weight": (Type(int) | "black" | "bold" | "extrabold" | "extralight" | "light" | "medium" | "regular" | "semibold" | "thin"))
( ⪰ "alternates": Type(bool) | "baseline": Type(length) | "body": Type(content) | "bottom-edge": (Type(length) | "baseline" | "bounds" | "descender") | "cjk-latin-spacing": (Type(auto) | Type(none)) | "costs": {"hyphenation": Type(ratio), "orphan": Type(ratio), "runt": Type(ratio), "widow": Type(ratio)} | "dir": Dir | "discretionary-ligatures": Type(bool) | "fallback": Type(bool) | "features": (Type(array) | Type(dictionary)) | "fill": Color | "font": (TextFont | Array<TextFont>) | "fractions": Type(bool) | "historical-ligatures": Type(bool) | "hyphenate": (Type(auto) | Type(bool)) | "kerning": Type(bool) | "lang": TextLang | "ligatures": Type(bool) | "number-type": (Type(auto) | "lining" | "old-style") | "number-width": (Type(auto) | "proportional" | "tabular") | "overhang": Type(bool) | "region": TextRegion | "script": (Type(auto) | Type(str)) | "size": TextSize | "slashed-zero": Type(bool) | "spacing": Type(relative) | "stretch": Type(ratio) | "stroke": Stroke | "style": ("italic" | "normal" | "oblique") | "stylistic-set": (Type(array) | Type(int) | Type(none)) | "top-edge": (Type(length) | "ascender" | "baseline" | "bounds" | "cap-height" | "x-height") | "tracking": Type(length) | "weight": (Type(int) | "black" | "bold" | "extrabold" | "extralight" | "light" | "medium" | "regular" | "semibold" | "thin"))

View file

@ -5,7 +5,8 @@ use ecow::{eco_format, EcoString};
use once_cell::sync::Lazy;
use regex::RegexSet;
use strum::{EnumIter, IntoEnumIterator};
use typst::foundations::CastInfo;
use typst::foundations::{CastInfo, Regex};
use typst::layout::Ratio;
use typst::syntax::FileId;
use typst::{
foundations::{AutoValue, Content, Func, NoneValue, ParamInfo, Type, Value},
@ -221,6 +222,7 @@ pub enum BuiltinTy {
Color,
TextSize,
TextFont,
TextFeature,
TextLang,
TextRegion,
@ -263,6 +265,7 @@ impl fmt::Debug for BuiltinTy {
BuiltinTy::Color => write!(f, "Color"),
BuiltinTy::TextSize => write!(f, "TextSize"),
BuiltinTy::TextFont => write!(f, "TextFont"),
BuiltinTy::TextFeature => write!(f, "TextFeature"),
BuiltinTy::TextLang => write!(f, "TextLang"),
BuiltinTy::TextRegion => write!(f, "TextRegion"),
BuiltinTy::Dir => write!(f, "Dir"),
@ -345,6 +348,7 @@ impl BuiltinTy {
BuiltinTy::Color => "color",
BuiltinTy::TextSize => "text.size",
BuiltinTy::TextFont => "text.font",
BuiltinTy::TextFeature => "text.feature",
BuiltinTy::TextLang => "text.lang",
BuiltinTy::TextRegion => "text.region",
BuiltinTy::Dir => "dir",
@ -456,6 +460,7 @@ macro_rules! flow_record {
pub(super) fn param_mapping(func: &Func, param: &ParamInfo) -> Option<Ty> {
// todo: remove path params which is compatible with 0.12.0
match (func.name()?, param.name) {
// todo: pdf.embed
("embed", "path") => Some(literally(Path(PathPreference::None))),
("cbor", "path" | "source") => Some(literally(Path(PathPreference::None))),
("plugin", "source") => Some(literally(Path(PathPreference::Wasm))),
@ -474,10 +479,24 @@ pub(super) fn param_mapping(func: &Func, param: &ParamInfo) -> Option<Ty> {
])),
("cite", "key") => Some(Ty::iter_union([literally(CiteLabel)])),
("ref", "target") => Some(Ty::iter_union([literally(RefLabel)])),
("link", "dest") | ("footnote", "body") => Some(Ty::iter_union([
("footnote", "body") => Some(Ty::iter_union([
literally(RefLabel),
Ty::from_cast_info(&param.input),
])),
("link", "dest") => {
static LINK_DEST_TYPE: Lazy<Ty> = Lazy::new(|| {
flow_union!(
literally(RefLabel),
Ty::Builtin(BuiltinTy::Type(Type::of::<foundations::Str>())),
Ty::Builtin(BuiltinTy::Type(Type::of::<typst::introspection::Location>())),
Ty::Dict(RecordTy::new(vec![
("x".into(), literally(Length)),
("y".into(), literally(Length)),
])),
)
});
Some(LINK_DEST_TYPE.clone())
}
("bibliography", "path" | "sources") => {
static BIB_PATH_TYPE: Lazy<Ty> = Lazy::new(|| {
let bib_path_ty = literally(Path(PathPreference::Bibliography));
@ -487,18 +506,52 @@ pub(super) fn param_mapping(func: &Func, param: &ParamInfo) -> Option<Ty> {
}
("text", "size") => Some(literally(TextSize)),
("text", "font") => {
// todo: the dict can be completed, but we have bugs...
static FONT_TYPE: Lazy<Ty> = Lazy::new(|| {
Ty::iter_union([literally(TextFont), Ty::Array(literally(TextFont).into())])
});
Some(FONT_TYPE.clone())
}
("text", "feature") => {
static FONT_TYPE: Lazy<Ty> = Lazy::new(|| {
Ty::iter_union([
// todo: the key can only be the text feature
Ty::Builtin(BuiltinTy::Type(Type::of::<foundations::Dict>())),
Ty::Array(literally(TextFeature).into()),
])
});
Some(FONT_TYPE.clone())
}
("text", "costs") => {
static FONT_TYPE: Lazy<Ty> = Lazy::new(|| {
Ty::Dict(flow_record!(
"hyphenation" => literally(BuiltinTy::Type(Type::of::<Ratio>())),
"runt" => literally(BuiltinTy::Type(Type::of::<Ratio>())),
"widow" => literally(BuiltinTy::Type(Type::of::<Ratio>())),
"orphan" => literally(BuiltinTy::Type(Type::of::<Ratio>())),
))
});
Some(FONT_TYPE.clone())
}
("text", "lang") => Some(literally(TextLang)),
("text", "region") => Some(literally(TextRegion)),
("text" | "stack", "dir") => Some(literally(Dir)),
("par", "first-line-indent") => {
static FIRST_LINE_INDENT: Lazy<Ty> = Lazy::new(|| {
Ty::iter_union([
literally(Length),
Ty::Dict(RecordTy::new(vec![
("amount".into(), literally(Length)),
("all".into(), Ty::Boolean(Option::None)),
])),
])
});
Some(FIRST_LINE_INDENT.clone())
}
(
// todo: polygon.regular
"page" | "highlight" | "text" | "path" | "rect" | "ellipse" | "circle" | "polygon"
| "box" | "block" | "table" | "regular",
"page" | "highlight" | "text" | "path" | "curve" | "rect" | "ellipse" | "circle"
| "polygon" | "box" | "block" | "table" | "regular",
"fill",
) => Some(literally(Color)),
(
@ -509,7 +562,7 @@ pub(super) fn param_mapping(func: &Func, param: &ParamInfo) -> Option<Ty> {
("block" | "box" | "circle" | "ellipse" | "rect" | "square", "outset") => {
Some(literally(Outset))
}
("block" | "box" | "rect" | "square", "radius") => Some(literally(Radius)),
("block" | "box" | "rect" | "square" | "highlight", "radius") => Some(literally(Radius)),
("grid" | "table", "columns" | "rows" | "gutter" | "column-gutter" | "row-gutter") => {
static COLUMN_TYPE: Lazy<Ty> = Lazy::new(|| {
flow_union!(
@ -521,7 +574,7 @@ pub(super) fn param_mapping(func: &Func, param: &ParamInfo) -> Option<Ty> {
});
Some(COLUMN_TYPE.clone())
}
("pattern", "size") => {
("pattern" | "tiling", "size") => {
static PATTERN_SIZE_TYPE: Lazy<Ty> = Lazy::new(|| {
flow_union!(
Ty::Value(InsTy::new(Value::Auto)),
@ -533,9 +586,9 @@ pub(super) fn param_mapping(func: &Func, param: &ParamInfo) -> Option<Ty> {
("stroke", "dash") => Some(FLOW_STROKE_DASH_TYPE.clone()),
(
//todo: table.cell, table.hline, table.vline, math.cancel, grid.cell, polygon.regular
"cancel" | "highlight" | "overline" | "strike" | "underline" | "text" | "path" | "rect"
| "ellipse" | "circle" | "polygon" | "box" | "block" | "table" | "line" | "cell"
| "hline" | "vline" | "regular",
"cancel" | "highlight" | "overline" | "strike" | "underline" | "text" | "path"
| "curve" | "rect" | "ellipse" | "circle" | "polygon" | "box" | "block" | "table"
| "line" | "cell" | "hline" | "vline" | "regular",
"stroke",
) => Some(Ty::Builtin(Stroke)),
("page", "margin") => Some(Ty::Builtin(Margin)),
@ -626,6 +679,13 @@ pub static FLOW_RADIUS_DICT: Lazy<Interned<RecordTy>> = Lazy::new(|| {
)
});
pub static FLOW_TEXT_FONT_DICT: Lazy<Interned<RecordTy>> = Lazy::new(|| {
flow_record!(
"name" => literally(TextFont),
"covers" => flow_union!("latin-in-cjk", BuiltinTy::Type(Type::of::<Regex>())),
)
});
// todo bad case: array.fold
// todo bad case: datetime
// todo bad case: selector
@ -634,9 +694,13 @@ pub static FLOW_RADIUS_DICT: Lazy<Interned<RecordTy>> = Lazy::new(|| {
// todo: numbering/supplement
// todo: grid/table.fill/align/stroke/inset can be a function
// todo: math.cancel.angle can be a function
// todo: text.features array/dictionary
// todo: math.mat.augment
// todo: csv.row-type can be an array or a dictionary
// todo: text.stylistic-set is an array of integer
// todo: raw.lang can be completed
// todo: smartquote.quotes can be an array or a dictionary
// todo: mat.augment can be a dictionary
// todo: pdf.embed mime-type can be special
// ISO 639

View file

@ -134,6 +134,10 @@ impl IfaceCheckDriver<'_> {
self.checker
.check(Iface::Dict(&FLOW_RADIUS_DICT), &mut self.ctx, pol);
}
Ty::Builtin(BuiltinTy::TextFont) if self.dict_as_iface() => {
self.checker
.check(Iface::Dict(&FLOW_TEXT_FONT_DICT), &mut self.ctx, pol);
}
// // todo: deduplicate checking early
Ty::Value(ins_ty) => {
if self.value_as_iface() {

View file

@ -186,6 +186,10 @@ impl SigCheckDriver<'_> {
self.checker
.check(Sig::DictCons(&FLOW_RADIUS_DICT), &mut self.ctx, pol);
}
Ty::Builtin(BuiltinTy::TextFont) if self.dict_as_sig() => {
self.checker
.check(Sig::DictCons(&FLOW_TEXT_FONT_DICT), &mut self.ctx, pol);
}
// todo: deduplicate checking early
Ty::Value(ins_ty) => {
if self.func_as_sig() {