mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-24 21:19:37 +00:00
dev: merge signature docs and rest docs (#685)
* dev: deduplicate code * dev: merge signature docs and rest docs * dev: change struct of the `DocString` * dev: improve structure of SymbolDocs
This commit is contained in:
parent
d121e8279d
commit
b9da92175e
12 changed files with 522 additions and 455 deletions
|
|
@ -130,7 +130,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_var(&mut self, s: Span, r: IdentRef) -> Option<Ty> {
|
fn get_var(&mut self, s: Span, r: IdentRef) -> Option<Interned<TypeVar>> {
|
||||||
let def_id = self.get_def_id(s, &r)?;
|
let def_id = self.get_def_id(s, &r)?;
|
||||||
|
|
||||||
// todo: false positive of clippy
|
// todo: false positive of clippy
|
||||||
|
|
@ -150,9 +150,9 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let var = self.info.vars.get_mut(&def_id).unwrap();
|
let var = self.info.vars.get(&def_id).unwrap().var.clone();
|
||||||
TypeScheme::witness_(s, var.as_type(), &mut self.info.mapping);
|
TypeScheme::witness_(s, Ty::Var(var.clone()), &mut self.info.mapping);
|
||||||
Some(var.as_type())
|
Some(var)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_ty(&mut self, def_id: DefId) -> Option<Ty> {
|
fn import_ty(&mut self, def_id: DefId) -> Option<Ty> {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use super::*;
|
||||||
const DOC_VARS: u64 = 0;
|
const DOC_VARS: u64 = 0;
|
||||||
|
|
||||||
impl<'a, 'w> TypeChecker<'a, 'w> {
|
impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
pub fn check_func_docs(&mut self, root: &LinkedNode) -> Option<DocString> {
|
pub fn check_func_docs(&mut self, root: &LinkedNode) -> Option<Arc<DocString>> {
|
||||||
let closure = root.cast::<ast::Closure>()?;
|
let closure = root.cast::<ast::Closure>()?;
|
||||||
let documenting_id = closure
|
let documenting_id = closure
|
||||||
.name()
|
.name()
|
||||||
|
|
@ -23,7 +23,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
self.check_docstring(root, DocStringKind::Function, documenting_id)
|
self.check_docstring(root, DocStringKind::Function, documenting_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_var_docs(&mut self, root: &LinkedNode) -> Option<DocString> {
|
pub fn check_var_docs(&mut self, root: &LinkedNode) -> Option<Arc<DocString>> {
|
||||||
let lb = root.cast::<ast::LetBinding>()?;
|
let lb = root.cast::<ast::LetBinding>()?;
|
||||||
let first = lb.kind().bindings();
|
let first = lb.kind().bindings();
|
||||||
let documenting_id = first
|
let documenting_id = first
|
||||||
|
|
@ -38,35 +38,40 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
root: &LinkedNode,
|
root: &LinkedNode,
|
||||||
kind: DocStringKind,
|
kind: DocStringKind,
|
||||||
base_id: DefId,
|
base_id: DefId,
|
||||||
) -> Option<DocString> {
|
) -> Option<Arc<DocString>> {
|
||||||
// todo: cache docs capture
|
// todo: cache docs capture
|
||||||
// use parent of params, todo: reliable way to get the def target
|
// use parent of params, todo: reliable way to get the def target
|
||||||
let def = get_non_strict_def_target(root.clone())?;
|
let def = get_non_strict_def_target(root.clone())?;
|
||||||
let docs = find_docs_of(&self.source, def)?;
|
let docs = find_docs_of(&self.source, def)?;
|
||||||
|
|
||||||
let docstring = self.ctx.compute_docstring(root.span().id()?, docs, kind)?;
|
let docstring = self.ctx.compute_docstring(root.span().id()?, docs, kind)?;
|
||||||
Some(docstring.take().rename_based_on(base_id, self))
|
let res = Arc::new(docstring.take().rename_based_on(base_id, self));
|
||||||
|
self.info.var_docs.insert(base_id, res.clone());
|
||||||
|
Some(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The documentation string of an item
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub(crate) struct DocString {
|
pub struct DocString {
|
||||||
/// The documentation of the item
|
/// The documentation of the item
|
||||||
pub docs: Option<String>,
|
pub docs: Option<EcoString>,
|
||||||
/// The typing on definitions
|
/// The typing on definitions
|
||||||
pub var_bounds: HashMap<DefId, TypeVarBounds>,
|
pub var_bounds: HashMap<DefId, TypeVarBounds>,
|
||||||
/// The variable doc associated with the item
|
/// The variable doc associated with the item
|
||||||
pub vars: HashMap<EcoString, VarDoc>,
|
pub vars: BTreeMap<StrRef, VarDoc>,
|
||||||
/// The type of the resultant type
|
/// The type of the resultant type
|
||||||
pub res_ty: Option<Ty>,
|
pub res_ty: Option<Ty>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocString {
|
impl DocString {
|
||||||
pub fn get_var(&self, name: &str) -> Option<&VarDoc> {
|
/// Get the documentation of a variable associated with the item
|
||||||
|
pub fn get_var(&self, name: &StrRef) -> Option<&VarDoc> {
|
||||||
self.vars.get(name)
|
self.vars.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn var_ty(&self, name: &str) -> Option<&Ty> {
|
/// Get the type of a variable associated with the item
|
||||||
|
pub fn var_ty(&self, name: &StrRef) -> Option<&Ty> {
|
||||||
self.get_var(name).and_then(|v| v.ty.as_ref())
|
self.get_var(name).and_then(|v| v.ty.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,11 +110,15 @@ impl DocString {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The documentation string of a variable associated with some item.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub(crate) struct VarDoc {
|
pub struct VarDoc {
|
||||||
pub _docs: Option<EcoString>,
|
/// The documentation of the variable
|
||||||
|
pub docs: Option<EcoString>,
|
||||||
|
/// The type of the variable
|
||||||
pub ty: Option<Ty>,
|
pub ty: Option<Ty>,
|
||||||
pub _default: Option<EcoString>,
|
/// The default value of the variable
|
||||||
|
pub default: Option<EcoString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_docstring(
|
pub(crate) fn compute_docstring(
|
||||||
|
|
@ -129,6 +138,10 @@ pub(crate) fn compute_docstring(
|
||||||
match kind {
|
match kind {
|
||||||
DocStringKind::Function => checker.check_func_docs(docs),
|
DocStringKind::Function => checker.check_func_docs(docs),
|
||||||
DocStringKind::Variable => checker.check_var_docs(docs),
|
DocStringKind::Variable => checker.check_var_docs(docs),
|
||||||
|
DocStringKind::Module => None,
|
||||||
|
DocStringKind::Constant => None,
|
||||||
|
DocStringKind::Struct => None,
|
||||||
|
DocStringKind::Reference => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,14 +161,14 @@ impl<'a, 'w> DocsChecker<'a, 'w> {
|
||||||
let converted = identify_func_docs(&converted).ok()?;
|
let converted = identify_func_docs(&converted).ok()?;
|
||||||
let module = self.ctx.module_by_str(docs)?;
|
let module = self.ctx.module_by_str(docs)?;
|
||||||
|
|
||||||
let mut params = HashMap::new();
|
let mut params = BTreeMap::new();
|
||||||
for param in converted.params.into_iter() {
|
for param in converted.params.into_iter() {
|
||||||
params.insert(
|
params.insert(
|
||||||
param.name,
|
param.name.into(),
|
||||||
VarDoc {
|
VarDoc {
|
||||||
_docs: Some(param.docs),
|
docs: Some(param.docs),
|
||||||
ty: self.check_type_strings(&module, ¶m.types),
|
ty: self.check_type_strings(&module, ¶m.types),
|
||||||
_default: param.default,
|
default: param.default,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +187,7 @@ impl<'a, 'w> DocsChecker<'a, 'w> {
|
||||||
|
|
||||||
pub fn check_var_docs(mut self, docs: String) -> Option<DocString> {
|
pub fn check_var_docs(mut self, docs: String) -> Option<DocString> {
|
||||||
let converted = convert_docs(self.ctx.world(), &docs).ok()?;
|
let converted = convert_docs(self.ctx.world(), &docs).ok()?;
|
||||||
let converted = identify_var_docs(&converted).ok()?;
|
let converted = identify_var_docs(converted).ok()?;
|
||||||
let module = self.ctx.module_by_str(docs)?;
|
let module = self.ctx.module_by_str(docs)?;
|
||||||
|
|
||||||
let res_ty = converted
|
let res_ty = converted
|
||||||
|
|
@ -184,7 +197,7 @@ impl<'a, 'w> DocsChecker<'a, 'w> {
|
||||||
Some(DocString {
|
Some(DocString {
|
||||||
docs: Some(converted.docs),
|
docs: Some(converted.docs),
|
||||||
var_bounds: self.vars,
|
var_bounds: self.vars,
|
||||||
vars: HashMap::new(),
|
vars: BTreeMap::new(),
|
||||||
res_ty,
|
res_ty,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,11 +189,13 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
range: root.range(),
|
range: root.range(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.get_var(root.span(), ident_ref).or_else(|| {
|
self.get_var(root.span(), ident_ref)
|
||||||
let s = root.span();
|
.map(Ty::Var)
|
||||||
let v = resolve_global_value(self.ctx, root, mode == InterpretMode::Math)?;
|
.or_else(|| {
|
||||||
Some(Ty::Value(InsTy::new_at(v, s)))
|
let s = root.span();
|
||||||
})
|
let v = resolve_global_value(self.ctx, root, mode == InterpretMode::Math)?;
|
||||||
|
Some(Ty::Value(InsTy::new_at(v, s)))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_array(&mut self, root: LinkedNode<'_>) -> Option<Ty> {
|
fn check_array(&mut self, root: LinkedNode<'_>) -> Option<Ty> {
|
||||||
|
|
@ -373,7 +375,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
|
|
||||||
fn check_closure(&mut self, root: LinkedNode<'_>) -> Option<Ty> {
|
fn check_closure(&mut self, root: LinkedNode<'_>) -> Option<Ty> {
|
||||||
let docstring = self.check_func_docs(&root);
|
let docstring = self.check_func_docs(&root);
|
||||||
let docstring = docstring.as_ref().unwrap_or(&EMPTY_DOCSTRING);
|
let docstring = docstring.as_deref().unwrap_or(&EMPTY_DOCSTRING);
|
||||||
let closure: ast::Closure = root.cast()?;
|
let closure: ast::Closure = root.cast()?;
|
||||||
|
|
||||||
log::debug!("check closure: {:?} -> {docstring:#?}", closure.name());
|
log::debug!("check closure: {:?} -> {docstring:#?}", closure.name());
|
||||||
|
|
@ -389,24 +391,24 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
pos.push(self.check_pattern(pattern, Ty::Any, docstring, root.clone()));
|
pos.push(self.check_pattern(pattern, Ty::Any, docstring, root.clone()));
|
||||||
}
|
}
|
||||||
ast::Param::Named(e) => {
|
ast::Param::Named(e) => {
|
||||||
let name = e.name().get();
|
let name = e.name().get().into();
|
||||||
let exp = self.check_expr_in(e.expr().span(), root.clone());
|
let exp = self.check_expr_in(e.expr().span(), root.clone());
|
||||||
let v = self.get_var(e.name().span(), to_ident_ref(&root, e.name())?)?;
|
let v = Ty::Var(self.get_var(e.name().span(), to_ident_ref(&root, e.name())?)?);
|
||||||
if let Some(annotated) = docstring.var_ty(name.as_str()) {
|
if let Some(annotated) = docstring.var_ty(&name) {
|
||||||
self.constrain(&v, annotated);
|
self.constrain(&v, annotated);
|
||||||
}
|
}
|
||||||
// todo: this is less efficient than v.lbs.push(exp), we may have some idea to
|
// todo: this is less efficient than v.lbs.push(exp), we may have some idea to
|
||||||
// optimize it, so I put a todo here.
|
// optimize it, so I put a todo here.
|
||||||
self.constrain(&exp, &v);
|
self.constrain(&exp, &v);
|
||||||
named.insert(name.into(), v);
|
named.insert(name.clone(), v);
|
||||||
defaults.insert(name.into(), exp);
|
defaults.insert(name, exp);
|
||||||
}
|
}
|
||||||
// todo: spread left/right
|
// todo: spread left/right
|
||||||
ast::Param::Spread(a) => {
|
ast::Param::Spread(a) => {
|
||||||
if let Some(e) = a.sink_ident() {
|
if let Some(e) = a.sink_ident() {
|
||||||
let exp = Ty::Builtin(BuiltinTy::Args);
|
let exp = Ty::Builtin(BuiltinTy::Args);
|
||||||
let v = self.get_var(e.span(), to_ident_ref(&root, e)?)?;
|
let v = Ty::Var(self.get_var(e.span(), to_ident_ref(&root, e)?)?);
|
||||||
if let Some(annotated) = docstring.var_ty(e.get().as_str()) {
|
if let Some(annotated) = docstring.var_ty(&e.get().as_str().into()) {
|
||||||
self.constrain(&v, annotated);
|
self.constrain(&v, annotated);
|
||||||
}
|
}
|
||||||
self.constrain(&exp, &v);
|
self.constrain(&exp, &v);
|
||||||
|
|
@ -467,13 +469,13 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
.map(|init| self.check_expr_in(init.span(), root.clone()))
|
.map(|init| self.check_expr_in(init.span(), root.clone()))
|
||||||
.unwrap_or_else(|| Ty::Builtin(BuiltinTy::Infer));
|
.unwrap_or_else(|| Ty::Builtin(BuiltinTy::Infer));
|
||||||
|
|
||||||
let v = self.get_var(c.span(), to_ident_ref(&root, c)?)?;
|
let v = Ty::Var(self.get_var(c.span(), to_ident_ref(&root, c)?)?);
|
||||||
self.constrain(&value, &v);
|
self.constrain(&value, &v);
|
||||||
// todo lbs is the lexical signature.
|
// todo lbs is the lexical signature.
|
||||||
}
|
}
|
||||||
ast::LetBindingKind::Normal(pattern) => {
|
ast::LetBindingKind::Normal(pattern) => {
|
||||||
let docstring = self.check_var_docs(&root);
|
let docstring = self.check_var_docs(&root);
|
||||||
let docstring = docstring.as_ref().unwrap_or(&EMPTY_DOCSTRING);
|
let docstring = docstring.as_deref().unwrap_or(&EMPTY_DOCSTRING);
|
||||||
|
|
||||||
let value = let_binding
|
let value = let_binding
|
||||||
.init()
|
.init()
|
||||||
|
|
@ -617,14 +619,15 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
) -> Option<Ty> {
|
) -> Option<Ty> {
|
||||||
Some(match pattern {
|
Some(match pattern {
|
||||||
ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
|
ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
|
||||||
let v = self.get_var(ident.span(), to_ident_ref(&root, ident)?)?;
|
let var = self.get_var(ident.span(), to_ident_ref(&root, ident)?)?;
|
||||||
let annotated = docs.var_ty(ident.get().as_str());
|
let annotated = docs.var_ty(&var.name);
|
||||||
|
let var = Ty::Var(var);
|
||||||
log::debug!("check pattern: {ident:?} with {value:?} and annotation {annotated:?}");
|
log::debug!("check pattern: {ident:?} with {value:?} and annotation {annotated:?}");
|
||||||
if let Some(annotated) = annotated {
|
if let Some(annotated) = annotated {
|
||||||
self.constrain(&v, annotated);
|
self.constrain(&var, annotated);
|
||||||
}
|
}
|
||||||
self.constrain(&value, &v);
|
self.constrain(&value, &var);
|
||||||
v
|
var
|
||||||
}
|
}
|
||||||
ast::Pattern::Normal(_) => Ty::Any,
|
ast::Pattern::Normal(_) => Ty::Any,
|
||||||
ast::Pattern::Placeholder(_) => Ty::Any,
|
ast::Pattern::Placeholder(_) => Ty::Any,
|
||||||
|
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
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;
|
|
||||||
use typst::{
|
|
||||||
diag::StrResult,
|
|
||||||
syntax::{FileId, VirtualPath},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::docs::library;
|
|
||||||
|
|
||||||
use super::tidy::*;
|
|
||||||
|
|
||||||
/// Kind of a docstring.
|
|
||||||
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
|
|
||||||
pub enum DocStringKind {
|
|
||||||
/// A docstring for a function.
|
|
||||||
Function,
|
|
||||||
/// A docstring for a variable.
|
|
||||||
Variable,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Docs about a symbol.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
#[serde(tag = "kind")]
|
|
||||||
pub enum RawDocs {
|
|
||||||
/// Docs about a function.
|
|
||||||
#[serde(rename = "func")]
|
|
||||||
Function(TidyFuncDocs),
|
|
||||||
/// Docs about a variable.
|
|
||||||
#[serde(rename = "var")]
|
|
||||||
Variable(TidyVarDocs),
|
|
||||||
/// Docs about a module.
|
|
||||||
#[serde(rename = "module")]
|
|
||||||
Module(TidyModuleDocs),
|
|
||||||
/// Other kinds of docs.
|
|
||||||
#[serde(rename = "plain")]
|
|
||||||
Plain(EcoString),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RawDocs {
|
|
||||||
/// Get the markdown representation of the docs.
|
|
||||||
pub fn docs(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::Function(docs) => docs.docs.as_str(),
|
|
||||||
Self::Variable(docs) => docs.docs.as_str(),
|
|
||||||
Self::Module(docs) => docs.docs.as_str(),
|
|
||||||
Self::Plain(docs) => docs.as_str(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unfortunately, we have only 65536 possible file ids and we cannot revoke
|
|
||||||
// them. So we share a global file id for all docs conversion.
|
|
||||||
static DOCS_CONVERT_ID: std::sync::LazyLock<Mutex<FileId>> = std::sync::LazyLock::new(|| {
|
|
||||||
Mutex::new(FileId::new(None, VirtualPath::new("__tinymist_docs__.typ")))
|
|
||||||
});
|
|
||||||
|
|
||||||
pub(crate) fn convert_docs(world: &LspWorld, content: &str) -> StrResult<EcoString> {
|
|
||||||
static DOCS_LIB: std::sync::LazyLock<Arc<typlite::scopes::Scopes<typlite::value::Value>>> =
|
|
||||||
std::sync::LazyLock::new(library::lib);
|
|
||||||
|
|
||||||
let conv_id = DOCS_CONVERT_ID.lock();
|
|
||||||
let entry = EntryState::new_rootless(conv_id.vpath().as_rooted_path().into()).unwrap();
|
|
||||||
let entry = entry.select_in_workspace(*conv_id);
|
|
||||||
|
|
||||||
let mut w = world.task(TaskInputs {
|
|
||||||
entry: Some(entry),
|
|
||||||
inputs: None,
|
|
||||||
});
|
|
||||||
w.map_shadow_by_id(*conv_id, Bytes::from(content.as_bytes().to_owned()))?;
|
|
||||||
// todo: bad performance
|
|
||||||
w.source_db.take_state();
|
|
||||||
|
|
||||||
let conv = typlite::Typlite::new(Arc::new(w))
|
|
||||||
.with_library(DOCS_LIB.clone())
|
|
||||||
.annotate_elements(true)
|
|
||||||
.convert()
|
|
||||||
.map_err(|e| eco_format!("failed to convert to markdown: {e}"))?;
|
|
||||||
|
|
||||||
Ok(conv)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn identify_docs(kind: &str, content: &str) -> StrResult<RawDocs> {
|
|
||||||
match kind {
|
|
||||||
"function" => identify_func_docs(content).map(RawDocs::Function),
|
|
||||||
"variable" => identify_var_docs(content).map(RawDocs::Variable),
|
|
||||||
"module" => identify_tidy_module_docs(content).map(RawDocs::Module),
|
|
||||||
_ => Err(eco_format!("unknown kind {kind}")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +1,17 @@
|
||||||
//! Documentation generation utilities.
|
//! Documentation generation utilities.
|
||||||
|
|
||||||
mod docstring;
|
|
||||||
mod library;
|
mod library;
|
||||||
mod module;
|
mod module;
|
||||||
mod package;
|
mod package;
|
||||||
mod signature;
|
mod symbol;
|
||||||
mod tidy;
|
mod tidy;
|
||||||
|
|
||||||
use ecow::EcoString;
|
|
||||||
use reflexo::path::unix_slash;
|
use reflexo::path::unix_slash;
|
||||||
use typst::{foundations::Value, syntax::FileId};
|
use typst::{foundations::Value, syntax::FileId};
|
||||||
|
|
||||||
pub use docstring::*;
|
|
||||||
pub use module::*;
|
pub use module::*;
|
||||||
pub use package::*;
|
pub use package::*;
|
||||||
pub use signature::*;
|
pub use symbol::*;
|
||||||
pub(crate) use tidy::*;
|
pub(crate) use tidy::*;
|
||||||
|
|
||||||
fn file_id_repr(k: FileId) -> String {
|
fn file_id_repr(k: FileId) -> String {
|
||||||
|
|
@ -25,13 +22,12 @@ fn file_id_repr(k: FileId) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind_of(val: &Value) -> EcoString {
|
fn kind_of(val: &Value) -> DocStringKind {
|
||||||
match val {
|
match val {
|
||||||
Value::Module(_) => "module",
|
Value::Module(_) => DocStringKind::Module,
|
||||||
Value::Type(_) => "struct",
|
Value::Type(_) => DocStringKind::Struct,
|
||||||
Value::Func(_) => "function",
|
Value::Func(_) => DocStringKind::Function,
|
||||||
Value::Label(_) => "reference",
|
Value::Label(_) => DocStringKind::Reference,
|
||||||
_ => "constant",
|
_ => DocStringKind::Constant,
|
||||||
}
|
}
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use crate::syntax::{find_docs_of, get_non_strict_def_target};
|
||||||
use crate::upstream::truncated_doc_repr;
|
use crate::upstream::truncated_doc_repr;
|
||||||
use crate::AnalysisContext;
|
use crate::AnalysisContext;
|
||||||
|
|
||||||
use super::{get_manifest, get_manifest_id, kind_of, PackageInfo, RawDocs, SignatureDocs};
|
use super::{get_manifest, get_manifest_id, kind_of, DocStringKind, PackageInfo, SymbolDocs};
|
||||||
|
|
||||||
/// Get documentation of symbols in a package.
|
/// Get documentation of symbols in a package.
|
||||||
pub fn package_module_docs(ctx: &mut AnalysisContext, pkg: &PackageInfo) -> StrResult<SymbolsInfo> {
|
pub fn package_module_docs(ctx: &mut AnalysisContext, pkg: &PackageInfo) -> StrResult<SymbolsInfo> {
|
||||||
|
|
@ -70,7 +70,7 @@ pub struct SymbolInfoHead {
|
||||||
/// The name of the symbol.
|
/// The name of the symbol.
|
||||||
pub name: EcoString,
|
pub name: EcoString,
|
||||||
/// The kind of the symbol.
|
/// The kind of the symbol.
|
||||||
pub kind: EcoString,
|
pub kind: DocStringKind,
|
||||||
/// The location (file, start, end) of the symbol.
|
/// The location (file, start, end) of the symbol.
|
||||||
pub loc: Option<(usize, usize, usize)>,
|
pub loc: Option<(usize, usize, usize)>,
|
||||||
/// Is the symbol reexport
|
/// Is the symbol reexport
|
||||||
|
|
@ -81,10 +81,8 @@ pub struct SymbolInfoHead {
|
||||||
pub oneliner: Option<String>,
|
pub oneliner: Option<String>,
|
||||||
/// The raw documentation of the symbol.
|
/// The raw documentation of the symbol.
|
||||||
pub docs: Option<String>,
|
pub docs: Option<String>,
|
||||||
/// The signature of the symbol.
|
|
||||||
pub signature: Option<SignatureDocs>,
|
|
||||||
/// The parsed documentation of the symbol.
|
/// The parsed documentation of the symbol.
|
||||||
pub parsed_docs: Option<RawDocs>,
|
pub parsed_docs: Option<SymbolDocs>,
|
||||||
/// The value of the symbol.
|
/// The value of the symbol.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub constant: Option<EcoString>,
|
pub constant: Option<EcoString>,
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,7 @@ use typst::syntax::package::{PackageManifest, PackageSpec};
|
||||||
use typst::syntax::{FileId, Span, VirtualPath};
|
use typst::syntax::{FileId, Span, VirtualPath};
|
||||||
use typst::World;
|
use typst::World;
|
||||||
|
|
||||||
use crate::docs::{
|
use crate::docs::{file_id_repr, module_docs, symbol_docs, SymbolDocs, SymbolsInfo};
|
||||||
convert_docs, file_id_repr, identify_docs, module_docs, signature_docs, RawDocs, SymbolsInfo,
|
|
||||||
};
|
|
||||||
use crate::syntax::IdentRef;
|
use crate::syntax::IdentRef;
|
||||||
use crate::ty::Ty;
|
use crate::ty::Ty;
|
||||||
use crate::AnalysisContext;
|
use crate::AnalysisContext;
|
||||||
|
|
@ -159,33 +157,32 @@ pub fn package_docs(
|
||||||
});
|
});
|
||||||
sym.head.loc = span;
|
sym.head.loc = span;
|
||||||
|
|
||||||
let sym_value = sym.head.value.clone();
|
let def_ident = sym.head.name_range.as_ref().map(|range| IdentRef {
|
||||||
let signature = sym_value.and_then(|e| {
|
name: sym.head.name.clone(),
|
||||||
let def_ident = IdentRef {
|
range: range.clone(),
|
||||||
name: sym.head.name.clone(),
|
|
||||||
range: sym.head.name_range.clone()?,
|
|
||||||
};
|
|
||||||
signature_docs(ctx, type_info, Some(&def_ident), &e, Some(&mut doc_ty))
|
|
||||||
});
|
});
|
||||||
sym.head.signature = signature;
|
let docs = symbol_docs(
|
||||||
|
ctx,
|
||||||
|
type_info,
|
||||||
|
sym.head.kind,
|
||||||
|
def_ident.as_ref(),
|
||||||
|
sym.head.value.as_ref(),
|
||||||
|
sym.head.docs.as_deref(),
|
||||||
|
Some(&mut doc_ty),
|
||||||
|
);
|
||||||
|
|
||||||
let mut convert_err = None;
|
let mut convert_err = None;
|
||||||
if let Some(docs) = &sym.head.docs {
|
match &docs {
|
||||||
match convert_docs(world, docs) {
|
Ok(docs) => {
|
||||||
Ok(content) => {
|
sym.head.parsed_docs = Some(docs.clone());
|
||||||
let docs = identify_docs(sym.head.kind.as_str(), &content)
|
sym.head.docs = None;
|
||||||
.unwrap_or(RawDocs::Plain(content));
|
}
|
||||||
|
Err(e) => {
|
||||||
sym.head.parsed_docs = Some(docs.clone());
|
let err = format!("failed to convert docs in {title}: {e}").replace(
|
||||||
sym.head.docs = None;
|
"-->", "—>", // avoid markdown comment
|
||||||
}
|
);
|
||||||
Err(e) => {
|
log::error!("{err}");
|
||||||
let err = format!("failed to convert docs in {title}: {e}").replace(
|
convert_err = Some(err);
|
||||||
"-->", "—>", // avoid markdown comment
|
|
||||||
);
|
|
||||||
log::error!("{err}");
|
|
||||||
convert_err = Some(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,7 +224,7 @@ pub fn package_docs(
|
||||||
let head = jbase64(&sym.head);
|
let head = jbase64(&sym.head);
|
||||||
let _ = writeln!(md, "<!-- begin:symbol {ident} {head} -->");
|
let _ = writeln!(md, "<!-- begin:symbol {ident} {head} -->");
|
||||||
|
|
||||||
if let Some(sig) = &sym.head.signature {
|
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 _ = writeln!(md, "let {name}({sig});", name = sym.head.name);
|
||||||
|
|
@ -245,13 +242,18 @@ pub fn package_docs(
|
||||||
}
|
}
|
||||||
(Some(docs), _) => {
|
(Some(docs), _) => {
|
||||||
let _ = writeln!(md, "{}", remove_list_annotations(docs.docs()));
|
let _ = writeln!(md, "{}", remove_list_annotations(docs.docs()));
|
||||||
if let RawDocs::Function(f) = docs {
|
if let SymbolDocs::Function(f) = docs {
|
||||||
for param in &f.params {
|
for param in f.pos.iter().chain(f.named.values()).chain(f.rest.as_ref())
|
||||||
|
{
|
||||||
let _ = writeln!(md, "<!-- begin:param {} -->", param.name);
|
let _ = writeln!(md, "<!-- begin:param {} -->", param.name);
|
||||||
|
let ty = match ¶m.cano_type {
|
||||||
|
Some((short, _)) => short,
|
||||||
|
None => "unknown",
|
||||||
|
};
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
md,
|
md,
|
||||||
"#### {} ({})\n<!-- begin:param-doc {} -->\n{}\n<!-- end:param-doc {} -->",
|
"#### {} ({ty:?})\n<!-- begin:param-doc {} -->\n{}\n<!-- end:param-doc {} -->",
|
||||||
param.name, param.types, param.name, param.docs, param.name
|
param.name, param.name, param.docs, param.name
|
||||||
);
|
);
|
||||||
let _ = writeln!(md, "<!-- end:param -->");
|
let _ = writeln!(md, "<!-- end:param -->");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
use std::fmt;
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
|
||||||
|
|
||||||
use ecow::EcoString;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use typst::foundations::Value;
|
|
||||||
|
|
||||||
use crate::analysis::analyze_dyn_signature;
|
|
||||||
use crate::syntax::IdentRef;
|
|
||||||
use crate::{ty::Ty, AnalysisContext};
|
|
||||||
|
|
||||||
type TypeRepr = Option<(/* short */ String, /* long */ String)>;
|
|
||||||
type ShowTypeRepr<'a> = &'a mut dyn FnMut(Option<&Ty>) -> TypeRepr;
|
|
||||||
|
|
||||||
/// Describes a primary function signature.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct SignatureDocs {
|
|
||||||
/// The positional parameters.
|
|
||||||
pub pos: Vec<ParamDocs>,
|
|
||||||
/// The named parameters.
|
|
||||||
pub named: HashMap<String, ParamDocs>,
|
|
||||||
/// The rest parameter.
|
|
||||||
pub rest: Option<ParamDocs>,
|
|
||||||
/// The return type.
|
|
||||||
pub ret_ty: TypeRepr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for SignatureDocs {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let mut is_first = true;
|
|
||||||
let mut write_sep = |f: &mut fmt::Formatter<'_>| {
|
|
||||||
if is_first {
|
|
||||||
is_first = false;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
f.write_str(", ")
|
|
||||||
};
|
|
||||||
|
|
||||||
for p in &self.pos {
|
|
||||||
write_sep(f)?;
|
|
||||||
f.write_str(&p.name)?;
|
|
||||||
if let Some(t) = &p.cano_type {
|
|
||||||
write!(f, ": {}", t.0)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(rest) = &self.rest {
|
|
||||||
write_sep(f)?;
|
|
||||||
f.write_str("..")?;
|
|
||||||
f.write_str(&rest.name)?;
|
|
||||||
if let Some(t) = &rest.cano_type {
|
|
||||||
write!(f, ": {}", t.0)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.named.is_empty() {
|
|
||||||
let mut name_prints = vec![];
|
|
||||||
for v in self.named.values() {
|
|
||||||
let ty = v.cano_type.as_ref().map(|t| &t.0);
|
|
||||||
name_prints.push((v.name.clone(), ty, v.expr.clone()))
|
|
||||||
}
|
|
||||||
name_prints.sort();
|
|
||||||
for (k, t, v) in name_prints {
|
|
||||||
write_sep(f)?;
|
|
||||||
let v = v.as_deref().unwrap_or("any");
|
|
||||||
let mut v = v.trim();
|
|
||||||
if v.starts_with('{') && v.ends_with('}') && v.len() > 30 {
|
|
||||||
v = "{ ... }"
|
|
||||||
}
|
|
||||||
if v.starts_with('`') && v.ends_with('`') && v.len() > 30 {
|
|
||||||
v = "raw"
|
|
||||||
}
|
|
||||||
if v.starts_with('[') && v.ends_with(']') && v.len() > 30 {
|
|
||||||
v = "content"
|
|
||||||
}
|
|
||||||
f.write_str(&k)?;
|
|
||||||
if let Some(t) = t {
|
|
||||||
write!(f, ": {t}")?;
|
|
||||||
}
|
|
||||||
write!(f, " = {v}")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes a function parameter.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct ParamDocs {
|
|
||||||
/// The parameter's name.
|
|
||||||
pub name: String,
|
|
||||||
/// Documentation for the parameter.
|
|
||||||
pub docs: String,
|
|
||||||
/// Inferred type of the parameter.
|
|
||||||
pub cano_type: TypeRepr,
|
|
||||||
/// The parameter's default name as value.
|
|
||||||
pub expr: Option<EcoString>,
|
|
||||||
/// Is the parameter positional?
|
|
||||||
pub positional: bool,
|
|
||||||
/// Is the parameter named?
|
|
||||||
///
|
|
||||||
/// Can be true even if `positional` is true if the parameter can be given
|
|
||||||
/// in both variants.
|
|
||||||
pub named: bool,
|
|
||||||
/// Can the parameter be given any number of times?
|
|
||||||
pub variadic: bool,
|
|
||||||
/// Is the parameter settable with a set rule?
|
|
||||||
pub settable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
type TypeInfo = (Arc<crate::analysis::DefUseInfo>, Arc<crate::ty::TypeScheme>);
|
|
||||||
|
|
||||||
pub(crate) fn signature_docs(
|
|
||||||
ctx: &mut AnalysisContext,
|
|
||||||
type_info: Option<&TypeInfo>,
|
|
||||||
def_ident: Option<&IdentRef>,
|
|
||||||
runtime_fn: &Value,
|
|
||||||
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 sig = analyze_dyn_signature(ctx, func.clone());
|
|
||||||
let type_sig = type_info.and_then(|(def_use, ty_chk)| {
|
|
||||||
let def_fid = func.span().id()?;
|
|
||||||
let (def_id, _) = def_use.get_def(def_fid, def_ident?)?;
|
|
||||||
ty_chk.type_of_def(def_id)
|
|
||||||
});
|
|
||||||
let type_sig = type_sig.and_then(|type_sig| type_sig.sig_repr(true));
|
|
||||||
|
|
||||||
const F: fn(Option<&Ty>) -> TypeRepr = |ty: Option<&Ty>| {
|
|
||||||
ty.and_then(|ty| ty.describe())
|
|
||||||
.map(|short| (short, format!("{ty:?}")))
|
|
||||||
};
|
|
||||||
let mut binding = F;
|
|
||||||
let doc_ty = doc_ty.unwrap_or(&mut binding);
|
|
||||||
let pos_in = sig
|
|
||||||
.primary()
|
|
||||||
.pos
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, pos)| (pos, type_sig.as_ref().and_then(|sig| sig.pos(i))));
|
|
||||||
let named_in = sig
|
|
||||||
.primary()
|
|
||||||
.named
|
|
||||||
.iter()
|
|
||||||
.map(|x| (x, type_sig.as_ref().and_then(|sig| sig.named(x.0))));
|
|
||||||
let rest_in = sig
|
|
||||||
.primary()
|
|
||||||
.rest
|
|
||||||
.as_ref()
|
|
||||||
.map(|x| (x, type_sig.as_ref().and_then(|sig| sig.rest_param())));
|
|
||||||
|
|
||||||
let ret_in = type_sig
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|sig| sig.body.as_ref())
|
|
||||||
.or_else(|| sig.primary().ret_ty.as_ref());
|
|
||||||
|
|
||||||
let pos = pos_in
|
|
||||||
.map(|(param, ty)| ParamDocs {
|
|
||||||
name: param.name.as_ref().to_owned(),
|
|
||||||
docs: param.docs.as_ref().to_owned(),
|
|
||||||
cano_type: doc_ty(ty.or(Some(¶m.base_type))),
|
|
||||||
expr: param.expr.clone(),
|
|
||||||
positional: param.positional,
|
|
||||||
named: param.named,
|
|
||||||
variadic: param.variadic,
|
|
||||||
settable: param.settable,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let named = named_in
|
|
||||||
.map(|((name, param), ty)| {
|
|
||||||
(
|
|
||||||
name.as_ref().to_owned(),
|
|
||||||
ParamDocs {
|
|
||||||
name: param.name.as_ref().to_owned(),
|
|
||||||
docs: param.docs.as_ref().to_owned(),
|
|
||||||
cano_type: doc_ty(ty.or(Some(¶m.base_type))),
|
|
||||||
expr: param.expr.clone(),
|
|
||||||
positional: param.positional,
|
|
||||||
named: param.named,
|
|
||||||
variadic: param.variadic,
|
|
||||||
settable: param.settable,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let rest = rest_in.map(|(param, ty)| ParamDocs {
|
|
||||||
name: param.name.as_ref().to_owned(),
|
|
||||||
docs: param.docs.as_ref().to_owned(),
|
|
||||||
cano_type: doc_ty(ty.or(Some(¶m.base_type))),
|
|
||||||
expr: param.expr.clone(),
|
|
||||||
positional: param.positional,
|
|
||||||
named: param.named,
|
|
||||||
variadic: param.variadic,
|
|
||||||
settable: param.settable,
|
|
||||||
});
|
|
||||||
|
|
||||||
let ret_ty = doc_ty(ret_in);
|
|
||||||
|
|
||||||
Some(SignatureDocs {
|
|
||||||
pos,
|
|
||||||
named,
|
|
||||||
rest,
|
|
||||||
ret_ty,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
375
crates/tinymist-query/src/docs/symbol.rs
Normal file
375
crates/tinymist-query/src/docs/symbol.rs
Normal file
|
|
@ -0,0 +1,375 @@
|
||||||
|
use core::fmt;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
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::{
|
||||||
|
diag::StrResult,
|
||||||
|
syntax::{FileId, VirtualPath},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::tidy::*;
|
||||||
|
use crate::analysis::{analyze_dyn_signature, ParamSpec};
|
||||||
|
use crate::docs::library;
|
||||||
|
use crate::syntax::IdentRef;
|
||||||
|
use crate::{ty::Ty, AnalysisContext};
|
||||||
|
|
||||||
|
type TypeRepr = Option<(/* short */ String, /* long */ String)>;
|
||||||
|
type ShowTypeRepr<'a> = &'a mut dyn FnMut(Option<&Ty>) -> TypeRepr;
|
||||||
|
|
||||||
|
/// Kind of a docstring.
|
||||||
|
#[derive(Debug, Default, Clone, Copy, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum DocStringKind {
|
||||||
|
/// A docstring for a any constant.
|
||||||
|
#[default]
|
||||||
|
Constant,
|
||||||
|
/// A docstring for a function.
|
||||||
|
Function,
|
||||||
|
/// A docstring for a variable.
|
||||||
|
Variable,
|
||||||
|
/// A docstring for a module.
|
||||||
|
Module,
|
||||||
|
/// A docstring for a struct.
|
||||||
|
Struct,
|
||||||
|
/// A docstring for a reference.
|
||||||
|
Reference,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DocStringKind {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Constant => write!(f, "constant"),
|
||||||
|
Self::Function => write!(f, "function"),
|
||||||
|
Self::Variable => write!(f, "variable"),
|
||||||
|
Self::Module => write!(f, "module"),
|
||||||
|
Self::Struct => write!(f, "struct"),
|
||||||
|
Self::Reference => write!(f, "reference"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Documentation about a symbol.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "kind")]
|
||||||
|
pub enum SymbolDocs {
|
||||||
|
/// Documentation about a function.
|
||||||
|
#[serde(rename = "func")]
|
||||||
|
Function(Box<SignatureDocs>),
|
||||||
|
/// Documentation about a variable.
|
||||||
|
#[serde(rename = "var")]
|
||||||
|
Variable(TidyVarDocs),
|
||||||
|
/// Documentation about a module.
|
||||||
|
#[serde(rename = "module")]
|
||||||
|
Module(TidyModuleDocs),
|
||||||
|
/// Other kinds of documentation.
|
||||||
|
#[serde(rename = "plain")]
|
||||||
|
Plain {
|
||||||
|
/// The content of the documentation.
|
||||||
|
docs: EcoString,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolDocs {
|
||||||
|
/// Get the markdown representation of the documentation.
|
||||||
|
pub fn docs(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Function(docs) => docs.docs.as_str(),
|
||||||
|
Self::Variable(docs) => docs.docs.as_str(),
|
||||||
|
Self::Module(docs) => docs.docs.as_str(),
|
||||||
|
Self::Plain { docs } => docs.as_str(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn symbol_docs(
|
||||||
|
ctx: &mut AnalysisContext,
|
||||||
|
type_info: Option<&TypeInfo>,
|
||||||
|
kind: DocStringKind,
|
||||||
|
def_ident: Option<&IdentRef>,
|
||||||
|
sym_value: Option<&Value>,
|
||||||
|
docs: Option<&str>,
|
||||||
|
doc_ty: Option<ShowTypeRepr>,
|
||||||
|
) -> Result<SymbolDocs, String> {
|
||||||
|
let signature = sym_value.and_then(|e| signature_docs(ctx, type_info, def_ident, e, doc_ty));
|
||||||
|
if let Some(signature) = signature {
|
||||||
|
return Ok(SymbolDocs::Function(Box::new(signature)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(docs) = &docs {
|
||||||
|
match convert_docs(ctx.world(), docs) {
|
||||||
|
Ok(content) => {
|
||||||
|
let docs = identify_docs(kind, content.clone())
|
||||||
|
.unwrap_or(SymbolDocs::Plain { docs: content });
|
||||||
|
return Ok(docs);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let err = format!("failed to convert docs: {e}").replace(
|
||||||
|
"-->", "—>", // avoid markdown comment
|
||||||
|
);
|
||||||
|
log::error!("{err}");
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SymbolDocs::Plain { docs: "".into() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes a primary function signature.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SignatureDocs {
|
||||||
|
/// Documentation for the function.
|
||||||
|
pub docs: EcoString,
|
||||||
|
// pub return_ty: Option<EcoString>,
|
||||||
|
// pub params: Vec<TidyParamDocs>,
|
||||||
|
/// The positional parameters.
|
||||||
|
pub pos: Vec<ParamDocs>,
|
||||||
|
/// The named parameters.
|
||||||
|
pub named: BTreeMap<String, ParamDocs>,
|
||||||
|
/// The rest parameter.
|
||||||
|
pub rest: Option<ParamDocs>,
|
||||||
|
/// The return type.
|
||||||
|
pub ret_ty: TypeRepr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SignatureDocs {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let mut is_first = true;
|
||||||
|
let mut write_sep = |f: &mut fmt::Formatter<'_>| {
|
||||||
|
if is_first {
|
||||||
|
is_first = false;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
f.write_str(", ")
|
||||||
|
};
|
||||||
|
|
||||||
|
for p in &self.pos {
|
||||||
|
write_sep(f)?;
|
||||||
|
f.write_str(&p.name)?;
|
||||||
|
if let Some(t) = &p.cano_type {
|
||||||
|
write!(f, ": {}", t.0)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(rest) = &self.rest {
|
||||||
|
write_sep(f)?;
|
||||||
|
f.write_str("..")?;
|
||||||
|
f.write_str(&rest.name)?;
|
||||||
|
if let Some(t) = &rest.cano_type {
|
||||||
|
write!(f, ": {}", t.0)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.named.is_empty() {
|
||||||
|
let mut name_prints = vec![];
|
||||||
|
for v in self.named.values() {
|
||||||
|
let ty = v.cano_type.as_ref().map(|t| &t.0);
|
||||||
|
name_prints.push((v.name.clone(), ty, v.expr.clone()))
|
||||||
|
}
|
||||||
|
name_prints.sort();
|
||||||
|
for (k, t, v) in name_prints {
|
||||||
|
write_sep(f)?;
|
||||||
|
let v = v.as_deref().unwrap_or("any");
|
||||||
|
let mut v = v.trim();
|
||||||
|
if v.starts_with('{') && v.ends_with('}') && v.len() > 30 {
|
||||||
|
v = "{ ... }"
|
||||||
|
}
|
||||||
|
if v.starts_with('`') && v.ends_with('`') && v.len() > 30 {
|
||||||
|
v = "raw"
|
||||||
|
}
|
||||||
|
if v.starts_with('[') && v.ends_with(']') && v.len() > 30 {
|
||||||
|
v = "content"
|
||||||
|
}
|
||||||
|
f.write_str(&k)?;
|
||||||
|
if let Some(t) = t {
|
||||||
|
write!(f, ": {t}")?;
|
||||||
|
}
|
||||||
|
write!(f, " = {v}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes a function parameter.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ParamDocs {
|
||||||
|
/// The parameter's name.
|
||||||
|
pub name: String,
|
||||||
|
/// Documentation for the parameter.
|
||||||
|
pub docs: String,
|
||||||
|
/// Inferred type of the parameter.
|
||||||
|
pub cano_type: TypeRepr,
|
||||||
|
/// The parameter's default name as value.
|
||||||
|
pub expr: Option<EcoString>,
|
||||||
|
/// Is the parameter positional?
|
||||||
|
pub positional: bool,
|
||||||
|
/// Is the parameter named?
|
||||||
|
///
|
||||||
|
/// Can be true even if `positional` is true if the parameter can be given
|
||||||
|
/// in both variants.
|
||||||
|
pub named: bool,
|
||||||
|
/// Can the parameter be given any number of times?
|
||||||
|
pub variadic: bool,
|
||||||
|
/// Is the parameter settable with a set rule?
|
||||||
|
pub settable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParamDocs {
|
||||||
|
fn new(param: &ParamSpec, ty: Option<&Ty>, doc_ty: Option<&mut ShowTypeRepr>) -> Self {
|
||||||
|
Self {
|
||||||
|
name: param.name.as_ref().to_owned(),
|
||||||
|
docs: param.docs.as_ref().to_owned(),
|
||||||
|
cano_type: format_ty(ty.or(Some(¶m.base_type)), doc_ty),
|
||||||
|
expr: param.expr.clone(),
|
||||||
|
positional: param.positional,
|
||||||
|
named: param.named,
|
||||||
|
variadic: param.variadic,
|
||||||
|
settable: param.settable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeInfo = (Arc<crate::analysis::DefUseInfo>, Arc<crate::ty::TypeScheme>);
|
||||||
|
|
||||||
|
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())
|
||||||
|
.map(|short| (short, format!("{ty:?}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn signature_docs(
|
||||||
|
ctx: &mut AnalysisContext,
|
||||||
|
type_info: Option<&TypeInfo>,
|
||||||
|
def_ident: Option<&IdentRef>,
|
||||||
|
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 sig = analyze_dyn_signature(ctx, func.clone());
|
||||||
|
let def_id = type_info.and_then(|(def_use, _)| {
|
||||||
|
let def_fid = func.span().id()?;
|
||||||
|
let (def_id, _) = def_use.get_def(def_fid, def_ident?)?;
|
||||||
|
Some(def_id)
|
||||||
|
});
|
||||||
|
let docstring = type_info.and_then(|(_, ty_chk)| ty_chk.var_docs.get(&def_id?));
|
||||||
|
let type_sig = type_info.and_then(|(_, ty_chk)| ty_chk.type_of_def(def_id?));
|
||||||
|
let type_sig = type_sig.and_then(|type_sig| type_sig.sig_repr(true));
|
||||||
|
|
||||||
|
let pos_in = sig
|
||||||
|
.primary()
|
||||||
|
.pos
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, pos)| (pos, type_sig.as_ref().and_then(|sig| sig.pos(i))));
|
||||||
|
let named_in = sig
|
||||||
|
.primary()
|
||||||
|
.named
|
||||||
|
.iter()
|
||||||
|
.map(|x| (x, type_sig.as_ref().and_then(|sig| sig.named(x.0))));
|
||||||
|
let rest_in = sig
|
||||||
|
.primary()
|
||||||
|
.rest
|
||||||
|
.as_ref()
|
||||||
|
.map(|x| (x, type_sig.as_ref().and_then(|sig| sig.rest_param())));
|
||||||
|
|
||||||
|
let ret_in = type_sig
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|sig| sig.body.as_ref())
|
||||||
|
.or_else(|| sig.primary().sig_ty.body.as_ref());
|
||||||
|
|
||||||
|
let pos = pos_in
|
||||||
|
.map(|(param, ty)| ParamDocs::new(param, ty, doc_ty.as_mut()))
|
||||||
|
.collect();
|
||||||
|
let named = named_in
|
||||||
|
.map(|((name, param), ty)| {
|
||||||
|
(
|
||||||
|
name.as_ref().to_owned(),
|
||||||
|
ParamDocs::new(param, ty, doc_ty.as_mut()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let rest = rest_in.map(|(param, ty)| ParamDocs::new(param, ty, doc_ty.as_mut()));
|
||||||
|
|
||||||
|
let ret_ty = format_ty(ret_in, doc_ty.as_mut());
|
||||||
|
|
||||||
|
Some(SignatureDocs {
|
||||||
|
docs: docstring.and_then(|x| x.docs.clone()).unwrap_or_default(),
|
||||||
|
pos,
|
||||||
|
named,
|
||||||
|
rest,
|
||||||
|
ret_ty,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unfortunately, we have only 65536 possible file ids and we cannot revoke
|
||||||
|
// them. So we share a global file id for all docs conversion.
|
||||||
|
static DOCS_CONVERT_ID: std::sync::LazyLock<Mutex<FileId>> = std::sync::LazyLock::new(|| {
|
||||||
|
Mutex::new(FileId::new(None, VirtualPath::new("__tinymist_docs__.typ")))
|
||||||
|
});
|
||||||
|
|
||||||
|
pub(crate) fn convert_docs(world: &LspWorld, content: &str) -> StrResult<EcoString> {
|
||||||
|
static DOCS_LIB: std::sync::LazyLock<Arc<typlite::scopes::Scopes<typlite::value::Value>>> =
|
||||||
|
std::sync::LazyLock::new(library::lib);
|
||||||
|
|
||||||
|
let conv_id = DOCS_CONVERT_ID.lock();
|
||||||
|
let entry = EntryState::new_rootless(conv_id.vpath().as_rooted_path().into()).unwrap();
|
||||||
|
let entry = entry.select_in_workspace(*conv_id);
|
||||||
|
|
||||||
|
let mut w = world.task(TaskInputs {
|
||||||
|
entry: Some(entry),
|
||||||
|
inputs: None,
|
||||||
|
});
|
||||||
|
w.map_shadow_by_id(*conv_id, Bytes::from(content.as_bytes().to_owned()))?;
|
||||||
|
// todo: bad performance
|
||||||
|
w.source_db.take_state();
|
||||||
|
|
||||||
|
let conv = typlite::Typlite::new(Arc::new(w))
|
||||||
|
.with_library(DOCS_LIB.clone())
|
||||||
|
.annotate_elements(true)
|
||||||
|
.convert()
|
||||||
|
.map_err(|e| eco_format!("failed to convert to markdown: {e}"))?;
|
||||||
|
|
||||||
|
Ok(conv)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn identify_docs(kind: DocStringKind, docs: EcoString) -> StrResult<SymbolDocs> {
|
||||||
|
match kind {
|
||||||
|
DocStringKind::Function => Err(eco_format!("must be already handled")),
|
||||||
|
DocStringKind::Variable => identify_var_docs(docs).map(SymbolDocs::Variable),
|
||||||
|
DocStringKind::Constant => identify_var_docs(docs).map(SymbolDocs::Variable),
|
||||||
|
DocStringKind::Module => identify_tidy_module_docs(docs).map(SymbolDocs::Module),
|
||||||
|
DocStringKind::Struct => Ok(SymbolDocs::Plain { docs }),
|
||||||
|
DocStringKind::Reference => Ok(SymbolDocs::Plain { docs }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,20 +13,20 @@ pub struct TidyParamDocs {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct TidyFuncDocs {
|
pub struct TidyFuncDocs {
|
||||||
pub docs: String,
|
pub docs: EcoString,
|
||||||
pub return_ty: Option<EcoString>,
|
pub return_ty: Option<EcoString>,
|
||||||
pub params: Vec<TidyParamDocs>,
|
pub params: Vec<TidyParamDocs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct TidyVarDocs {
|
pub struct TidyVarDocs {
|
||||||
pub docs: String,
|
pub docs: EcoString,
|
||||||
pub return_ty: Option<String>,
|
pub return_ty: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct TidyModuleDocs {
|
pub struct TidyModuleDocs {
|
||||||
pub docs: String,
|
pub docs: EcoString,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn identify_func_docs(converted: &str) -> StrResult<TidyFuncDocs> {
|
pub fn identify_func_docs(converted: &str) -> StrResult<TidyFuncDocs> {
|
||||||
|
|
@ -129,8 +129,8 @@ pub fn identify_func_docs(converted: &str) -> StrResult<TidyFuncDocs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let docs = match break_line {
|
let docs = match break_line {
|
||||||
Some(line_no) => (lines[..line_no]).iter().copied().join("\n"),
|
Some(line_no) => (lines[..line_no]).iter().copied().join("\n").into(),
|
||||||
None => converted.to_owned(),
|
None => converted.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
params.reverse();
|
params.reverse();
|
||||||
|
|
@ -141,7 +141,7 @@ pub fn identify_func_docs(converted: &str) -> StrResult<TidyFuncDocs> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn identify_var_docs(converted: &str) -> StrResult<TidyVarDocs> {
|
pub fn identify_var_docs(converted: EcoString) -> StrResult<TidyVarDocs> {
|
||||||
let lines = converted.lines().collect::<Vec<_>>();
|
let lines = converted.lines().collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut return_ty = None;
|
let mut return_ty = None;
|
||||||
|
|
@ -170,17 +170,15 @@ pub fn identify_var_docs(converted: &str) -> StrResult<TidyVarDocs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let docs = match break_line {
|
let docs = match break_line {
|
||||||
Some(line_no) => (lines[..line_no]).iter().copied().join("\n"),
|
Some(line_no) => (lines[..line_no]).iter().copied().join("\n").into(),
|
||||||
None => converted.to_owned(),
|
None => converted,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(TidyVarDocs { docs, return_ty })
|
Ok(TidyVarDocs { docs, return_ty })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn identify_tidy_module_docs(converted: &str) -> StrResult<TidyModuleDocs> {
|
pub fn identify_tidy_module_docs(docs: EcoString) -> StrResult<TidyModuleDocs> {
|
||||||
Ok(TidyModuleDocs {
|
Ok(TidyModuleDocs { docs })
|
||||||
docs: converted.to_owned(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_brace(trim_start: &str) -> Option<(&str, &str)> {
|
fn match_brace(trim_start: &str) -> Option<(&str, &str)> {
|
||||||
|
|
@ -232,7 +230,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn var(s: &str) -> String {
|
fn var(s: &str) -> String {
|
||||||
let f = super::identify_var_docs(s).unwrap();
|
let f = super::identify_var_docs(s.into()).unwrap();
|
||||||
let mut res = format!(">> docs:\n{}\n<< docs", f.docs);
|
let mut res = format!(">> docs:\n{}\n<< docs", f.docs);
|
||||||
if let Some(t) = f.return_ty {
|
if let Some(t) = f.return_ty {
|
||||||
res.push_str(&format!("\n>>return\n{t}\n<<return"));
|
res.push_str(&format!("\n>>return\n{t}\n<<return"));
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use typst::{
|
||||||
use super::PackageId;
|
use super::PackageId;
|
||||||
use crate::{
|
use crate::{
|
||||||
adt::{interner::impl_internable, snapshot_map},
|
adt::{interner::impl_internable, snapshot_map},
|
||||||
analysis::BuiltinTy,
|
analysis::{BuiltinTy, DocString},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use super::{TyCtx, TyCtxMut};
|
pub(crate) use super::{TyCtx, TyCtxMut};
|
||||||
|
|
@ -951,6 +951,8 @@ impl IfTy {
|
||||||
pub struct TypeScheme {
|
pub struct TypeScheme {
|
||||||
/// The typing on definitions
|
/// The typing on definitions
|
||||||
pub vars: HashMap<DefId, TypeVarBounds>,
|
pub vars: HashMap<DefId, TypeVarBounds>,
|
||||||
|
/// The checked documentation of definitions
|
||||||
|
pub var_docs: HashMap<DefId, Arc<DocString>>,
|
||||||
/// The local binding of the type variable
|
/// The local binding of the type variable
|
||||||
pub local_binds: snapshot_map::SnapshotMap<DefId, Ty>,
|
pub local_binds: snapshot_map::SnapshotMap<DefId, Ty>,
|
||||||
/// The typing on syntax structures
|
/// The typing on syntax structures
|
||||||
|
|
@ -975,6 +977,7 @@ impl TypeScheme {
|
||||||
pub fn type_of_def(&self, def: DefId) -> Option<Ty> {
|
pub fn type_of_def(&self, def: DefId) -> Option<Ty> {
|
||||||
Some(self.simplify(self.vars.get(&def).map(|e| e.as_type())?, false))
|
Some(self.simplify(self.vars.get(&def).map(|e| e.as_type())?, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the type of a syntax structure
|
/// Get the type of a syntax structure
|
||||||
pub fn type_of_span(&self, site: Span) -> Option<Ty> {
|
pub fn type_of_span(&self, site: Span) -> Option<Ty> {
|
||||||
self.mapping
|
self.mapping
|
||||||
|
|
|
||||||
|
|
@ -609,7 +609,7 @@ function MakeDoc(root: DocElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function FuncItem(v: DocElement) {
|
function FuncItem(v: DocElement) {
|
||||||
const sig = v.data.signature;
|
const sig = v.data?.parsed_docs as DocSignature | undefined;
|
||||||
|
|
||||||
const export_again = v.data.export_again
|
const export_again = v.data.export_again
|
||||||
? [kwHl("external"), code(" ")]
|
? [kwHl("external"), code(" ")]
|
||||||
|
|
@ -621,8 +621,9 @@ function MakeDoc(root: DocElement) {
|
||||||
},
|
},
|
||||||
code(v.data.name)
|
code(v.data.name)
|
||||||
);
|
);
|
||||||
let funcTitle = [...export_again, name, "("];
|
let funcTitle = [...export_again, name];
|
||||||
if (sig) {
|
if (sig) {
|
||||||
|
funcTitle.push(code("("));
|
||||||
// funcTitle.push(...sig.pos.map((e: DocParam) => code(e.name)));
|
// funcTitle.push(...sig.pos.map((e: DocParam) => code(e.name)));
|
||||||
for (let i = 0; i < sig.pos.length; i++) {
|
for (let i = 0; i < sig.pos.length; i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
|
|
@ -637,11 +638,11 @@ function MakeDoc(root: DocElement) {
|
||||||
}
|
}
|
||||||
funcTitle.push(code(".."));
|
funcTitle.push(code(".."));
|
||||||
}
|
}
|
||||||
}
|
funcTitle.push(code(")"));
|
||||||
funcTitle.push(code(")"));
|
if (sig.ret_ty) {
|
||||||
if (v.data.parsed_docs?.return_ty) {
|
funcTitle.push(code(" -> "));
|
||||||
funcTitle.push(code(" -> "));
|
typeHighlighted(sig.ret_ty[0], funcTitle);
|
||||||
typeHighlighted(v.data.parsed_docs.return_ty, funcTitle);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return div(
|
return div(
|
||||||
|
|
@ -668,8 +669,7 @@ function MakeDoc(root: DocElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function SigDocs(v: DocElement): ChildDom[] {
|
function SigDocs(v: DocElement): ChildDom[] {
|
||||||
const sig: DocSignature = v.data.signature;
|
const sig = v.data.parsed_docs as DocSignature;
|
||||||
const parsed_docs = v.data.parsed_docs;
|
|
||||||
const res: ChildDom[] = [];
|
const res: ChildDom[] = [];
|
||||||
|
|
||||||
if (!sig) {
|
if (!sig) {
|
||||||
|
|
@ -677,14 +677,15 @@ function MakeDoc(root: DocElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const docsMapping = new Map<string, any>();
|
const docsMapping = new Map<string, any>();
|
||||||
// for (const doc of parsed_docs) {
|
|
||||||
// docsMapping.set(doc.name, doc.contents.join(""));
|
|
||||||
// }
|
|
||||||
// return_ty
|
// return_ty
|
||||||
if (parsed_docs?.params) {
|
for (const param of sig.pos) {
|
||||||
for (const param of parsed_docs.params) {
|
docsMapping.set(param.name, param);
|
||||||
docsMapping.set(param.name, param);
|
}
|
||||||
}
|
for (const param of Object.values(sig.named)) {
|
||||||
|
docsMapping.set(param.name, param);
|
||||||
|
}
|
||||||
|
if (sig.rest) {
|
||||||
|
docsMapping.set(sig.rest.name, sig.rest);
|
||||||
}
|
}
|
||||||
if (v.data.renderedParams) {
|
if (v.data.renderedParams) {
|
||||||
for (const p of Object.values(v.data.renderedParams)) {
|
for (const p of Object.values(v.data.renderedParams)) {
|
||||||
|
|
@ -710,9 +711,9 @@ function MakeDoc(root: DocElement) {
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (parsed_docs?.return_ty || sig.ret_ty) {
|
if (sig.ret_ty) {
|
||||||
let paramTitle = [codeHl("op", "-> ")];
|
let paramTitle = [codeHl("op", "-> ")];
|
||||||
sigTypeHighlighted(parsed_docs?.return_ty, sig.ret_ty, paramTitle);
|
sigTypeHighlighted(sig.ret_ty, paramTitle);
|
||||||
|
|
||||||
res.push(h3("Resultant"));
|
res.push(h3("Resultant"));
|
||||||
res.push(
|
res.push(
|
||||||
|
|
@ -754,10 +755,10 @@ function MakeDoc(root: DocElement) {
|
||||||
param.name
|
param.name
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
if (docsMeta?.types || param.cano_type) {
|
if (param.cano_type) {
|
||||||
paramTitle.push(code(": "));
|
paramTitle.push(code(": "));
|
||||||
// paramTitle += `: ${docsMeta.types}`;
|
// paramTitle += `: ${docsMeta.types}`;
|
||||||
sigTypeHighlighted(docsMeta?.types, param.cano_type, paramTitle);
|
sigTypeHighlighted(param.cano_type, paramTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param.expr) {
|
if (param.expr) {
|
||||||
|
|
@ -796,7 +797,7 @@ function MakeDoc(root: DocElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function SigPreview(v: DocElement): ChildDom[] {
|
function SigPreview(v: DocElement): ChildDom[] {
|
||||||
const sig = v.data.signature;
|
const sig = v.data.parsed_docs as DocSignature;
|
||||||
if (!sig) {
|
if (!sig) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -861,9 +862,9 @@ function MakeDoc(root: DocElement) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
sigTitle.push(code(")"));
|
sigTitle.push(code(")"));
|
||||||
if (v.data.parsed_docs?.return_ty) {
|
if (sig.ret_ty) {
|
||||||
sigTitle.push(code(" -> "));
|
sigTitle.push(code(" -> "));
|
||||||
typeHighlighted(v.data.parsed_docs.return_ty, sigTitle);
|
typeHighlighted(sig.ret_ty[0], sigTitle);
|
||||||
}
|
}
|
||||||
sigTitle.push(code(";"));
|
sigTitle.push(code(";"));
|
||||||
|
|
||||||
|
|
@ -913,13 +914,14 @@ function MakeDoc(root: DocElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sigTypeHighlighted(
|
function sigTypeHighlighted(
|
||||||
types: string | undefined,
|
|
||||||
inferred: [string, string] | undefined,
|
inferred: [string, string] | undefined,
|
||||||
target: ChildDom[]
|
target: ChildDom[]
|
||||||
) {
|
) {
|
||||||
if (types) {
|
// todo: determine whether it is inferred
|
||||||
typeHighlighted(types, target);
|
// if (types) {
|
||||||
} else if (inferred) {
|
// typeHighlighted(types, target);
|
||||||
|
// } else
|
||||||
|
if (inferred) {
|
||||||
const rendered: ChildDom[] = [];
|
const rendered: ChildDom[] = [];
|
||||||
typeHighlighted(inferred[0], rendered, "|");
|
typeHighlighted(inferred[0], rendered, "|");
|
||||||
const infer = span(
|
const infer = span(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue