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(),
docs: param_doc.docs.clone(),
cano_type: (),
default: Some(eco_format!("{exp}")),
default: Some(exp.repr()),
attrs: ParamAttrs::named(),
},
);

View file

@ -439,6 +439,6 @@ mod tests {
#[test]
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 {
let _ = writeln!(md, "<!-- begin:sig -->");
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, "<!-- end:sig -->");
}

View file

@ -207,17 +207,19 @@ pub type UntypedSignatureDocs = SignatureDocsT<()>;
/// Documentation about a signature.
pub type SignatureDocs = SignatureDocsT<TypeRepr>;
impl fmt::Display for SignatureDocs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl SignatureDocs {
/// 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 write_sep = |f: &mut fmt::Formatter<'_>| {
let mut write_sep = |f: &mut dyn std::fmt::Write| {
if is_first {
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 {
write_sep(f)?;
f.write_str(&p.name)?;
@ -246,7 +248,7 @@ impl fmt::Display for SignatureDocs {
let v = v.as_deref().unwrap_or("any");
let mut v = v.trim();
if v.starts_with('{') && v.ends_with('}') && v.len() > 30 {
v = "{ ... }"
v = "{ .. }"
}
if v.starts_with('`') && v.ends_with('`') && v.len() > 30 {
v = "raw"
@ -258,9 +260,17 @@ impl fmt::Display for SignatureDocs {
if let Some(t) = 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(())
}
@ -303,7 +313,7 @@ fn format_ty(ty: Option<&Ty>, doc_ty: Option<&mut ShowTypeRepr>) -> TypeRepr {
match doc_ty {
Some(doc_ty) => doc_ty(ty),
None => ty
.and_then(|ty| ty.describe())
.and_then(|ty| ty.repr())
.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
---
{
"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"
}

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
---
{
"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"
}

View file

@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
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"
}

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
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
"max-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
181..183 -> @fn

View file

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

View file

@ -3,7 +3,7 @@ source: crates/tinymist-query/src/analysis.rs
expression: result
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)
"x" = 3
---

View file

@ -6,7 +6,7 @@ input_file: crates/tinymist-query/src/fixtures/type_check/infer.typ
1..6 -> Func(image)
1..18 -> Element(image)
21..25 -> Func(read)
21..37 -> (Type(bytes) | Type(string))
21..37 -> (Type(bytes) | Type(str))
40..44 -> Func(json)
40..57 -> Any
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")
"content" = Any
"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
10..17 -> @content

View file

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

View file

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

View file

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

View file

@ -90,7 +90,7 @@ impl Expr {
impl fmt::Display for Expr {
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 {
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,
);
struct ExprFormatter<'a, 'b> {
f: &'a mut fmt::Formatter<'b>,
struct ExprFormatter<'a, T: fmt::Write> {
f: &'a mut T,
repr: bool,
indent: usize,
}
impl<'a, 'b> ExprFormatter<'a, 'b> {
fn new(f: &'a mut fmt::Formatter<'b>) -> Self {
Self { f, indent: 0 }
impl<'a, T: fmt::Write> ExprFormatter<'a, T> {
fn new(f: &'a mut T, repr: bool) -> Self {
Self { f, repr, indent: 0 }
}
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 {
if self.repr {
return self.write_decl(&func.decl);
}
write!(self.f, "func[{:?}](", func.decl)?;
self.write_pattern_sig(&func.params)?;
write!(self.f, " = ")?;
@ -1130,12 +1143,20 @@ impl<'a, 'b> ExprFormatter<'a, 'b> {
}
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.write_expr(c)?;
self.f.write_str(")")
}
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.write_expr(&c.cond)?;
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 {
if self.repr {
return self.f.write_str("Expr(..)");
}
self.f.write_str("while(")?;
self.write_expr(&w.cond)?;
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 {
if self.repr {
return self.f.write_str("Expr(..)");
}
self.f.write_str("for(")?;
self.write_pattern(&f.pattern)?;
self.f.write_str(", ")?;

View file

@ -263,7 +263,7 @@ impl fmt::Debug for BuiltinTy {
BuiltinTy::Inset => write!(f, "Inset"),
BuiltinTy::Outset => write!(f, "Outset"),
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::Tag(tag) => {
let (name, id) = tag.as_ref();

View file

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

View file

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