use std::sync::OnceLock;
use ecow::EcoString;
use tinymist_analysis::Signature;
use tinymist_analysis::docs::tidy::remove_list_annotations;
use tinymist_analysis::docs::{
DocText, DocTextResolver, ParamDocs, SignatureDocs, VarDocs, format_ty,
};
use tinymist_analysis::ty::DocSource;
use typst::syntax::Span;
use crate::LocalContext;
use crate::analysis::SharedContext;
pub(crate) fn var_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs> {
let source = ctx.source_by_id(pos.id()?).ok()?;
let type_info = ctx.type_check(&source);
let ty = type_info.type_of_span(pos)?;
let mut srcs = ty.sources();
srcs.sort();
log::debug!("check variable docs of ty: {ty:?} => {srcs:?}");
let doc_source = srcs.into_iter().next()?;
let simplified_ty = type_info.simplify(ty, false);
let return_ty = format_ty(Some(&simplified_ty));
match doc_source {
DocSource::Var(var) => {
let docs = type_info
.var_docs
.get(&var.def)
.map(|docs| docs.docs().clone());
Some(VarDocs {
docs: docs.unwrap_or_default(),
return_ty,
def_docs: OnceLock::new(),
})
}
DocSource::Ins(ins) => ins.syntax.as_ref().map(|src| {
let docs = convert_typst_docs(ctx, src.doc.as_ref().into());
VarDocs {
docs,
return_ty,
def_docs: OnceLock::new(),
}
}),
_ => None,
}
}
pub(crate) fn sig_docs(ctx: &mut LocalContext, sig: &Signature) -> Option<SignatureDocs> {
let type_sig = sig.type_sig().clone();
let pos_in = sig
.primary()
.pos()
.iter()
.enumerate()
.map(|(idx, pos)| (pos, type_sig.pos(idx)));
let named_in = sig
.primary()
.named()
.iter()
.map(|param| (param, type_sig.named(¶m.name)));
let rest_in = sig.primary().rest().map(|x| (x, type_sig.rest_param()));
let ret_in = type_sig.body.as_ref();
let pos = pos_in
.map(|(param, ty)| ParamDocs::new(ctx, param, ty))
.collect::<Vec<_>>();
let named = named_in
.map(|(param, ty)| (param.name.clone(), ParamDocs::new(ctx, param, ty)))
.collect::<std::collections::BTreeMap<_, _>>();
let rest = rest_in.map(|(param, ty)| ParamDocs::new(ctx, param, ty));
let ret_ty = format_ty(ret_in);
let docs = sig
.primary()
.docs
.as_ref()
.map(|docs| resolve_doc_text(ctx, docs))
.unwrap_or_default();
Some(SignatureDocs {
docs,
pos,
named,
rest,
ret_ty,
hover_docs: OnceLock::new(),
})
}
pub(crate) fn resolve_doc_text(ctx: &mut LocalContext, docs: &DocText) -> EcoString {
let shared = ctx.shared();
docs.get_or_init(|raw| convert_official_doc(shared, raw.clone()))
.clone()
}
impl DocTextResolver for LocalContext {
fn resolve_doc_text(&mut self, docs: &DocText) -> EcoString {
resolve_doc_text(self, docs)
}
}
fn convert_official_doc(ctx: &SharedContext, docs: EcoString) -> EcoString {
if docs.trim().is_empty() {
return docs;
}
let docs_text = DocText::official(docs.clone());
match crate::docs::convert_docs(ctx, &docs_text, None) {
Ok(converted) => {
let converted = remove_list_annotations(&converted);
ctx.remove_html(converted.trim().into())
}
Err(err) => {
log::warn!("failed to convert official Typst docs to Markdown: {err}");
ctx.remove_html(docs)
}
}
}
pub(crate) fn convert_typst_docs(ctx: &mut LocalContext, docs: EcoString) -> EcoString {
if docs.trim().is_empty() {
return docs;
}
let shared = ctx.shared_();
let docs_text = DocText::plain(docs.clone());
match crate::docs::convert_docs(&shared, &docs_text, None) {
Ok(converted) => {
let converted = remove_list_annotations(&converted);
ctx.remove_html(converted.trim().into())
}
Err(err) => {
log::warn!("failed to convert Typst docs to Markdown: {err}");
ctx.remove_html(docs)
}
}
}