diff --git a/crates/tinymist-query/src/analysis.rs b/crates/tinymist-query/src/analysis.rs index 6642969e..3982a700 100644 --- a/crates/tinymist-query/src/analysis.rs +++ b/crates/tinymist-query/src/analysis.rs @@ -388,7 +388,7 @@ mod type_describe_tests { description => format!("Check on {text:?} ({pos:?})"), }, { let literal_type = literal_type.and_then(|e| e.describe()) - .unwrap_or_else(|| "".to_string()); + .unwrap_or_else(|| "".into()); assert_snapshot!(literal_type); }) }); diff --git a/crates/tinymist-query/src/docs/def.rs b/crates/tinymist-query/src/docs/def.rs index 07bb4123..7ed8fbe7 100644 --- a/crates/tinymist-query/src/docs/def.rs +++ b/crates/tinymist-query/src/docs/def.rs @@ -2,7 +2,7 @@ use core::fmt; use std::collections::BTreeMap; use std::sync::OnceLock; -use ecow::EcoString; +use ecow::{eco_format, EcoString}; use serde::{Deserialize, Serialize}; use typst::syntax::Span; @@ -14,9 +14,9 @@ use crate::ty::{DocSource, Interned}; use crate::upstream::plain_docs_sentence; type TypeRepr = Option<( - /* short */ String, - /* long */ String, - /* value */ String, + /* short */ EcoString, + /* long */ EcoString, + /* value */ EcoString, )>; /// Documentation about a definition (without type information). @@ -100,41 +100,57 @@ impl fmt::Display for SigHoverDocs<'_> { let docs = self.0; let base_docs = docs.docs.trim(); - let has_params_docs = !docs.pos.is_empty() || !docs.named.is_empty() || docs.rest.is_some(); - if !base_docs.is_empty() { f.write_str(base_docs)?; + } - if has_params_docs { - f.write_str("\n\n")?; + fn write_param_docs( + f: &mut fmt::Formatter<'_>, + p: &ParamDocsT, + kind: &str, + is_first: &mut bool, + ) -> fmt::Result { + if *is_first { + *is_first = false; + write!(f, "\n\n## {}\n\n", p.name)?; + } else { + write!(f, "\n\n## {} ({kind})\n\n", p.name)?; + } + + // p.cano_type.0 + if let Some(t) = &p.cano_type { + write!(f, "```typc\ntype: {}\n```\n\n", t.2)?; + } + + f.write_str(p.docs.trim())?; + + Ok(()) + } + + if !docs.pos.is_empty() { + f.write_str("\n\n# Positional Parameters")?; + + let mut is_first = true; + for p in &docs.pos { + write_param_docs(f, p, "positional", &mut is_first)?; } } - if has_params_docs { - f.write_str("## Parameters")?; - - for p in &docs.pos { - write!(f, "\n\n@positional `{}`", p.name)?; - if !p.docs.is_empty() { - f.write_str(" — ")?; - f.write_str(&p.docs)?; - } - } - - for (name, p) in &docs.named { - write!(f, "\n\n@named `{name}`")?; - if !p.docs.is_empty() { - f.write_str(" — ")?; - f.write_str(&p.docs)?; - } - } + if docs.rest.is_some() { + f.write_str("\n\n# Rest Parameters")?; + let mut is_first = true; if let Some(rest) = &docs.rest { - write!(f, "\n\n@rest `{}`", rest.name)?; - if !rest.docs.is_empty() { - f.write_str(" — ")?; - f.write_str(&rest.docs)?; - } + write_param_docs(f, rest, "spread right", &mut is_first)?; + } + } + + if !docs.named.is_empty() { + f.write_str("\n\n# Named Parameters")?; + + let mut is_first = true; + for p in docs.named.values() { + write_param_docs(f, p, "named", &mut is_first)?; } } @@ -219,7 +235,7 @@ impl SignatureDocs { /// Documentation about a variable (without type information). pub type UntypedVarDocs = VarDocsT<()>; /// Documentation about a variable. -pub type VarDocs = VarDocsT>; +pub type VarDocs = VarDocsT>; /// Describes a primary pattern binding. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -276,9 +292,9 @@ impl ParamDocs { fn format_ty(ty: Option<&Ty>) -> TypeRepr { let ty = ty?; - let short = ty.repr().unwrap_or_else(|| "any".to_owned()); - let long = format!("{ty:?}"); - let value = ty.value_repr().unwrap_or_else(|| "".to_owned()); + let short = ty.repr().unwrap_or_else(|| "any".into()); + let long = eco_format!("{ty:?}"); + let value = ty.value_repr().unwrap_or_else(|| "".into()); Some((short, long, value)) } diff --git a/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_docs_error.typ.snap b/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_docs_error.typ.snap index 43947f40..cb8ef331 100644 --- a/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_docs_error.typ.snap +++ b/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_docs_error.typ.snap @@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)" input_file: crates/tinymist-query/src/fixtures/hover/annotate_docs_error.typ --- { - "contents": "```typc\nlet speaker-note(\n note: any,\n mode: str = \"typ\",\n setting: (any) => any = Closure(..),\n) = none;\n```\n\n---\nSpeaker notes are a way to add additional information to your slides that is not visible to the audience. This can be useful for providing additional context or reminders to yourself.\n\n ## Example\n\n ```typ\n#speaker-note[This is a speaker note]\n\n```\n```\nRender Error\ncompiling node: error: unknown variable: speaker-note at \"/__render__.typ\":201..213\nHint: if you meant to use subtraction, try adding spaces around the minus sign: \\`speaker - note\\`\n\n```\n\n## Parameters\n\n@positional `note`\n\n@named `mode`\n\n@named `setting`", + "contents": "```typc\nlet speaker-note(\n note: any,\n mode: str = \"typ\",\n setting: (any) => any = Closure(..),\n) = none;\n```\n\n---\nSpeaker notes are a way to add additional information to your slides that is not visible to the audience. This can be useful for providing additional context or reminders to yourself.\n\n ## Example\n\n ```typ\n#speaker-note[This is a speaker note]\n\n```\n```\nRender Error\ncompiling node: error: unknown variable: speaker-note at \"/__render__.typ\":201..213\nHint: if you meant to use subtraction, try adding spaces around the minus sign: \\`speaker - note\\`\n\n```\n\n# Positional Parameters\n\n## note\n\n```typc\ntype: \n```\n\n\n\n# Named Parameters\n\n## mode\n\n```typc\ntype: \"typ\"\n```\n\n\n\n## setting (named)\n\n```typc\ntype: (any) => any\n```\n\n", "range": "11:20:11:32" } diff --git a/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_fn.typ.snap b/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_fn.typ.snap index da771e8f..fd783927 100644 --- a/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_fn.typ.snap +++ b/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_fn.typ.snap @@ -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(\n fn: (..: any) => 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`.", + "contents": "```typc\nlet touying-fn-wrapper(\n fn: (..: any) => any | function,\n ..args: arguments,\n max-repetitions: int | none = none,\n repetitions: int | none = none,\n) = none;\n```\n\n---\n\n\n# Positional Parameters\n\n## fn\n\n```typc\ntype: (..: any) => any | function\n```\n\nThe `fn`.\n\n# Rest Parameters\n\n## args\n\n```typc\ntype: arguments\n```\n\nThe `args`.\n\n# Named Parameters\n\n## max-repetitions\n\n```typc\ntype: int | none\n```\n\nThe `max-repetitions`.\n\n## repetitions (named)\n\n```typc\ntype: int | none\n```\n\nThe `repetitions`.", "range": "8:20:8:38" } diff --git a/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_ret.typ.snap b/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_ret.typ.snap index 5b8dd63f..0e21f4eb 100644 --- a/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_ret.typ.snap +++ b/crates/tinymist-query/src/fixtures/hover/snaps/test@annotate_ret.typ.snap @@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)" input_file: crates/tinymist-query/src/fixtures/hover/annotate_ret.typ --- { - "contents": "```typc\nlet _delayed-wrapper(\n body: any,\n) = content;\n```\n\n---\n## Parameters\n\n@positional `body`", + "contents": "```typc\nlet _delayed-wrapper(\n body: any,\n) = content;\n```\n\n---\n\n\n# Positional Parameters\n\n## body\n\n```typc\ntype: \n```\n\n", "range": "6:20:6:36" } diff --git a/crates/tinymist-query/src/fixtures/hover/snaps/test@builtin.typ.snap b/crates/tinymist-query/src/fixtures/hover/snaps/test@builtin.typ.snap index 10ce9e1a..a00c9cf0 100644 --- a/crates/tinymist-query/src/fixtures/hover/snaps/test@builtin.typ.snap +++ b/crates/tinymist-query/src/fixtures/hover/snaps/test@builtin.typ.snap @@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)" input_file: crates/tinymist-query/src/fixtures/hover/builtin.typ --- { - "contents": "```typc\nlet table(\n ..children: content,\n align: alignment | array | auto | function = auto,\n column-gutter: array | auto | length | type = (),\n columns: array | auto | length | type = (),\n fill: color = none,\n gutter: array | auto | length | type = (),\n inset: inset = 0% + 5pt,\n row-gutter: array | auto | length | type = (),\n rows: array | auto | length | type = (),\n stroke: stroke = 1pt + black,\n);\n```\n\n---\nA table of items.\n\nTables are used to arrange content in cells. Cells can contain arbitrary\ncontent, including multiple paragraphs and are specified in row-major order.\nFor a hands-on explanation of all the ways you can use and customize tables\nin Typst, check out the [table guide](https://typst.app/docs/guides/table-guide/).\n\nBecause tables are just grids with different defaults for some cell\nproperties (notably `stroke` and `inset`), refer to the [grid\ndocumentation](https://typst.app/docs/reference/layout/grid/) for more information on how to size the table tracks\nand specify the cell appearance properties.\n\nIf you are unsure whether you should be using a table or a grid, consider\nwhether the content you are arranging semantically belongs together as a set\nof related data points or similar or whether you are just want to enhance\nyour presentation by arranging unrelated content in a grid. In the former\ncase, a table is the right choice, while in the latter case, a grid is more\nappropriate. Furthermore, Typst will annotate its output in the future such\nthat screenreaders will announce content in `table` as tabular while a\ngrid's content will be announced no different than multiple content blocks\nin the document flow.\n\nNote that, to override a particular cell's properties or apply show rules on\ntable cells, you can use the [`table.cell`](https://typst.app/docs/reference/model/table/#definitions-cell) element. See its\ndocumentation for more information.\n\nAlthough the `table` and the `grid` share most properties, set and show\nrules on one of them do not affect the other.\n\nTo give a table a caption and make it [referenceable](https://typst.app/docs/reference/model/ref/), put it into a\n[figure].\n\n# Example\n\nThe example below demonstrates some of the most common table options.\n```typ\n#table(\n columns: (1fr, auto, auto),\n inset: 10pt,\n align: horizon,\n table.header(\n [], [*Volume*], [*Parameters*],\n ),\n image(\"cylinder.svg\"),\n $ pi h (D^2 - d^2) / 4 $,\n [\n $h$: height \\\n $D$: outer radius \\\n $d$: inner radius\n ],\n image(\"tetrahedron.svg\"),\n $ sqrt(2) / 12 a^3 $,\n [$a$: edge length]\n)\n```\n\nMuch like with grids, you can use [`table.cell`](https://typst.app/docs/reference/model/table/#definitions-cell) to customize\nthe appearance and the position of each cell.\n\n```typ\n>>> #set page(width: auto)\n>>> #set text(font: \"IBM Plex Sans\")\n>>> #let gray = rgb(\"#565565\")\n>>>\n#set table(\n stroke: none,\n gutter: 0.2em,\n fill: (x, y) =>\n if x == 0 or y == 0 { gray },\n inset: (right: 1.5em),\n)\n\n#show table.cell: it => {\n if it.x == 0 or it.y == 0 {\n set text(white)\n strong(it)\n } else if it.body == [] {\n // Replace empty cells with 'N/A'\n pad(..it.inset)[_N/A_]\n } else {\n it\n }\n}\n\n#let a = table.cell(\n fill: green.lighten(60%),\n)[A]\n#let b = table.cell(\n fill: aqua.lighten(60%),\n)[B]\n\n#table(\n columns: 4,\n [], [Exam 1], [Exam 2], [Exam 3],\n\n [John], [], a, [],\n [Mary], [], a, a,\n [Robert], b, a, b,\n)\n```\n\n## Parameters\n\n@named `align` — How to align the cells' content.\n\nThis can either be a single alignment, an array of alignments\n(corresponding to each column) or a function that returns an alignment.\nThe function receives the cells' column and row indices, starting from\nzero. If set to `auto`, the outer alignment is used.\n\n```typ\n#table(\n columns: 3,\n align: (left, center, right),\n [Hello], [Hello], [Hello],\n [A], [B], [C],\n)\n```\n\n@named `column-gutter` — The gaps between columns. Takes precedence over `gutter`. See the\n[grid documentation](https://typst.app/docs/reference/layout/grid/) for more information on gutters.\n\n@named `columns` — The column sizes. See the [grid documentation](https://typst.app/docs/reference/layout/grid/) for more\ninformation on track sizing.\n\n@named `fill` — How to fill the cells.\n\nThis can be a color or a function that returns a color. The function\nreceives the cells' column and row indices, starting from zero. This can\nbe used to implement striped tables.\n\n```typ\n#table(\n fill: (x, _) =>\n if calc.odd(x) { luma(240) }\n else { white },\n align: (x, y) =>\n if y == 0 { center }\n else if x == 0 { left }\n else { right },\n columns: 4,\n [], [*Q1*], [*Q2*], [*Q3*],\n [Revenue:], [1000 €], [2000 €], [3000 €],\n [Expenses:], [500 €], [1000 €], [1500 €],\n [Profit:], [500 €], [1000 €], [1500 €],\n)\n```\n\n@named `gutter` — The gaps between rows and columns. This is a shorthand for setting\n`column-gutter` and `row-gutter` to the same value. See the [grid\ndocumentation](https://typst.app/docs/reference/layout/grid/) for more information on gutters.\n\n@named `inset` — How much to pad the cells' content.\n\n```typ\n#table(\n inset: 10pt,\n [Hello],\n [World],\n)\n\n#table(\n columns: 2,\n inset: (\n x: 20pt,\n y: 10pt,\n ),\n [Hello],\n [World],\n)\n```\n\n@named `row-gutter` — The gaps between rows. Takes precedence over `gutter`. See the\n[grid documentation](https://typst.app/docs/reference/layout/grid/) for more information on gutters.\n\n@named `rows` — The row sizes. See the [grid documentation](https://typst.app/docs/reference/layout/grid/) for more information\non track sizing.\n\n@named `stroke` — How to [stroke] the cells.\n\nStrokes can be disabled by setting this to `none`.\n\nIf it is necessary to place lines which can cross spacing between cells\nproduced by the `gutter` option, or to override the stroke between\nmultiple specific cells, consider specifying one or more of\n[`table.hline`](https://typst.app/docs/reference/model/table/#definitions-hline) and [`table.vline`](https://typst.app/docs/reference/model/table/#definitions-vline)\nalongside your table cells.\n\nSee the [grid documentation](https://typst.app/docs/reference/layout/grid/#parameters-stroke) for more information on\nstrokes.\n\n@rest `children` — The contents of the table cells, plus any extra table lines specified\nwith the [`table.hline`](https://typst.app/docs/reference/model/table/#definitions-hline) and\n[`table.vline`](https://typst.app/docs/reference/model/table/#definitions-vline) elements.\n\n---\n[Open docs](https://typst.app/docs/reference/model/table/)", + "contents": "```typc\nlet table(\n ..children: content,\n align: alignment | array | auto | function = auto,\n column-gutter: array | auto | length | type = (),\n columns: array | auto | length | type = (),\n fill: color = none,\n gutter: array | auto | length | type = (),\n inset: inset = 0% + 5pt,\n row-gutter: array | auto | length | type = (),\n rows: array | auto | length | type = (),\n stroke: stroke = 1pt + black,\n);\n```\n\n---\nA table of items.\n\nTables are used to arrange content in cells. Cells can contain arbitrary\ncontent, including multiple paragraphs and are specified in row-major order.\nFor a hands-on explanation of all the ways you can use and customize tables\nin Typst, check out the [table guide](https://typst.app/docs/guides/table-guide/).\n\nBecause tables are just grids with different defaults for some cell\nproperties (notably `stroke` and `inset`), refer to the [grid\ndocumentation](https://typst.app/docs/reference/layout/grid/) for more information on how to size the table tracks\nand specify the cell appearance properties.\n\nIf you are unsure whether you should be using a table or a grid, consider\nwhether the content you are arranging semantically belongs together as a set\nof related data points or similar or whether you are just want to enhance\nyour presentation by arranging unrelated content in a grid. In the former\ncase, a table is the right choice, while in the latter case, a grid is more\nappropriate. Furthermore, Typst will annotate its output in the future such\nthat screenreaders will announce content in `table` as tabular while a\ngrid's content will be announced no different than multiple content blocks\nin the document flow.\n\nNote that, to override a particular cell's properties or apply show rules on\ntable cells, you can use the [`table.cell`](https://typst.app/docs/reference/model/table/#definitions-cell) element. See its\ndocumentation for more information.\n\nAlthough the `table` and the `grid` share most properties, set and show\nrules on one of them do not affect the other.\n\nTo give a table a caption and make it [referenceable](https://typst.app/docs/reference/model/ref/), put it into a\n[figure].\n\n# Example\n\nThe example below demonstrates some of the most common table options.\n```typ\n#table(\n columns: (1fr, auto, auto),\n inset: 10pt,\n align: horizon,\n table.header(\n [], [*Volume*], [*Parameters*],\n ),\n image(\"cylinder.svg\"),\n $ pi h (D^2 - d^2) / 4 $,\n [\n $h$: height \\\n $D$: outer radius \\\n $d$: inner radius\n ],\n image(\"tetrahedron.svg\"),\n $ sqrt(2) / 12 a^3 $,\n [$a$: edge length]\n)\n```\n\nMuch like with grids, you can use [`table.cell`](https://typst.app/docs/reference/model/table/#definitions-cell) to customize\nthe appearance and the position of each cell.\n\n```typ\n>>> #set page(width: auto)\n>>> #set text(font: \"IBM Plex Sans\")\n>>> #let gray = rgb(\"#565565\")\n>>>\n#set table(\n stroke: none,\n gutter: 0.2em,\n fill: (x, y) =>\n if x == 0 or y == 0 { gray },\n inset: (right: 1.5em),\n)\n\n#show table.cell: it => {\n if it.x == 0 or it.y == 0 {\n set text(white)\n strong(it)\n } else if it.body == [] {\n // Replace empty cells with 'N/A'\n pad(..it.inset)[_N/A_]\n } else {\n it\n }\n}\n\n#let a = table.cell(\n fill: green.lighten(60%),\n)[A]\n#let b = table.cell(\n fill: aqua.lighten(60%),\n)[B]\n\n#table(\n columns: 4,\n [], [Exam 1], [Exam 2], [Exam 3],\n\n [John], [], a, [],\n [Mary], [], a, a,\n [Robert], b, a, b,\n)\n```\n\n# Rest Parameters\n\n## children\n\n```typc\ntype: content\n```\n\nThe contents of the table cells, plus any extra table lines specified\nwith the [`table.hline`](https://typst.app/docs/reference/model/table/#definitions-hline) and\n[`table.vline`](https://typst.app/docs/reference/model/table/#definitions-vline) elements.\n\n# Named Parameters\n\n## align\n\n```typc\ntype: alignment | array | auto | function\n```\n\nHow to align the cells' content.\n\nThis can either be a single alignment, an array of alignments\n(corresponding to each column) or a function that returns an alignment.\nThe function receives the cells' column and row indices, starting from\nzero. If set to `auto`, the outer alignment is used.\n\n```typ\n#table(\n columns: 3,\n align: (left, center, right),\n [Hello], [Hello], [Hello],\n [A], [B], [C],\n)\n```\n\n## column-gutter (named)\n\n```typc\ntype: array | auto | integer | length\n```\n\nThe gaps between columns. Takes precedence over `gutter`. See the\n[grid documentation](https://typst.app/docs/reference/layout/grid/) for more information on gutters.\n\n## columns (named)\n\n```typc\ntype: array | auto | integer | length\n```\n\nThe column sizes. See the [grid documentation](https://typst.app/docs/reference/layout/grid/) for more\ninformation on track sizing.\n\n## fill (named)\n\n```typc\ntype: color\n```\n\nHow to fill the cells.\n\nThis can be a color or a function that returns a color. The function\nreceives the cells' column and row indices, starting from zero. This can\nbe used to implement striped tables.\n\n```typ\n#table(\n fill: (x, _) =>\n if calc.odd(x) { luma(240) }\n else { white },\n align: (x, y) =>\n if y == 0 { center }\n else if x == 0 { left }\n else { right },\n columns: 4,\n [], [*Q1*], [*Q2*], [*Q3*],\n [Revenue:], [1000 €], [2000 €], [3000 €],\n [Expenses:], [500 €], [1000 €], [1500 €],\n [Profit:], [500 €], [1000 €], [1500 €],\n)\n```\n\n## gutter (named)\n\n```typc\ntype: array | auto | integer | length\n```\n\nThe gaps between rows and columns. This is a shorthand for setting\n`column-gutter` and `row-gutter` to the same value. See the [grid\ndocumentation](https://typst.app/docs/reference/layout/grid/) for more information on gutters.\n\n## inset (named)\n\n```typc\ntype: inset\n```\n\nHow much to pad the cells' content.\n\n```typ\n#table(\n inset: 10pt,\n [Hello],\n [World],\n)\n\n#table(\n columns: 2,\n inset: (\n x: 20pt,\n y: 10pt,\n ),\n [Hello],\n [World],\n)\n```\n\n## row-gutter (named)\n\n```typc\ntype: array | auto | integer | length\n```\n\nThe gaps between rows. Takes precedence over `gutter`. See the\n[grid documentation](https://typst.app/docs/reference/layout/grid/) for more information on gutters.\n\n## rows (named)\n\n```typc\ntype: array | auto | integer | length\n```\n\nThe row sizes. See the [grid documentation](https://typst.app/docs/reference/layout/grid/) for more information\non track sizing.\n\n## stroke (named)\n\n```typc\ntype: stroke\n```\n\nHow to [stroke] the cells.\n\nStrokes can be disabled by setting this to `none`.\n\nIf it is necessary to place lines which can cross spacing between cells\nproduced by the `gutter` option, or to override the stroke between\nmultiple specific cells, consider specifying one or more of\n[`table.hline`](https://typst.app/docs/reference/model/table/#definitions-hline) and [`table.vline`](https://typst.app/docs/reference/model/table/#definitions-vline)\nalongside your table cells.\n\nSee the [grid documentation](https://typst.app/docs/reference/layout/grid/#parameters-stroke) for more information on\nstrokes.\n\n---\n[Open docs](https://typst.app/docs/reference/model/table/)", "range": "0:20:0:25" } diff --git a/crates/tinymist-query/src/fixtures/hover/snaps/test@pagebreak.typ.snap b/crates/tinymist-query/src/fixtures/hover/snaps/test@pagebreak.typ.snap index e7e47c3f..8b9152ae 100644 --- a/crates/tinymist-query/src/fixtures/hover/snaps/test@pagebreak.typ.snap +++ b/crates/tinymist-query/src/fixtures/hover/snaps/test@pagebreak.typ.snap @@ -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(\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/)", + "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# Named Parameters\n\n## to\n\n```typc\ntype: \"even\" | \"odd\" | none\n```\n\nIf 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## weak (named)\n\n```typc\ntype: bool\n```\n\nIf `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" } diff --git a/crates/tinymist-query/src/fixtures/hover/snaps/test@value_repr.typ.snap b/crates/tinymist-query/src/fixtures/hover/snaps/test@value_repr.typ.snap index f24f9db4..7f39008c 100644 --- a/crates/tinymist-query/src/fixtures/hover/snaps/test@value_repr.typ.snap +++ b/crates/tinymist-query/src/fixtures/hover/snaps/test@value_repr.typ.snap @@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)" input_file: crates/tinymist-query/src/fixtures/hover/value_repr.typ --- { - "contents": "```typc\nlet f(\n x: any,\n y: any,\n z: any,\n w01: int = 1,\n w02: str = \"test\",\n w03: any = 1 + 2,\n w04: any = Label(test),\n w05: function = box,\n w06: any = list.item,\n w07: content = Expr(..),\n w08: any = Expr(..),\n w09: any = 1 + 2,\n w10: array = (\n 1,\n 2,\n ),\n w11: array = (),\n w12: dict = (:),\n w13: dict = (a: 1),\n w14: dict = (a: box),\n w15: dict = (a: list.item),\n) = int;\n```\n\n---\n## Parameters\n\n@positional `x`\n\n@positional `y`\n\n@positional `z`\n\n@named `w01`\n\n@named `w02`\n\n@named `w03`\n\n@named `w04`\n\n@named `w05`\n\n@named `w06`\n\n@named `w07`\n\n@named `w08`\n\n@named `w09`\n\n@named `w10`\n\n@named `w11`\n\n@named `w12`\n\n@named `w13`\n\n@named `w14`\n\n@named `w15`", + "contents": "```typc\nlet f(\n x: any,\n y: any,\n z: any,\n w01: int = 1,\n w02: str = \"test\",\n w03: any = 1 + 2,\n w04: any = Label(test),\n w05: function = box,\n w06: any = list.item,\n w07: content = Expr(..),\n w08: any = Expr(..),\n w09: any = 1 + 2,\n w10: array = (\n 1,\n 2,\n ),\n w11: array = (),\n w12: dict = (:),\n w13: dict = (a: 1),\n w14: dict = (a: box),\n w15: dict = (a: list.item),\n) = int;\n```\n\n---\n\n\n# Positional Parameters\n\n## x\n\n```typc\ntype: \n```\n\n\n\n## y (positional)\n\n```typc\ntype: \n```\n\n\n\n## z (positional)\n\n```typc\ntype: \n```\n\n\n\n# Named Parameters\n\n## w01\n\n```typc\ntype: 1\n```\n\n\n\n## w02 (named)\n\n```typc\ntype: \"test\"\n```\n\n\n\n## w03 (named)\n\n```typc\ntype: any\n```\n\n\n\n## w04 (named)\n\n```typc\ntype: \n```\n\n\n\n## w05 (named)\n\n```typc\ntype: box\n```\n\n\n\n## w06 (named)\n\n```typc\ntype: any\n```\n\n\n\n## w07 (named)\n\n```typc\ntype: content\n```\n\n\n\n## w08 (named)\n\n```typc\ntype: any\n```\n\n\n\n## w09 (named)\n\n```typc\ntype: any\n```\n\n\n\n## w10 (named)\n\n```typc\ntype: array\n```\n\n\n\n## w11 (named)\n\n```typc\ntype: array\n```\n\n\n\n## w12 (named)\n\n```typc\ntype: dict\n```\n\n\n\n## w13 (named)\n\n```typc\ntype: dict\n```\n\n\n\n## w14 (named)\n\n```typc\ntype: dict\n```\n\n\n\n## w15 (named)\n\n```typc\ntype: dict\n```\n\n", "range": "23:20:23:21" } diff --git a/crates/tinymist-query/src/hover.rs b/crates/tinymist-query/src/hover.rs index f6a5523f..7d0e9b82 100644 --- a/crates/tinymist-query/src/hover.rs +++ b/crates/tinymist-query/src/hover.rs @@ -272,7 +272,11 @@ fn link_tooltip( Some(HoverContents::Array(results)) } -fn push_result_ty(name: &str, ty_repr: Option<&(String, String, String)>, type_doc: &mut String) { +fn push_result_ty( + name: &str, + ty_repr: Option<&(EcoString, EcoString, EcoString)>, + type_doc: &mut String, +) { let Some((short, _, _)) = ty_repr else { return; }; diff --git a/crates/tinymist-query/src/ty/builtin.rs b/crates/tinymist-query/src/ty/builtin.rs index 0ada2171..a2e80023 100644 --- a/crates/tinymist-query/src/ty/builtin.rs +++ b/crates/tinymist-query/src/ty/builtin.rs @@ -1,5 +1,6 @@ use core::fmt; +use ecow::{eco_format, EcoString}; use once_cell::sync::Lazy; use regex::RegexSet; use strum::{EnumIter, IntoEnumIterator}; @@ -312,7 +313,7 @@ impl BuiltinTy { BuiltinTy::Type(builtin).literally() } - pub(crate) fn describe(&self) -> String { + pub(crate) fn describe(&self) -> EcoString { let res = match self { BuiltinTy::Clause => "any", BuiltinTy::Undef => "any", @@ -347,9 +348,9 @@ impl BuiltinTy { BuiltinTy::Tag(tag) => { let (name, id) = tag.as_ref(); return if let Some(id) = id { - format!("tag {name} of {id:?}") + eco_format!("tag {name} of {id:?}") } else { - format!("tag {name}") + eco_format!("tag {name}") }; } BuiltinTy::Path(s) => match s { @@ -369,7 +370,7 @@ impl BuiltinTy { }, }; - res.to_string() + res.into() } } diff --git a/crates/tinymist-query/src/ty/describe.rs b/crates/tinymist-query/src/ty/describe.rs index 9a36f9da..736b7a80 100644 --- a/crates/tinymist-query/src/ty/describe.rs +++ b/crates/tinymist-query/src/ty/describe.rs @@ -1,11 +1,12 @@ +use ecow::{eco_format, EcoString}; use reflexo::hash::hash128; use typst::foundations::Repr; -use crate::ty::prelude::*; +use crate::{ty::prelude::*, upstream::truncated_repr_}; impl TypeScheme { /// Describe the given type with the given type scheme. - pub fn describe(&self, ty: &Ty) -> Option { + pub fn describe(&self, ty: &Ty) -> Option { let mut worker: TypeDescriber = TypeDescriber::default(); worker.describe_root(ty) } @@ -13,7 +14,7 @@ impl TypeScheme { impl Ty { /// Describe the given type. - pub fn repr(&self) -> Option { + pub fn repr(&self) -> Option { let mut worker = TypeDescriber { repr: true, ..Default::default() @@ -22,16 +23,17 @@ impl Ty { } /// Describe available value instances of the given type. - pub fn value_repr(&self) -> Option { + pub fn value_repr(&self) -> Option { let mut worker = TypeDescriber { repr: true, + value: true, ..Default::default() }; worker.describe_root(self) } /// Describe the given type. - pub fn describe(&self) -> Option { + pub fn describe(&self) -> Option { let mut worker = TypeDescriber::default(); worker.describe_root(self) } @@ -55,13 +57,14 @@ impl Ty { #[derive(Default)] struct TypeDescriber { repr: bool, - described: HashMap, - results: HashSet, + value: bool, + described: HashMap, + results: HashSet, functions: Vec>, } impl TypeDescriber { - fn describe_root(&mut self, ty: &Ty) -> Option { + fn describe_root(&mut self, ty: &Ty) -> Option { let _ = TypeDescriber::describe_iter; // recursive structure if let Some(t) = self.described.get(&hash128(ty)) { @@ -72,7 +75,7 @@ impl TypeDescriber { if !res.is_empty() { return Some(res); } - self.described.insert(hash128(ty), "$self".to_string()); + self.described.insert(hash128(ty), "$self".into()); let mut results = std::mem::take(&mut self.results) .into_iter() @@ -83,7 +86,7 @@ impl TypeDescriber { // only first function is described let f = functions[0].clone(); - let mut res = String::new(); + let mut res = EcoString::new(); res.push('('); let mut not_first = false; for ty in f.positional_params() { @@ -124,13 +127,13 @@ impl TypeDescriber { } if results.is_empty() { - self.described.insert(hash128(ty), "any".to_string()); + self.described.insert(hash128(ty), "any".into()); return None; } results.sort(); results.dedup(); - let res = results.join(" | "); + let res: EcoString = results.join(" | ").into(); self.described.insert(hash128(ty), res.clone()); Some(res) } @@ -144,7 +147,7 @@ impl TypeDescriber { } } - fn describe(&mut self, ty: &Ty) -> String { + fn describe(&mut self, ty: &Ty) -> EcoString { match ty { Ty::Var(..) => {} Ty::Union(tys) => { @@ -158,57 +161,56 @@ impl TypeDescriber { self.functions.push(f.clone()); } Ty::Dict(..) => { - return "dict".to_string(); + return "dict".into(); } Ty::Tuple(..) => { - return "array".to_string(); + return "array".into(); } Ty::Array(..) => { - return "array".to_string(); + return "array".into(); } // todo: sig with Ty::With(w) => { return self.describe(&w.sig); } Ty::Builtin(BuiltinTy::Content | BuiltinTy::Space) => { - return "content".to_string(); + return "content".into(); } // Doesn't provide any information, hence we doesn't describe it intermediately here. Ty::Any | Ty::Builtin(BuiltinTy::Clause | BuiltinTy::Undef | BuiltinTy::Infer) => {} Ty::Builtin(BuiltinTy::FlowNone | BuiltinTy::None) => { - return "none".to_string(); + return "none".into(); } Ty::Builtin(BuiltinTy::Auto) => { - return "auto".to_string(); + return "auto".into(); } Ty::Boolean(..) if self.repr => { - return "bool".to_string(); + return "bool".into(); } Ty::Boolean(None) => { - return "bool".to_string(); + return "bool".into(); } Ty::Boolean(Some(b)) => { - return b.to_string(); + return eco_format!("{b}"); } 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::Value(v) if self.value => return truncated_repr_::<181>(&v.val), + Ty::Value(v) if self.repr => return v.val.ty().short_name().into(), + Ty::Value(v) => return v.val.repr(), Ty::Field(..) => { - return "field".to_string(); + return "field".into(); } Ty::Args(..) => { - return "arguments".to_string(); + return "arguments".into(); } Ty::Pattern(..) => { - return "pattern".to_string(); - } - Ty::Select(..) | Ty::Unary(..) | Ty::Binary(..) | Ty::If(..) => { - return "any".to_string() + return "pattern".into(); } + Ty::Select(..) | Ty::Unary(..) | Ty::Binary(..) | Ty::If(..) => return "any".into(), } - String::new() + EcoString::new() } } diff --git a/crates/tinymist-query/src/upstream/complete/ext.rs b/crates/tinymist-query/src/upstream/complete/ext.rs index 30ffb8b4..e62eb2b7 100644 --- a/crates/tinymist-query/src/upstream/complete/ext.rs +++ b/crates/tinymist-query/src/upstream/complete/ext.rs @@ -469,7 +469,6 @@ fn describe_value(ctx: &mut LocalContext, v: &Value) -> EcoString { .ty() .describe() .unwrap_or_else(|| "function".into()) - .into() } Value::Module(m) => { if let Some(fid) = m.file_id() { diff --git a/tests/e2e/main.rs b/tests/e2e/main.rs index 58944321..3d2d98aa 100644 --- a/tests/e2e/main.rs +++ b/tests/e2e/main.rs @@ -374,7 +374,7 @@ fn e2e() { }); let hash = replay_log(&tinymist_binary, &root.join("neovim")); - insta::assert_snapshot!(hash, @"siphash128_13:513716cde1f43609375d257774bdddd9"); + insta::assert_snapshot!(hash, @"siphash128_13:37db2c66ca1b5a0cdec20f531c15d5"); } { @@ -385,7 +385,7 @@ fn e2e() { }); let hash = replay_log(&tinymist_binary, &root.join("vscode")); - insta::assert_snapshot!(hash, @"siphash128_13:5fc968f2227784be47aac5beca4ef369"); + insta::assert_snapshot!(hash, @"siphash128_13:338a3a6f7c14c355579c3a4a99f47b30"); } }