mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 13:13:43 +00:00
feat: render hover docs with converted result (#701)
* feat: render hover docs with converted result * dev: update snapshot
This commit is contained in:
parent
e35b9f9c73
commit
e57cf36f9b
10 changed files with 100 additions and 39 deletions
|
@ -482,6 +482,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
named: named_docs,
|
||||
rest: rest_docs,
|
||||
ret_ty: (),
|
||||
def_docs: Default::default(),
|
||||
}))),
|
||||
);
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use core::fmt;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use ecow::{eco_format, EcoString};
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tinymist_world::base::{EntryState, ShadowApi, TaskInputs};
|
||||
use tinymist_world::LspWorld;
|
||||
use typst::foundations::{Bytes, Value};
|
||||
use typst::foundations::{Bytes, Func, Value};
|
||||
use typst::{
|
||||
diag::StrResult,
|
||||
syntax::{FileId, VirtualPath},
|
||||
|
@ -17,6 +17,7 @@ use super::tidy::*;
|
|||
use crate::analysis::{ParamAttrs, ParamSpec};
|
||||
use crate::docs::library;
|
||||
use crate::ty::Interned;
|
||||
use crate::upstream::plain_docs_sentence;
|
||||
use crate::{ty::Ty, AnalysisContext};
|
||||
|
||||
type TypeRepr = Option<(/* short */ String, /* long */ String)>;
|
||||
|
@ -137,6 +138,66 @@ pub struct SignatureDocsT<T> {
|
|||
pub rest: Option<ParamDocsT<T>>,
|
||||
/// The return type.
|
||||
pub ret_ty: T,
|
||||
/// The full documentation for the signature.
|
||||
#[serde(skip)]
|
||||
pub def_docs: OnceLock<String>,
|
||||
}
|
||||
|
||||
impl SignatureDocsT<TypeRepr> {
|
||||
/// Get full documentation for the signature.
|
||||
pub fn def_docs(&self) -> &String {
|
||||
self.def_docs
|
||||
.get_or_init(|| plain_docs_sentence(&format!("{}", DefDocs(self))).into())
|
||||
}
|
||||
}
|
||||
|
||||
struct DefDocs<'a>(&'a SignatureDocs);
|
||||
|
||||
impl fmt::Display for DefDocs<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
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")?;
|
||||
}
|
||||
}
|
||||
|
||||
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 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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Documentation about a signature.
|
||||
|
@ -250,28 +311,7 @@ pub(crate) fn signature_docs(
|
|||
runtime_fn: &Value,
|
||||
mut doc_ty: Option<ShowTypeRepr>,
|
||||
) -> Option<SignatureDocs> {
|
||||
let func = match runtime_fn {
|
||||
Value::Func(f) => f,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// todo: documenting with bindings
|
||||
use typst::foundations::func::Repr;
|
||||
let mut func = func;
|
||||
loop {
|
||||
match func.inner() {
|
||||
Repr::Element(..) | Repr::Native(..) => {
|
||||
break;
|
||||
}
|
||||
Repr::With(w) => {
|
||||
func = &w.0;
|
||||
}
|
||||
Repr::Closure(..) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let func = runtime_fn.clone().cast::<Func>().ok()?;
|
||||
let sig = ctx.signature_dyn(func.clone());
|
||||
let type_sig = sig.type_sig().clone();
|
||||
|
||||
|
@ -311,6 +351,7 @@ pub(crate) fn signature_docs(
|
|||
named,
|
||||
rest,
|
||||
ret_ty,
|
||||
def_docs: OnceLock::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -342,7 +383,7 @@ pub(crate) fn convert_docs(world: &LspWorld, content: &str) -> StrResult<EcoStri
|
|||
.convert()
|
||||
.map_err(|e| eco_format!("failed to convert to markdown: {e}"))?;
|
||||
|
||||
Ok(conv)
|
||||
Ok(conv.replace("```example", "```typ"))
|
||||
}
|
||||
|
||||
pub(crate) fn identify_docs(kind: DocStringKind, docs: EcoString) -> StrResult<SymbolDocs> {
|
||||
|
|
|
@ -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);\n```\n\n---\n\n\n #let fn = `(..fn-args) => any`;\n\n - fn (function, fn): The `fn`.\n - max-repetitions (int): The `max-repetitions`.\n - repetitions (int): The `repetitions`.\n - args (any, fn-args): The `args`.",
|
||||
"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`.",
|
||||
"range": "8:20:8:38"
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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---\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---\n[Open docs](https://typst.app/docs/reference/layout/pagebreak/)",
|
||||
"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/)",
|
||||
"range": "0:20:0:29"
|
||||
}
|
||||
|
|
|
@ -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();\n```\n\n---\n\n\nTest",
|
||||
"contents": "```typc\nlet f() = 1;\n```\n\n---\nTest",
|
||||
"range": "3:20:3:21"
|
||||
}
|
||||
|
|
|
@ -256,14 +256,15 @@ fn def_tooltip(
|
|||
results.push(MarkedString::LanguageString(LanguageString {
|
||||
language: "typc".to_owned(),
|
||||
value: format!(
|
||||
"let {name}({params});",
|
||||
"let {name}({params}){result};",
|
||||
name = lnk.name,
|
||||
params = ParamTooltip(sig.as_ref())
|
||||
params = ParamTooltip(sig.as_ref()),
|
||||
result = ResultTooltip(&lnk.name, sig.as_ref())
|
||||
),
|
||||
}));
|
||||
|
||||
if let Some(doc) = DocTooltip::get(ctx, &lnk) {
|
||||
results.push(MarkedString::String(doc));
|
||||
if let Some(doc) = sig {
|
||||
results.push(MarkedString::String(doc.def_docs().into()));
|
||||
}
|
||||
|
||||
if let Some(link) = ExternalDocLink::get(ctx, &lnk) {
|
||||
|
@ -360,6 +361,25 @@ impl fmt::Display for ParamTooltip<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
struct ResultTooltip<'a>(&'a str, Option<&'a SignatureDocs>);
|
||||
|
||||
impl fmt::Display for ResultTooltip<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Some(sig) = self.1 else {
|
||||
return Ok(());
|
||||
};
|
||||
if let Some((short, _)) = &sig.ret_ty {
|
||||
if short == self.0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write!(f, " = {short}")
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExternalDocLink;
|
||||
|
||||
impl ExternalDocLink {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue