feat: improve signature formatting in docs (#750)

* feat: improve signature formatting in docs

* test: update snapshot

* fix: package docs formatting
This commit is contained in:
Myriad-Dreamin 2024-10-30 15:17:08 +08:00 committed by GitHub
parent b97907cde6
commit ac97c34d0f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 129 additions and 60 deletions

View file

@ -218,7 +218,7 @@ impl<'a> TypeChecker<'a> {
name: name.clone(), name: name.clone(),
docs: param_doc.docs.clone(), docs: param_doc.docs.clone(),
cano_type: (), cano_type: (),
default: Some(eco_format!("{exp}")), default: Some(exp.repr()),
attrs: ParamAttrs::named(), attrs: ParamAttrs::named(),
}, },
); );

View file

@ -439,6 +439,6 @@ mod tests {
#[test] #[test]
fn test_pkgs() { fn test_pkgs() {
snapshot_testing("completion-pkgs", &run(TestConfig { pkg_mode: true })); snapshot_testing("pkgs", &run(TestConfig { pkg_mode: true }));
} }
} }

View file

@ -221,7 +221,9 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
if let Some(SymbolDocs::Function(sig)) = &sym.head.parsed_docs { if let Some(SymbolDocs::Function(sig)) = &sym.head.parsed_docs {
let _ = writeln!(md, "<!-- begin:sig -->"); let _ = writeln!(md, "<!-- begin:sig -->");
let _ = writeln!(md, "```typc"); let _ = writeln!(md, "```typc");
let _ = writeln!(md, "let {name}({sig});", name = sym.head.name); let _ = write!(md, "let {}", sym.head.name);
let _ = sig.print(&mut md);
let _ = writeln!(md, ";");
let _ = writeln!(md, "```"); let _ = writeln!(md, "```");
let _ = writeln!(md, "<!-- end:sig -->"); let _ = writeln!(md, "<!-- end:sig -->");
} }

View file

@ -207,17 +207,19 @@ pub type UntypedSignatureDocs = SignatureDocsT<()>;
/// Documentation about a signature. /// Documentation about a signature.
pub type SignatureDocs = SignatureDocsT<TypeRepr>; pub type SignatureDocs = SignatureDocsT<TypeRepr>;
impl fmt::Display for SignatureDocs { impl SignatureDocs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// Get the markdown representation of the documentation.
pub fn print(&self, f: &mut impl std::fmt::Write) -> fmt::Result {
let mut is_first = true; let mut is_first = true;
let mut write_sep = |f: &mut fmt::Formatter<'_>| { let mut write_sep = |f: &mut dyn std::fmt::Write| {
if is_first { if is_first {
is_first = false; is_first = false;
return Ok(()); return f.write_str("\n ");
} }
f.write_str(", ") f.write_str(",\n ")
}; };
f.write_char('(')?;
for p in &self.pos { for p in &self.pos {
write_sep(f)?; write_sep(f)?;
f.write_str(&p.name)?; f.write_str(&p.name)?;
@ -246,7 +248,7 @@ impl fmt::Display for SignatureDocs {
let v = v.as_deref().unwrap_or("any"); let v = v.as_deref().unwrap_or("any");
let mut v = v.trim(); let mut v = v.trim();
if v.starts_with('{') && v.ends_with('}') && v.len() > 30 { if v.starts_with('{') && v.ends_with('}') && v.len() > 30 {
v = "{ ... }" v = "{ .. }"
} }
if v.starts_with('`') && v.ends_with('`') && v.len() > 30 { if v.starts_with('`') && v.ends_with('`') && v.len() > 30 {
v = "raw" v = "raw"
@ -258,9 +260,17 @@ impl fmt::Display for SignatureDocs {
if let Some(t) = t { if let Some(t) = t {
write!(f, ": {t}")?; write!(f, ": {t}")?;
} }
write!(f, " = {v}")?; if v.contains('\n') {
write!(f, " = {}", v.replace("\n", "\n "))?;
} else {
write!(f, " = {v}")?;
}
} }
} }
if !is_first {
f.write_str(",\n")?;
}
f.write_char(')')?;
Ok(()) Ok(())
} }
@ -303,7 +313,7 @@ fn format_ty(ty: Option<&Ty>, doc_ty: Option<&mut ShowTypeRepr>) -> TypeRepr {
match doc_ty { match doc_ty {
Some(doc_ty) => doc_ty(ty), Some(doc_ty) => doc_ty(ty),
None => ty None => ty
.and_then(|ty| ty.describe()) .and_then(|ty| ty.repr())
.map(|short| (short, format!("{ty:?}"))), .map(|short| (short, format!("{ty:?}"))),
} }
} }

View file

@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/annotate_fn.typ input_file: crates/tinymist-query/src/fixtures/hover/annotate_fn.typ
--- ---
{ {
"contents": "```typc\nlet touying-fn-wrapper(fn: (..: []) => any | function, ..args: arguments, max-repetitions: int | none = none, repetitions: int | none = none) = none;\n```\n\n---\n## Parameters\n\n@positional `fn` — The `fn`.\n\n@named `max-repetitions` — The `max-repetitions`.\n\n@named `repetitions` — The `repetitions`.\n\n@rest `args` — The `args`.", "contents": "```typc\nlet touying-fn-wrapper(\n fn: (..: []) => any | function,\n ..args: arguments,\n max-repetitions: int | none = none,\n repetitions: int | none = none,\n) = none;\n```\n\n---\n## Parameters\n\n@positional `fn` — The `fn`.\n\n@named `max-repetitions` — The `max-repetitions`.\n\n@named `repetitions` — The `repetitions`.\n\n@rest `args` — The `args`.",
"range": "8:20:8:38" "range": "8:20:8:38"
} }

File diff suppressed because one or more lines are too long

View file

@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/pagebreak.typ input_file: crates/tinymist-query/src/fixtures/hover/pagebreak.typ
--- ---
{ {
"contents": "```typc\nlet pagebreak(to: \"even\" | \"odd\" | none = none, weak: bool = false);\n```\n\n---\nA manual page break.\n\nMust not be used inside any containers.\n\n# Example\n```typ\nThe next page contains\nmore details on compound theory.\n#pagebreak()\n\n== Compound Theory\nIn 1984, the first ...\n```\n\n## Parameters\n\n@named `to` — If given, ensures that the next page will be an even/odd page, with an\nempty page in between if necessary.\n\n```typ\n#set page(height: 30pt)\n\nFirst.\n#pagebreak(to: \"odd\")\nThird.\n```\n\n@named `weak` — If `true`, the page break is skipped if the current page is already\nempty.\n\n---\n[Open docs](https://typst.app/docs/reference/layout/pagebreak/)", "contents": "```typc\nlet pagebreak(\n to: none | str = none,\n weak: bool = false,\n);\n```\n\n---\nA manual page break.\n\nMust not be used inside any containers.\n\n# Example\n```typ\nThe next page contains\nmore details on compound theory.\n#pagebreak()\n\n== Compound Theory\nIn 1984, the first ...\n```\n\n## Parameters\n\n@named `to` — If given, ensures that the next page will be an even/odd page, with an\nempty page in between if necessary.\n\n```typ\n#set page(height: 30pt)\n\nFirst.\n#pagebreak(to: \"odd\")\nThird.\n```\n\n@named `weak` — If `true`, the page break is skipped if the current page is already\nempty.\n\n---\n[Open docs](https://typst.app/docs/reference/layout/pagebreak/)",
"range": "0:20:0:29" "range": "0:20:0:29"
} }

View file

@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/user.typ input_file: crates/tinymist-query/src/fixtures/hover/user.typ
--- ---
{ {
"contents": "```typc\nlet f() = 1;\n```\n\n---\nTest", "contents": "```typc\nlet f() = int;\n```\n\n---\nTest",
"range": "3:20:3:21" "range": "3:20:3:21"
} }

View file

@ -0,0 +1,9 @@
---
source: crates/tinymist-query/src/hover.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/value_repr.typ
---
{
"contents": "```typc\nlet reconstruct(\n it,\n ..new-body: arguments,\n body-name: str = \"body\",\n labeled: bool = true,\n max-depth: int = 9999,\n wrapper: () => box = Closure(..),\n) = none;\n```\n\n---\n## Parameters\n\n@positional `it`\n\n@named `body-name`\n\n@named `labeled`\n\n@named `max-depth`\n\n@named `wrapper`\n\n@rest `new-body`",
"range": "1:20:1:31"
}

View file

@ -0,0 +1,2 @@
#let reconstruct(body-name: "body", labeled: true, max-depth: 9999, wrapper: () => box(""), it, ..new-body) = { }
#(/* ident after */ reconstruct);

View file

@ -4,4 +4,4 @@ description: "Check on \"(\" (17)"
expression: literal_type expression: literal_type
input_file: crates/tinymist-query/src/fixtures/post_type_check/with_builtin.typ input_file: crates/tinymist-query/src/fixtures/post_type_check/with_builtin.typ
--- ---
( ⪯ (Type(integer) | Type(ratio))) ( ⪯ (Type(int) | Type(ratio)))

View file

@ -7,7 +7,7 @@ input_file: crates/tinymist-query/src/fixtures/type_check/annotation_fn.typ
"fn" = Any "fn" = Any
"max-repetitions" = None "max-repetitions" = None
"repetitions" = None "repetitions" = None
"touying-fn-wrapper" = ((( ⪯ Type(function)), "max-repetitions": ( ⪯ Type(integer)), "repetitions": ( ⪯ Type(integer)), ...: ( ⪯ Any)) => None).with(..("max-repetitions": None, "repetitions": None) => any) "touying-fn-wrapper" = ((( ⪯ Type(function)), "max-repetitions": ( ⪯ Type(int)), "repetitions": ( ⪯ Type(int)), ...: ( ⪯ Any)) => None).with(..("max-repetitions": None, "repetitions": None) => any)
--- ---
162..180 -> @touying-fn-wrapper 162..180 -> @touying-fn-wrapper
181..183 -> @fn 181..183 -> @fn

View file

@ -4,7 +4,7 @@ expression: result
input_file: crates/tinymist-query/src/fixtures/type_check/annotation_sum.typ input_file: crates/tinymist-query/src/fixtures/type_check/annotation_sum.typ
--- ---
"args" = Args "args" = Args
"sum" = (...: ( ⪯ Array<Type(integer)>)) => None "sum" = (...: ( ⪯ Array<Type(int)>)) => None
--- ---
65..68 -> @sum 65..68 -> @sum
71..75 -> @args 71..75 -> @args

View file

@ -3,7 +3,7 @@ source: crates/tinymist-query/src/analysis.rs
expression: result expression: result
input_file: crates/tinymist-query/src/fixtures/type_check/fn_named.typ input_file: crates/tinymist-query/src/fixtures/type_check/fn_named.typ
--- ---
"d" = ( ⪰ 3 | Type(integer)) "d" = ( ⪰ 3 | Type(int))
"foo" = (("d": Any) => Any).with(..("d": 3) => any) "foo" = (("d": Any) => Any).with(..("d": 3) => any)
"x" = 3 "x" = 3
--- ---

View file

@ -6,7 +6,7 @@ input_file: crates/tinymist-query/src/fixtures/type_check/infer.typ
1..6 -> Func(image) 1..6 -> Func(image)
1..18 -> Element(image) 1..18 -> Element(image)
21..25 -> Func(read) 21..25 -> Func(read)
21..37 -> (Type(bytes) | Type(string)) 21..37 -> (Type(bytes) | Type(str))
40..44 -> Func(json) 40..44 -> Func(json)
40..57 -> Any 40..57 -> Any
60..64 -> Func(yaml) 60..64 -> Func(yaml)

View file

@ -7,7 +7,7 @@ input_file: crates/tinymist-query/src/fixtures/type_check/sig_template_set.typ
"class" = ( ⪰ "article" | "article" | "letter" | "article" | "letter") "class" = ( ⪰ "article" | "article" | "letter" | "article" | "letter")
"content" = Any "content" = Any
"font" = None "font" = None
"tmpl" = ((Any, "authors": ( ⪯ (Type(array) | Type(string))), "class": Any, "font": ( ⪯ (TextFont | Array<TextFont>))) => Any).with(..("authors": (), "class": "article", "font": None) => any) "tmpl" = ((Any, "authors": ( ⪯ (Type(array) | Type(str))), "class": Any, "font": ( ⪯ (TextFont | Array<TextFont>))) => Any).with(..("authors": (), "class": "article", "font": None) => any)
--- ---
5..9 -> @tmpl 5..9 -> @tmpl
10..17 -> @content 10..17 -> @content

View file

@ -3,10 +3,10 @@ source: crates/tinymist-query/src/analysis.rs
expression: result expression: result
input_file: crates/tinymist-query/src/fixtures/type_check/tuple_map.typ input_file: crates/tinymist-query/src/fixtures/type_check/tuple_map.typ
--- ---
"" = (( ⪯ (Type(bytes) | Type(decimal) | Type(float) | Type(integer) | Type(label) | Type(string) | Type(type) | Type(version)))) => Type(string) "" = (( ⪯ (Type(bytes) | Type(decimal) | Type(float) | Type(int) | Type(label) | Type(str) | Type(type) | Type(version)))) => Type(str)
"a" = (1, ) "a" = (1, )
"b" = (Type(string), ) "b" = (Type(str), )
"f" = (( ⪯ (Type(bytes) | Type(decimal) | Type(float) | Type(integer) | Type(label) | Type(string) | Type(type) | Type(version)))) => Type(string) "f" = (( ⪯ (Type(bytes) | Type(decimal) | Type(float) | Type(int) | Type(label) | Type(str) | Type(type) | Type(version)))) => Type(str)
"x" = Any "x" = Any
--- ---
5..6 -> @a 5..6 -> @a
@ -14,9 +14,9 @@ input_file: crates/tinymist-query/src/fixtures/type_check/tuple_map.typ
24..25 -> @x 24..25 -> @x
24..35 -> @ 24..35 -> @
29..32 -> Type(string) 29..32 -> Type(string)
29..35 -> Type(string) 29..35 -> Type(str)
33..34 -> @x 33..34 -> @x
42..43 -> @b 42..43 -> @b
46..47 -> @a 46..47 -> @a
46..54 -> (Type(string), ) 46..54 -> (Type(str), )
52..53 -> @f 52..53 -> @f

View file

@ -5,7 +5,7 @@ input_file: crates/tinymist-query/src/fixtures/type_check/with.typ
--- ---
"f" = (Any) => Any "f" = (Any) => Any
"g" = ((Any) => Any).with(..(1) => any) "g" = ((Any) => Any).with(..(1) => any)
"x" = ( ⪰ Type(integer) | Type(integer)) "x" = ( ⪰ Type(int) | Type(int))
"x" = 1 "x" = 1
--- ---
5..6 -> @f 5..6 -> @f

View file

@ -256,7 +256,7 @@ fn def_tooltip(
results.push(MarkedString::LanguageString(LanguageString { results.push(MarkedString::LanguageString(LanguageString {
language: "typc".to_owned(), language: "typc".to_owned(),
value: format!( value: format!(
"let {name}({params}){result};", "let {name}{params}{result};",
name = def.name(), name = def.name(),
params = ParamTooltip(sig.as_ref()), params = ParamTooltip(sig.as_ref()),
result = result =
@ -372,7 +372,7 @@ impl fmt::Display for ParamTooltip<'_> {
let Some(sig) = self.0 else { let Some(sig) = self.0 else {
return Ok(()); return Ok(());
}; };
sig.fmt(f) sig.print(f)
} }
} }

View file

@ -90,7 +90,7 @@ impl Expr {
impl fmt::Display for Expr { impl fmt::Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
ExprFormatter::new(f).write_expr(self) ExprFormatter::new(f, false).write_expr(self)
} }
} }
@ -571,7 +571,15 @@ pub enum Pattern {
impl fmt::Display for Pattern { impl fmt::Display for Pattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ExprFormatter::new(f).write_pattern(self) ExprFormatter::new(f, false).write_pattern(self)
}
}
impl Pattern {
pub(crate) fn repr(&self) -> EcoString {
let mut s = EcoString::new();
let _ = ExprFormatter::new(&mut s, true).write_pattern(self);
s
} }
} }
@ -847,14 +855,15 @@ impl_internable!(
ApplyExpr, ApplyExpr,
); );
struct ExprFormatter<'a, 'b> { struct ExprFormatter<'a, T: fmt::Write> {
f: &'a mut fmt::Formatter<'b>, f: &'a mut T,
repr: bool,
indent: usize, indent: usize,
} }
impl<'a, 'b> ExprFormatter<'a, 'b> { impl<'a, T: fmt::Write> ExprFormatter<'a, T> {
fn new(f: &'a mut fmt::Formatter<'b>) -> Self { fn new(f: &'a mut T, repr: bool) -> Self {
Self { f, indent: 0 } Self { f, repr, indent: 0 }
} }
fn write_decl(&mut self, d: &Decl) -> fmt::Result { fn write_decl(&mut self, d: &Decl) -> fmt::Result {
@ -1043,6 +1052,10 @@ impl<'a, 'b> ExprFormatter<'a, 'b> {
} }
fn write_func(&mut self, func: &Interned<FuncExpr>) -> fmt::Result { fn write_func(&mut self, func: &Interned<FuncExpr>) -> fmt::Result {
if self.repr {
return self.write_decl(&func.decl);
}
write!(self.f, "func[{:?}](", func.decl)?; write!(self.f, "func[{:?}](", func.decl)?;
self.write_pattern_sig(&func.params)?; self.write_pattern_sig(&func.params)?;
write!(self.f, " = ")?; write!(self.f, " = ")?;
@ -1130,12 +1143,20 @@ impl<'a, 'b> ExprFormatter<'a, 'b> {
} }
fn write_contextual(&mut self, c: &Interned<Expr>) -> fmt::Result { fn write_contextual(&mut self, c: &Interned<Expr>) -> fmt::Result {
if self.repr {
return self.f.write_str("content");
}
self.f.write_str("contextual(")?; self.f.write_str("contextual(")?;
self.write_expr(c)?; self.write_expr(c)?;
self.f.write_str(")") self.f.write_str(")")
} }
fn write_conditional(&mut self, c: &Interned<IfExpr>) -> fmt::Result { fn write_conditional(&mut self, c: &Interned<IfExpr>) -> fmt::Result {
if self.repr {
return self.f.write_str("Expr(..)");
}
self.f.write_str("if(")?; self.f.write_str("if(")?;
self.write_expr(&c.cond)?; self.write_expr(&c.cond)?;
self.f.write_str(", then = ")?; self.f.write_str(", then = ")?;
@ -1146,6 +1167,10 @@ impl<'a, 'b> ExprFormatter<'a, 'b> {
} }
fn write_while_loop(&mut self, w: &Interned<WhileExpr>) -> fmt::Result { fn write_while_loop(&mut self, w: &Interned<WhileExpr>) -> fmt::Result {
if self.repr {
return self.f.write_str("Expr(..)");
}
self.f.write_str("while(")?; self.f.write_str("while(")?;
self.write_expr(&w.cond)?; self.write_expr(&w.cond)?;
self.f.write_str(", ")?; self.f.write_str(", ")?;
@ -1154,6 +1179,10 @@ impl<'a, 'b> ExprFormatter<'a, 'b> {
} }
fn write_for_loop(&mut self, f: &Interned<ForExpr>) -> fmt::Result { fn write_for_loop(&mut self, f: &Interned<ForExpr>) -> fmt::Result {
if self.repr {
return self.f.write_str("Expr(..)");
}
self.f.write_str("for(")?; self.f.write_str("for(")?;
self.write_pattern(&f.pattern)?; self.write_pattern(&f.pattern)?;
self.f.write_str(", ")?; self.f.write_str(", ")?;

View file

@ -263,7 +263,7 @@ impl fmt::Debug for BuiltinTy {
BuiltinTy::Inset => write!(f, "Inset"), BuiltinTy::Inset => write!(f, "Inset"),
BuiltinTy::Outset => write!(f, "Outset"), BuiltinTy::Outset => write!(f, "Outset"),
BuiltinTy::Radius => write!(f, "Radius"), BuiltinTy::Radius => write!(f, "Radius"),
BuiltinTy::Type(ty) => write!(f, "Type({})", ty.long_name()), BuiltinTy::Type(ty) => write!(f, "Type({})", ty.short_name()),
BuiltinTy::Element(e) => e.fmt(f), BuiltinTy::Element(e) => e.fmt(f),
BuiltinTy::Tag(tag) => { BuiltinTy::Tag(tag) => {
let (name, id) = tag.as_ref(); let (name, id) = tag.as_ref();

View file

@ -6,12 +6,21 @@ use crate::ty::prelude::*;
impl TypeScheme { impl TypeScheme {
/// Describe the given type with the given type scheme. /// Describe the given type with the given type scheme.
pub fn describe(&self, ty: &Ty) -> Option<String> { pub fn describe(&self, ty: &Ty) -> Option<String> {
let mut worker = TypeDescriber::default(); let mut worker: TypeDescriber = TypeDescriber::default();
worker.describe_root(ty) worker.describe_root(ty)
} }
} }
impl Ty { impl Ty {
/// Describe the given type.
pub fn repr(&self) -> Option<String> {
let mut worker = TypeDescriber {
repr: true,
..Default::default()
};
worker.describe_root(self)
}
/// Describe the given type. /// Describe the given type.
pub fn describe(&self) -> Option<String> { pub fn describe(&self) -> Option<String> {
let mut worker = TypeDescriber::default(); let mut worker = TypeDescriber::default();
@ -21,6 +30,7 @@ impl Ty {
#[derive(Default)] #[derive(Default)]
struct TypeDescriber { struct TypeDescriber {
repr: bool,
described: HashMap<u128, String>, described: HashMap<u128, String>,
results: HashSet<String>, results: HashSet<String>,
functions: Vec<Interned<SigTy>>, functions: Vec<Interned<SigTy>>,
@ -148,8 +158,11 @@ impl TypeDescriber {
Ty::Builtin(BuiltinTy::Auto) => { Ty::Builtin(BuiltinTy::Auto) => {
return "auto".to_string(); return "auto".to_string();
} }
Ty::Boolean(..) if self.repr => {
return "bool".to_string();
}
Ty::Boolean(None) => { Ty::Boolean(None) => {
return "boolean".to_string(); return "bool".to_string();
} }
Ty::Boolean(Some(b)) => { Ty::Boolean(Some(b)) => {
return b.to_string(); return b.to_string();
@ -157,27 +170,19 @@ impl TypeDescriber {
Ty::Builtin(b) => { Ty::Builtin(b) => {
return b.describe(); return b.describe();
} }
Ty::Value(v) if self.repr => return v.val.ty().short_name().to_string(),
Ty::Value(v) => return v.val.repr().to_string(), Ty::Value(v) => return v.val.repr().to_string(),
Ty::Field(..) => { Ty::Field(..) => {
return "field".to_string(); return "field".to_string();
} }
Ty::Args(..) => { Ty::Args(..) => {
return "args".to_string(); return "arguments".to_string();
} }
Ty::Pattern(..) => { Ty::Pattern(..) => {
return "pattern".to_string(); return "pattern".to_string();
} }
Ty::Select(..) => { Ty::Select(..) | Ty::Unary(..) | Ty::Binary(..) | Ty::If(..) => {
return "any".to_string(); return "any".to_string()
}
Ty::Unary(..) => {
return "any".to_string();
}
Ty::Binary(..) => {
return "any".to_string();
}
Ty::If(..) => {
return "any".to_string();
} }
} }

View file

@ -909,7 +909,7 @@ fn type_completion(
} else { } else {
ctx.completions.push(Completion { ctx.completions.push(Completion {
kind: CompletionKind::Syntax, kind: CompletionKind::Syntax,
label: ty.long_name().into(), label: ty.short_name().into(),
apply: Some(eco_format!("${{{ty}}}")), apply: Some(eco_format!("${{{ty}}}")),
detail: Some(eco_format!("A value of type {ty}.")), detail: Some(eco_format!("A value of type {ty}.")),
..Completion::default() ..Completion::default()

View file

@ -96,7 +96,7 @@ const primitiveFunctions = {
const primitiveTypes: textmate.PatternMatch = { const primitiveTypes: textmate.PatternMatch = {
match: match:
/\b(auto|any|none|false|true|str|int|float|bool|length|content)\b(?!-)/, /\b(any|str|int|float|bool|length|content|array|dictionary|arguments)\b(?!-)/,
name: "entity.name.type.primitive.typst", name: "entity.name.type.primitive.typst",
}; };
@ -380,7 +380,12 @@ const markupEnterCode: textmate.Pattern = {
), ),
enterExpression( enterExpression(
"entity.name.type.primitive.hash.typst", "entity.name.type.primitive.hash.typst",
/(?=(?:auto|any|none|false|true|str|int|float|bool|length|content)\b(?!-))/ /(?=(?:any|str|int|float|bool|length|content|array|dictionary|arguments)\b(?!-))/
),
enterExpression("keyword.other.none.hash.typst", /(?=(?:none)\b(?!-))/),
enterExpression(
"constant.language.boolean.hash.typst",
/(?=(?:false|true)\b(?!-))/
), ),
enterExpression( enterExpression(
"entity.name.function.hash.typst", "entity.name.function.hash.typst",
@ -421,20 +426,25 @@ const floatUnit = (unit: RegExp, canDotSuff: boolean) =>
FLOAT_OR_INT.source + (canDotSuff ? "" : "(?<!\\.)") + unit.source FLOAT_OR_INT.source + (canDotSuff ? "" : "(?<!\\.)") + unit.source
); );
const constants: textmate.Pattern = { const keywordConstants: textmate.Pattern = {
patterns: [ patterns: [
{ {
name: "constant.language.none.typst", name: "keyword.other.none.typst",
match: /(?<!\)|\]|\})\bnone\b(?!-)/, match: /(?<!\)|\]|\})\bnone\b(?!-)/,
}, },
{ {
name: "constant.language.auto.typst", name: "keyword.other.auto.typst",
match: /(?<!\)|\]|\})\bauto\b(?!-)/, match: /(?<!\)|\]|\})\bauto\b(?!-)/,
}, },
{ {
name: "constant.language.boolean.typst", name: "constant.language.boolean.typst",
match: /(?<!\)|\]|\})\b(true|false)\b(?!-)/, match: /(?<!\)|\]|\})\b(false|true)\b(?!-)/,
}, },
],
};
const constants: textmate.Pattern = {
patterns: [
{ {
name: "constant.numeric.length.typst", name: "constant.numeric.length.typst",
match: floatUnit(/(mm|pt|cm|in|em)($|\b)/, false), match: floatUnit(/(mm|pt|cm|in|em)($|\b)/, false),
@ -522,6 +532,7 @@ const expressions = (): textmate.Grammar => {
{ include: "#primitiveFunctions" }, { include: "#primitiveFunctions" },
{ include: "#primitiveTypes" }, { include: "#primitiveTypes" },
// todo: enable if only if for completely right grammar // todo: enable if only if for completely right grammar
{ include: "#keywordConstants" },
{ include: "#identifier" }, { include: "#identifier" },
{ include: "#constants" }, { include: "#constants" },
{ {
@ -1373,6 +1384,7 @@ export const typst: textmate.Grammar = {
markup, markup,
markupEnterCode, markupEnterCode,
code, code,
keywordConstants,
constants, constants,
primitiveColors, primitiveColors,

View file

@ -374,7 +374,7 @@ fn e2e() {
}); });
let hash = replay_log(&tinymist_binary, &root.join("neovim")); let hash = replay_log(&tinymist_binary, &root.join("neovim"));
insta::assert_snapshot!(hash, @"siphash128_13:65fa45388648e656b1ec7cc30c48ad85"); insta::assert_snapshot!(hash, @"siphash128_13:bafb35d07c168f6630fc455df6ba0a3");
} }
{ {
@ -385,7 +385,7 @@ fn e2e() {
}); });
let hash = replay_log(&tinymist_binary, &root.join("vscode")); let hash = replay_log(&tinymist_binary, &root.join("vscode"));
insta::assert_snapshot!(hash, @"siphash128_13:79e7e3ea6fab006d0b4b510940e10ee9"); insta::assert_snapshot!(hash, @"siphash128_13:1cd09a6f0362384cafcbf632da1a1d0");
} }
} }