mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-25 05:22:52 +00:00
feat: cache docstring building (Part. 2) (#680)
* feat: cache docstring building (Part. 2) * dev: fix import * dev: change some variable names
This commit is contained in:
parent
3ed401740e
commit
4aeb3747bc
7 changed files with 398 additions and 259 deletions
|
|
@ -19,9 +19,10 @@ use typst_shim::syntax::LinkedNodeExt;
|
||||||
use crate::adt::interner::Interned;
|
use crate::adt::interner::Interned;
|
||||||
use crate::analysis::{
|
use crate::analysis::{
|
||||||
analyze_bib, analyze_dyn_signature, analyze_expr_, analyze_import_, post_type_check, BibInfo,
|
analyze_bib, analyze_dyn_signature, analyze_expr_, analyze_import_, post_type_check, BibInfo,
|
||||||
DefUseInfo, DefinitionLink, IdentRef, ImportInfo, PathPreference, SigTy, Signature,
|
DefUseInfo, DefinitionLink, DocString, IdentRef, ImportInfo, PathPreference, SigTy, Signature,
|
||||||
SignatureTarget, Ty, TypeScheme,
|
SignatureTarget, Ty, TypeScheme,
|
||||||
};
|
};
|
||||||
|
use crate::docs::DocStringKind;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
construct_module_dependencies, find_expr_in_import, get_deref_target, resolve_id_by_path,
|
construct_module_dependencies, find_expr_in_import, get_deref_target, resolve_id_by_path,
|
||||||
|
|
@ -85,6 +86,7 @@ pub struct AnalysisGlobalCaches {
|
||||||
def_use: FxDashMap<u128, (u64, Option<Arc<DefUseInfo>>)>,
|
def_use: FxDashMap<u128, (u64, Option<Arc<DefUseInfo>>)>,
|
||||||
type_check: FxDashMap<u128, (u64, Option<Arc<TypeScheme>>)>,
|
type_check: FxDashMap<u128, (u64, Option<Arc<TypeScheme>>)>,
|
||||||
static_signatures: FxDashMap<u128, (u64, Source, usize, Signature)>,
|
static_signatures: FxDashMap<u128, (u64, Source, usize, Signature)>,
|
||||||
|
docstrings: FxDashMap<u128, Option<Arc<DocString>>>,
|
||||||
signatures: FxDashMap<u128, (u64, foundations::Func, Signature)>,
|
signatures: FxDashMap<u128, (u64, foundations::Func, Signature)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -349,13 +351,13 @@ impl<'w> AnalysisContext<'w> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a module by string.
|
/// Get a module by string.
|
||||||
pub fn module_by_str(&mut self, rr: String) -> Option<Module> {
|
pub fn module_by_str(&self, rr: String) -> Option<Module> {
|
||||||
let src = Source::new(*DETACHED_ENTRY, rr);
|
let src = Source::new(*DETACHED_ENTRY, rr);
|
||||||
self.module_by_src(src).ok()
|
self.module_by_src(src).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get (Create) a module by source.
|
/// Get (Create) a module by source.
|
||||||
pub fn module_by_src(&mut self, source: Source) -> SourceResult<Module> {
|
pub fn module_by_src(&self, source: Source) -> SourceResult<Module> {
|
||||||
let route = Route::default();
|
let route = Route::default();
|
||||||
let mut tracer = Tracer::default();
|
let mut tracer = Tracer::default();
|
||||||
|
|
||||||
|
|
@ -479,6 +481,23 @@ impl<'w> AnalysisContext<'w> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compute_docstring(
|
||||||
|
&self,
|
||||||
|
docs: String,
|
||||||
|
kind: DocStringKind,
|
||||||
|
) -> Option<Arc<DocString>> {
|
||||||
|
let h = hash128(&(&docs, &kind));
|
||||||
|
let res = if let Some(res) = self.analysis.caches.docstrings.get(&h) {
|
||||||
|
res.clone()
|
||||||
|
} else {
|
||||||
|
let res = crate::analysis::tyck::compute_docstring(self, docs, kind).map(Arc::new);
|
||||||
|
self.analysis.caches.docstrings.insert(h, res.clone());
|
||||||
|
res
|
||||||
|
};
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute the signature of a function.
|
/// Compute the signature of a function.
|
||||||
pub fn compute_signature(
|
pub fn compute_signature(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,12 @@ use super::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod apply;
|
mod apply;
|
||||||
|
mod docs;
|
||||||
mod select;
|
mod select;
|
||||||
mod syntax;
|
mod syntax;
|
||||||
|
|
||||||
pub(crate) use apply::*;
|
pub(crate) use apply::*;
|
||||||
|
pub(crate) use docs::*;
|
||||||
pub(crate) use select::*;
|
pub(crate) use select::*;
|
||||||
|
|
||||||
/// Type checking at the source unit level.
|
/// Type checking at the source unit level.
|
||||||
|
|
@ -44,9 +46,6 @@ pub(crate) fn type_check(ctx: &mut AnalysisContext, source: Source) -> Option<Ar
|
||||||
def_use_info,
|
def_use_info,
|
||||||
info: &mut info,
|
info: &mut info,
|
||||||
externals: HashMap::new(),
|
externals: HashMap::new(),
|
||||||
docs_scope: HashMap::new(),
|
|
||||||
documenting_id: None,
|
|
||||||
generated: HashMap::new(),
|
|
||||||
mode: InterpretMode::Markup,
|
mode: InterpretMode::Markup,
|
||||||
};
|
};
|
||||||
let lnk = LinkedNode::new(source.root());
|
let lnk = LinkedNode::new(source.root());
|
||||||
|
|
@ -74,9 +73,6 @@ struct TypeChecker<'a, 'w> {
|
||||||
def_use_info: Arc<DefUseInfo>,
|
def_use_info: Arc<DefUseInfo>,
|
||||||
|
|
||||||
info: &'a mut TypeScheme,
|
info: &'a mut TypeScheme,
|
||||||
docs_scope: HashMap<EcoString, Option<Ty>>,
|
|
||||||
documenting_id: Option<DefId>,
|
|
||||||
generated: HashMap<DefId, u32>,
|
|
||||||
externals: HashMap<DefId, Option<Ty>>,
|
externals: HashMap<DefId, Option<Ty>>,
|
||||||
mode: InterpretMode,
|
mode: InterpretMode,
|
||||||
}
|
}
|
||||||
|
|
@ -119,12 +115,16 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
.or_else(|| Some(self.def_use_info.get_def(s.id()?, r)?.0))
|
.or_else(|| Some(self.def_use_info.get_def(s.id()?, r)?.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_var(&mut self, name: StrRef, id: DefId) -> Ty {
|
fn copy_based_on(&mut self, fr: &TypeVarBounds, offset: u64, id: DefId) -> Ty {
|
||||||
let next_id = self.generated.entry(id).or_insert(0);
|
let encoded = DefId((id.0 + 1) * 0x100_0000_0000 + offset + fr.id().0);
|
||||||
*next_id += 1;
|
log::debug!("copy var {fr:?} as {encoded:?}");
|
||||||
let encoded = DefId((id.0 + 1) * 0x100_0000_0000 + (*next_id as u64));
|
let bounds = TypeVarBounds::new(
|
||||||
log::debug!("generate var {name:?} {encoded:?}");
|
TypeVar {
|
||||||
let bounds = TypeVarBounds::new(TypeVar { name, def: encoded }, TypeBounds::default());
|
name: fr.name().clone(),
|
||||||
|
def: encoded,
|
||||||
|
},
|
||||||
|
fr.bounds.bounds().read().clone(),
|
||||||
|
);
|
||||||
let var = bounds.as_type();
|
let var = bounds.as_type();
|
||||||
self.info.vars.insert(encoded, bounds);
|
self.info.vars.insert(encoded, bounds);
|
||||||
var
|
var
|
||||||
|
|
@ -155,13 +155,6 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
Some(var.as_type())
|
Some(var.as_type())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_docs_scope<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
|
|
||||||
let res = f(self);
|
|
||||||
self.docs_scope.clear();
|
|
||||||
self.documenting_id = None;
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn import_ty(&mut self, def_id: DefId) -> Option<Ty> {
|
fn import_ty(&mut self, def_id: DefId) -> Option<Ty> {
|
||||||
if let Some(ty) = self.externals.get(&def_id) {
|
if let Some(ty) = self.externals.get(&def_id) {
|
||||||
return ty.clone();
|
return ty.clone();
|
||||||
|
|
|
||||||
324
crates/tinymist-query/src/analysis/tyck/docs.rs
Normal file
324
crates/tinymist-query/src/analysis/tyck/docs.rs
Normal file
|
|
@ -0,0 +1,324 @@
|
||||||
|
use std::{collections::BTreeMap, sync::LazyLock};
|
||||||
|
|
||||||
|
use reflexo::TakeAs;
|
||||||
|
use typst::foundations::{IntoValue, Module, Str, Type};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
docs::{convert_docs, identify_func_docs, DocStringKind},
|
||||||
|
syntax::{find_docs_of, get_non_strict_def_target},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const DOC_VARS: u64 = 0;
|
||||||
|
|
||||||
|
impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
|
pub fn check_closure_docs(&mut self, root: &LinkedNode) -> Option<DocString> {
|
||||||
|
let closure = root.cast::<ast::Closure>()?;
|
||||||
|
|
||||||
|
// todo: cache docs capture
|
||||||
|
// use parent of params, todo: reliable way to get the def target
|
||||||
|
let def = get_non_strict_def_target(root.clone())?;
|
||||||
|
let docs = find_docs_of(&self.source, def)?;
|
||||||
|
|
||||||
|
let documenting_id = closure
|
||||||
|
.name()
|
||||||
|
.and_then(|n| self.get_def_id(n.span(), &to_ident_ref(root, n)?))?;
|
||||||
|
let docstring = self.ctx.compute_docstring(docs, DocStringKind::Function)?;
|
||||||
|
Some(docstring.take().rename_based_on(documenting_id, self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub(crate) struct DocString {
|
||||||
|
/// The documentation of the item
|
||||||
|
pub docs: Option<String>,
|
||||||
|
/// The typing on definitions
|
||||||
|
pub var_bounds: HashMap<DefId, TypeVarBounds>,
|
||||||
|
/// The variable doc associated with the item
|
||||||
|
pub vars: HashMap<EcoString, VarDoc>,
|
||||||
|
/// The type of the resultant type
|
||||||
|
pub res_ty: Option<Ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DocString {
|
||||||
|
pub fn get_var(&self, name: &str) -> Option<&VarDoc> {
|
||||||
|
self.vars.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn var_ty(&self, name: &str) -> Option<&Ty> {
|
||||||
|
self.get_var(name).and_then(|v| v.ty.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rename_based_on(self, documenting_id: DefId, base: &mut TypeChecker) -> DocString {
|
||||||
|
let DocString {
|
||||||
|
docs,
|
||||||
|
var_bounds,
|
||||||
|
vars,
|
||||||
|
mut res_ty,
|
||||||
|
} = self;
|
||||||
|
let mut renamer = IdRenamer {
|
||||||
|
base,
|
||||||
|
var_bounds: &var_bounds,
|
||||||
|
base_id: documenting_id,
|
||||||
|
offset: DOC_VARS,
|
||||||
|
};
|
||||||
|
let mut vars = vars;
|
||||||
|
for (_name, doc) in vars.iter_mut() {
|
||||||
|
if let Some(ty) = &mut doc.ty {
|
||||||
|
if let Some(mutated) = ty.mutate(true, &mut renamer) {
|
||||||
|
*ty = mutated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ty) = res_ty.as_mut() {
|
||||||
|
if let Some(mutated) = ty.mutate(true, &mut renamer) {
|
||||||
|
*ty = mutated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DocString {
|
||||||
|
docs,
|
||||||
|
var_bounds,
|
||||||
|
vars,
|
||||||
|
res_ty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub(crate) struct VarDoc {
|
||||||
|
pub _docs: Option<EcoString>,
|
||||||
|
pub ty: Option<Ty>,
|
||||||
|
pub _default: Option<EcoString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compute_docstring(
|
||||||
|
ctx: &AnalysisContext,
|
||||||
|
docs: String,
|
||||||
|
kind: DocStringKind,
|
||||||
|
) -> Option<DocString> {
|
||||||
|
let checker = DocsChecker {
|
||||||
|
ctx,
|
||||||
|
vars: HashMap::new(),
|
||||||
|
docs_scope: HashMap::new(),
|
||||||
|
next_id: 0,
|
||||||
|
};
|
||||||
|
match kind {
|
||||||
|
DocStringKind::Function => checker.check_closure_docs(docs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DocsChecker<'a, 'w> {
|
||||||
|
ctx: &'a AnalysisContext<'w>,
|
||||||
|
/// The typing on definitions
|
||||||
|
vars: HashMap<DefId, TypeVarBounds>,
|
||||||
|
docs_scope: HashMap<EcoString, Option<Ty>>,
|
||||||
|
next_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'w> DocsChecker<'a, 'w> {
|
||||||
|
pub fn check_closure_docs(mut self, docs: String) -> Option<DocString> {
|
||||||
|
let converted = convert_docs(self.ctx.world(), &docs).ok()?;
|
||||||
|
let converted = identify_func_docs(&converted).ok()?;
|
||||||
|
let module = self.ctx.module_by_str(docs)?;
|
||||||
|
|
||||||
|
let mut params = HashMap::new();
|
||||||
|
for param in converted.params.into_iter() {
|
||||||
|
params.insert(
|
||||||
|
param.name,
|
||||||
|
VarDoc {
|
||||||
|
_docs: Some(param.docs),
|
||||||
|
ty: self.check_doc_types(&module, ¶m.types),
|
||||||
|
_default: param.default,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res_ty = converted
|
||||||
|
.return_ty
|
||||||
|
.and_then(|ty| self.check_doc_types(&module, &ty));
|
||||||
|
|
||||||
|
Some(DocString {
|
||||||
|
docs: Some(converted.docs),
|
||||||
|
var_bounds: self.vars,
|
||||||
|
vars: params,
|
||||||
|
res_ty,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_doc_types(&mut self, m: &Module, strs: &str) -> Option<Ty> {
|
||||||
|
let mut types = vec![];
|
||||||
|
for name in strs.split(",").map(|e| e.trim()) {
|
||||||
|
let Some(ty) = self.check_doc_type_ident(m, name) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
types.push(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Ty::from_types(types.into_iter()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_doc_type_ident(&mut self, m: &Module, name: &str) -> Option<Ty> {
|
||||||
|
static TYPE_REPRS: LazyLock<HashMap<&'static str, Ty>> = LazyLock::new(|| {
|
||||||
|
let values = Vec::from_iter(
|
||||||
|
[
|
||||||
|
Value::None,
|
||||||
|
Value::Auto,
|
||||||
|
// Value::Bool(Default::default()),
|
||||||
|
Value::Int(Default::default()),
|
||||||
|
Value::Float(Default::default()),
|
||||||
|
Value::Length(Default::default()),
|
||||||
|
Value::Angle(Default::default()),
|
||||||
|
Value::Ratio(Default::default()),
|
||||||
|
Value::Relative(Default::default()),
|
||||||
|
Value::Fraction(Default::default()),
|
||||||
|
Value::Str(Default::default()),
|
||||||
|
]
|
||||||
|
.map(|v| v.ty())
|
||||||
|
.into_iter()
|
||||||
|
.chain([
|
||||||
|
Type::of::<typst::visualize::Color>(),
|
||||||
|
Type::of::<typst::visualize::Gradient>(),
|
||||||
|
Type::of::<typst::visualize::Pattern>(),
|
||||||
|
Type::of::<typst::symbols::Symbol>(),
|
||||||
|
Type::of::<typst::foundations::Version>(),
|
||||||
|
Type::of::<typst::foundations::Bytes>(),
|
||||||
|
Type::of::<typst::foundations::Label>(),
|
||||||
|
Type::of::<typst::foundations::Datetime>(),
|
||||||
|
Type::of::<typst::foundations::Duration>(),
|
||||||
|
Type::of::<typst::foundations::Content>(),
|
||||||
|
Type::of::<typst::foundations::Styles>(),
|
||||||
|
Type::of::<typst::foundations::Array>(),
|
||||||
|
Type::of::<typst::foundations::Dict>(),
|
||||||
|
Type::of::<typst::foundations::Func>(),
|
||||||
|
Type::of::<typst::foundations::Args>(),
|
||||||
|
Type::of::<typst::foundations::Type>(),
|
||||||
|
Type::of::<typst::foundations::Module>(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
let shorts = values
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|ty| (ty.short_name(), Ty::Builtin(BuiltinTy::Type(ty))));
|
||||||
|
let longs = values
|
||||||
|
.into_iter()
|
||||||
|
.map(|ty| (ty.long_name(), Ty::Builtin(BuiltinTy::Type(ty))));
|
||||||
|
let builtins = [
|
||||||
|
("any", Ty::Any),
|
||||||
|
("bool", Ty::Boolean(None)),
|
||||||
|
("boolean", Ty::Boolean(None)),
|
||||||
|
("false", Ty::Boolean(Some(false))),
|
||||||
|
("true", Ty::Boolean(Some(true))),
|
||||||
|
];
|
||||||
|
HashMap::from_iter(shorts.chain(longs).chain(builtins))
|
||||||
|
});
|
||||||
|
|
||||||
|
let builtin_ty = TYPE_REPRS.get(name).cloned();
|
||||||
|
builtin_ty.or_else(|| self.check_doc_type_anno(m, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_doc_type_anno(&mut self, m: &Module, name: &str) -> Option<Ty> {
|
||||||
|
if let Some(v) = self.docs_scope.get(name) {
|
||||||
|
return v.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let v = m.scope().get(name)?;
|
||||||
|
log::debug!("check doc type annotation: {name:?}");
|
||||||
|
if let Value::Content(c) = v {
|
||||||
|
let annotated = c.clone().unpack::<typst::text::RawElem>().ok()?;
|
||||||
|
let text = annotated.text().clone().into_value().cast::<Str>().ok()?;
|
||||||
|
let code = typst::syntax::parse_code(&text.as_str().replace('\'', "θ"));
|
||||||
|
let mut exprs = code.cast::<ast::Code>()?.exprs();
|
||||||
|
let ret = self.check_doc_type_expr(m, exprs.next()?);
|
||||||
|
self.docs_scope.insert(name.into(), ret.clone());
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_var(&mut self, name: StrRef) -> Ty {
|
||||||
|
self.next_id += 1;
|
||||||
|
let encoded = DefId(self.next_id as u64);
|
||||||
|
log::debug!("generate var {name:?} {encoded:?}");
|
||||||
|
let bounds = TypeVarBounds::new(TypeVar { name, def: encoded }, TypeBounds::default());
|
||||||
|
let var = bounds.as_type();
|
||||||
|
self.vars.insert(encoded, bounds);
|
||||||
|
var
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_doc_type_expr(&mut self, m: &Module, s: ast::Expr) -> Option<Ty> {
|
||||||
|
log::debug!("check doc type expr: {s:?}");
|
||||||
|
match s {
|
||||||
|
ast::Expr::Ident(i) => self.check_doc_type_ident(m, i.get().as_str()),
|
||||||
|
ast::Expr::Closure(c) => {
|
||||||
|
log::debug!("check doc closure annotation: {c:?}");
|
||||||
|
let mut pos = vec![];
|
||||||
|
let mut named = BTreeMap::new();
|
||||||
|
let mut rest = None;
|
||||||
|
|
||||||
|
for param in c.params().children() {
|
||||||
|
match param {
|
||||||
|
ast::Param::Pos(ast::Pattern::Normal(ast::Expr::Ident(i))) => {
|
||||||
|
let base_ty = self.docs_scope.get(i.get().as_str()).cloned();
|
||||||
|
pos.push(base_ty.flatten().unwrap_or(Ty::Any));
|
||||||
|
}
|
||||||
|
ast::Param::Pos(_) => {
|
||||||
|
pos.push(Ty::Any);
|
||||||
|
}
|
||||||
|
ast::Param::Named(e) => {
|
||||||
|
let exp = self.check_doc_type_expr(m, e.expr()).unwrap_or(Ty::Any);
|
||||||
|
named.insert(e.name().into(), exp);
|
||||||
|
}
|
||||||
|
// todo: spread left/right
|
||||||
|
ast::Param::Spread(s) => {
|
||||||
|
let Some(i) = s.sink_ident() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let name = i.get().clone();
|
||||||
|
let rest_ty = self
|
||||||
|
.docs_scope
|
||||||
|
.get(i.get().as_str())
|
||||||
|
.cloned()
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_else(|| self.generate_var(name.as_str().into()));
|
||||||
|
self.docs_scope.insert(name, Some(rest_ty.clone()));
|
||||||
|
rest = Some(rest_ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = self.check_doc_type_expr(m, c.body())?;
|
||||||
|
let sig = SigTy::new(pos, named, rest, Some(body)).into();
|
||||||
|
|
||||||
|
Some(Ty::Func(sig))
|
||||||
|
}
|
||||||
|
ast::Expr::Dict(d) => {
|
||||||
|
log::debug!("check doc dict annotation: {d:?}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IdRenamer<'a, 'b, 'w> {
|
||||||
|
base: &'a mut TypeChecker<'b, 'w>,
|
||||||
|
var_bounds: &'a HashMap<DefId, TypeVarBounds>,
|
||||||
|
base_id: DefId,
|
||||||
|
offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, 'w> TyMutator for IdRenamer<'a, 'b, 'w> {
|
||||||
|
fn mutate(&mut self, ty: &Ty, pol: bool) -> Option<Ty> {
|
||||||
|
match ty {
|
||||||
|
Ty::Var(v) => Some(self.base.copy_based_on(
|
||||||
|
self.var_bounds.get(&v.def).unwrap(),
|
||||||
|
self.offset,
|
||||||
|
self.base_id,
|
||||||
|
)),
|
||||||
|
ty => self.mutate_rec(ty, pol),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
use std::{collections::BTreeMap, sync::LazyLock};
|
use std::{collections::BTreeMap, sync::LazyLock};
|
||||||
|
|
||||||
use typst::{
|
use typst::{
|
||||||
foundations::{IntoValue, Module, Str, Type, Value},
|
foundations::Value,
|
||||||
syntax::{
|
syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
LinkedNode, SyntaxKind,
|
LinkedNode, SyntaxKind,
|
||||||
|
|
@ -11,21 +11,9 @@ use typst::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{adt::interner::Interned, ty::*};
|
||||||
adt::interner::Interned,
|
|
||||||
docs::{convert_docs, identify_func_docs},
|
|
||||||
syntax::{find_docs_of, get_non_strict_def_target},
|
|
||||||
ty::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
static EMPTY_DOCSTRING: LazyLock<DocString> = LazyLock::new(DocString::default);
|
||||||
struct ParamDoc {
|
|
||||||
// docs: String,
|
|
||||||
ty: Option<Ty>,
|
|
||||||
// default: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
type ParamDocs = HashMap<String, ParamDoc>;
|
|
||||||
|
|
||||||
impl<'a, 'w> TypeChecker<'a, 'w> {
|
impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
pub(crate) fn check_syntax(&mut self, root: LinkedNode) -> Option<Ty> {
|
pub(crate) fn check_syntax(&mut self, root: LinkedNode) -> Option<Ty> {
|
||||||
|
|
@ -383,50 +371,11 @@ 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 dostring = self.check_closure_docs(&root);
|
||||||
|
let dostring = dostring.as_ref().unwrap_or(&EMPTY_DOCSTRING);
|
||||||
let closure: ast::Closure = root.cast()?;
|
let closure: ast::Closure = root.cast()?;
|
||||||
|
|
||||||
let docs = None.or_else(|| {
|
log::debug!("check closure: {:?} -> {dostring:#?}", closure.name());
|
||||||
// use parent of params, todo: reliable way to get the def target
|
|
||||||
let def = get_non_strict_def_target(root.clone())?;
|
|
||||||
find_docs_of(&self.source, def)
|
|
||||||
});
|
|
||||||
|
|
||||||
let parsed = docs.and_then(|docs| {
|
|
||||||
let documenting_id = closure
|
|
||||||
.name()
|
|
||||||
.and_then(|n| self.get_def_id(n.span(), &to_ident_ref(&root, n)?))?;
|
|
||||||
|
|
||||||
let converted = convert_docs(self.ctx.world(), &docs).ok()?;
|
|
||||||
let converted = identify_func_docs(&converted).ok()?;
|
|
||||||
let module = self.ctx.module_by_str(docs)?;
|
|
||||||
|
|
||||||
// Wrap a docs scope
|
|
||||||
self.with_docs_scope(|this| {
|
|
||||||
this.documenting_id = Some(documenting_id);
|
|
||||||
let mut params = ParamDocs::new();
|
|
||||||
for param in converted.params.into_iter() {
|
|
||||||
params.insert(
|
|
||||||
param.name,
|
|
||||||
ParamDoc {
|
|
||||||
// docs: param.docs,
|
|
||||||
ty: this.check_doc_types(&module, param.types),
|
|
||||||
// default: param.default,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((
|
|
||||||
Some(converted.docs),
|
|
||||||
params,
|
|
||||||
converted
|
|
||||||
.return_ty
|
|
||||||
.map(|ty| this.check_doc_types(&module, ty)),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
let (_docs, param_docs, _ret) = parsed.unwrap_or_default();
|
|
||||||
|
|
||||||
log::debug!("check closure: {:?} -> {param_docs:#?}", closure.name());
|
|
||||||
|
|
||||||
let mut pos = vec![];
|
let mut pos = vec![];
|
||||||
let mut named = BTreeMap::new();
|
let mut named = BTreeMap::new();
|
||||||
|
|
@ -436,16 +385,14 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
for param in closure.params().children() {
|
for param in closure.params().children() {
|
||||||
match param {
|
match param {
|
||||||
ast::Param::Pos(pattern) => {
|
ast::Param::Pos(pattern) => {
|
||||||
pos.push(self.check_pattern(pattern, Ty::Any, ¶m_docs, root.clone()));
|
pos.push(self.check_pattern(pattern, Ty::Any, dostring, root.clone()));
|
||||||
}
|
}
|
||||||
ast::Param::Named(e) => {
|
ast::Param::Named(e) => {
|
||||||
let name = e.name().get();
|
let name = e.name().get();
|
||||||
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 = self.get_var(e.name().span(), to_ident_ref(&root, e.name())?)?;
|
||||||
let anno = param_docs.get(name.as_str()).and_then(|p| p.ty.clone());
|
if let Some(annotated) = dostring.var_ty(name.as_str()) {
|
||||||
log::debug!("check closure param: {name} with {exp:?} and annotation {anno:?}");
|
self.constrain(&v, annotated);
|
||||||
if let Some(anno) = anno {
|
|
||||||
self.constrain(&v, &anno);
|
|
||||||
}
|
}
|
||||||
// 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.
|
||||||
|
|
@ -458,9 +405,8 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
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 = self.get_var(e.span(), to_ident_ref(&root, e)?)?;
|
||||||
let anno = param_docs.get(e.get().as_str()).and_then(|p| p.ty.clone());
|
if let Some(annotated) = dostring.var_ty(e.get().as_str()) {
|
||||||
if let Some(anno) = anno {
|
self.constrain(&v, annotated);
|
||||||
self.constrain(&v, &anno);
|
|
||||||
}
|
}
|
||||||
self.constrain(&exp, &v);
|
self.constrain(&exp, &v);
|
||||||
rest = Some(v);
|
rest = Some(v);
|
||||||
|
|
@ -471,6 +417,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = self.check_expr_in(closure.body().span(), root);
|
let body = self.check_expr_in(closure.body().span(), root);
|
||||||
|
let _ = dostring.res_ty;
|
||||||
|
|
||||||
let named: Vec<(Interned<str>, Ty)> = named.into_iter().collect();
|
let named: Vec<(Interned<str>, Ty)> = named.into_iter().collect();
|
||||||
|
|
||||||
|
|
@ -521,7 +468,7 @@ 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));
|
||||||
|
|
||||||
self.check_pattern(pattern, value, &ParamDocs::default(), root.clone());
|
self.check_pattern(pattern, value, &EMPTY_DOCSTRING, root.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -627,6 +574,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
fn check_destruct_assign(&mut self, _root: LinkedNode<'_>) -> Option<Ty> {
|
fn check_destruct_assign(&mut self, _root: LinkedNode<'_>) -> Option<Ty> {
|
||||||
Some(Ty::Builtin(BuiltinTy::None))
|
Some(Ty::Builtin(BuiltinTy::None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr_in(&mut self, span: Span, root: LinkedNode<'_>) -> Ty {
|
fn check_expr_in(&mut self, span: Span, root: LinkedNode<'_>) -> Ty {
|
||||||
root.find(span)
|
root.find(span)
|
||||||
.map(|node| self.check(node))
|
.map(|node| self.check(node))
|
||||||
|
|
@ -637,10 +585,10 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: ast::Pattern<'_>,
|
pattern: ast::Pattern<'_>,
|
||||||
value: Ty,
|
value: Ty,
|
||||||
param_docs: &ParamDocs,
|
docs: &DocString,
|
||||||
root: LinkedNode<'_>,
|
root: LinkedNode<'_>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
self.check_pattern_(pattern, value, param_docs, root)
|
self.check_pattern_(pattern, value, docs, root)
|
||||||
.unwrap_or(Ty::Builtin(BuiltinTy::Undef))
|
.unwrap_or(Ty::Builtin(BuiltinTy::Undef))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -648,18 +596,16 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: ast::Pattern<'_>,
|
pattern: ast::Pattern<'_>,
|
||||||
value: Ty,
|
value: Ty,
|
||||||
param_docs: &ParamDocs,
|
docs: &DocString,
|
||||||
root: LinkedNode<'_>,
|
root: LinkedNode<'_>,
|
||||||
) -> 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 v = self.get_var(ident.span(), to_ident_ref(&root, ident)?)?;
|
||||||
let anno = param_docs
|
let annotated = docs.var_ty(ident.get().as_str());
|
||||||
.get(ident.get().as_str())
|
log::debug!("check pattern: {ident:?} with {value:?} and annotation {annotated:?}");
|
||||||
.and_then(|p| p.ty.clone());
|
if let Some(annotated) = annotated {
|
||||||
log::debug!("check pattern: {ident:?} with {value:?} and annotation {anno:?}");
|
self.constrain(&v, annotated);
|
||||||
if let Some(anno) = anno {
|
|
||||||
self.constrain(&v, &anno);
|
|
||||||
}
|
}
|
||||||
self.constrain(&value, &v);
|
self.constrain(&value, &v);
|
||||||
v
|
v
|
||||||
|
|
@ -667,161 +613,10 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
||||||
ast::Pattern::Normal(_) => Ty::Any,
|
ast::Pattern::Normal(_) => Ty::Any,
|
||||||
ast::Pattern::Placeholder(_) => Ty::Any,
|
ast::Pattern::Placeholder(_) => Ty::Any,
|
||||||
ast::Pattern::Parenthesized(exp) => {
|
ast::Pattern::Parenthesized(exp) => {
|
||||||
self.check_pattern(exp.pattern(), value, param_docs, root)
|
self.check_pattern(exp.pattern(), value, docs, root)
|
||||||
}
|
}
|
||||||
// todo: pattern
|
// todo: pattern
|
||||||
ast::Pattern::Destructuring(_destruct) => Ty::Any,
|
ast::Pattern::Destructuring(_destruct) => Ty::Any,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_doc_types(&mut self, m: &Module, strs: String) -> Option<Ty> {
|
|
||||||
let mut types = vec![];
|
|
||||||
for name in strs.split(",").map(|e| e.trim()) {
|
|
||||||
let Some(ty) = self.check_doc_type_ident(m, name) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
types.push(ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Ty::from_types(types.into_iter()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_doc_type_ident(&mut self, m: &Module, name: &str) -> Option<Ty> {
|
|
||||||
static TYPE_REPRS: LazyLock<HashMap<&'static str, Ty>> = LazyLock::new(|| {
|
|
||||||
let values = Vec::from_iter(
|
|
||||||
[
|
|
||||||
Value::None,
|
|
||||||
Value::Auto,
|
|
||||||
// Value::Bool(Default::default()),
|
|
||||||
Value::Int(Default::default()),
|
|
||||||
Value::Float(Default::default()),
|
|
||||||
Value::Length(Default::default()),
|
|
||||||
Value::Angle(Default::default()),
|
|
||||||
Value::Ratio(Default::default()),
|
|
||||||
Value::Relative(Default::default()),
|
|
||||||
Value::Fraction(Default::default()),
|
|
||||||
Value::Str(Default::default()),
|
|
||||||
]
|
|
||||||
.map(|v| v.ty())
|
|
||||||
.into_iter()
|
|
||||||
.chain([
|
|
||||||
Type::of::<typst::visualize::Color>(),
|
|
||||||
Type::of::<typst::visualize::Gradient>(),
|
|
||||||
Type::of::<typst::visualize::Pattern>(),
|
|
||||||
Type::of::<typst::symbols::Symbol>(),
|
|
||||||
Type::of::<typst::foundations::Version>(),
|
|
||||||
Type::of::<typst::foundations::Bytes>(),
|
|
||||||
Type::of::<typst::foundations::Label>(),
|
|
||||||
Type::of::<typst::foundations::Datetime>(),
|
|
||||||
Type::of::<typst::foundations::Duration>(),
|
|
||||||
Type::of::<typst::foundations::Content>(),
|
|
||||||
Type::of::<typst::foundations::Styles>(),
|
|
||||||
Type::of::<typst::foundations::Array>(),
|
|
||||||
Type::of::<typst::foundations::Dict>(),
|
|
||||||
Type::of::<typst::foundations::Func>(),
|
|
||||||
Type::of::<typst::foundations::Args>(),
|
|
||||||
Type::of::<typst::foundations::Type>(),
|
|
||||||
Type::of::<typst::foundations::Module>(),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
let shorts = values
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(|ty| (ty.short_name(), Ty::Builtin(BuiltinTy::Type(ty))));
|
|
||||||
let longs = values
|
|
||||||
.into_iter()
|
|
||||||
.map(|ty| (ty.long_name(), Ty::Builtin(BuiltinTy::Type(ty))));
|
|
||||||
let builtins = [
|
|
||||||
("any", Ty::Any),
|
|
||||||
("bool", Ty::Boolean(None)),
|
|
||||||
("boolean", Ty::Boolean(None)),
|
|
||||||
("false", Ty::Boolean(Some(false))),
|
|
||||||
("true", Ty::Boolean(Some(true))),
|
|
||||||
];
|
|
||||||
HashMap::from_iter(shorts.chain(longs).chain(builtins))
|
|
||||||
});
|
|
||||||
|
|
||||||
let builtin_ty = TYPE_REPRS.get(name).cloned();
|
|
||||||
builtin_ty.or_else(|| self.check_doc_type_anno(m, name))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_doc_type_anno(&mut self, m: &Module, name: &str) -> Option<Ty> {
|
|
||||||
if let Some(v) = self.docs_scope.get(name) {
|
|
||||||
return v.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
let v = m.scope().get(name)?;
|
|
||||||
log::debug!("check doc type annotation: {name:?}");
|
|
||||||
if let Value::Content(c) = v {
|
|
||||||
let anno = c.clone().unpack::<typst::text::RawElem>().ok()?;
|
|
||||||
let text = anno.text().clone().into_value().cast::<Str>().ok()?;
|
|
||||||
let code = typst::syntax::parse_code(&text.as_str().replace('\'', "θ"));
|
|
||||||
let mut exprs = code.cast::<ast::Code>()?.exprs();
|
|
||||||
let ret = self.check_doc_type_expr(m, exprs.next()?);
|
|
||||||
self.docs_scope.insert(name.into(), ret.clone());
|
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_doc_type_expr(&mut self, m: &Module, s: ast::Expr) -> Option<Ty> {
|
|
||||||
log::debug!("check doc type expr: {s:?}");
|
|
||||||
match s {
|
|
||||||
ast::Expr::Ident(i) => self.check_doc_type_ident(m, i.get().as_str()),
|
|
||||||
ast::Expr::Closure(c) => {
|
|
||||||
log::debug!("check doc closure annotation: {c:?}");
|
|
||||||
let mut pos = vec![];
|
|
||||||
let mut named = BTreeMap::new();
|
|
||||||
let mut rest = None;
|
|
||||||
|
|
||||||
for param in c.params().children() {
|
|
||||||
match param {
|
|
||||||
ast::Param::Pos(ast::Pattern::Normal(ast::Expr::Ident(i))) => {
|
|
||||||
let base_ty = self.docs_scope.get(i.get().as_str()).cloned();
|
|
||||||
pos.push(base_ty.flatten().unwrap_or(Ty::Any));
|
|
||||||
}
|
|
||||||
ast::Param::Pos(_) => {
|
|
||||||
pos.push(Ty::Any);
|
|
||||||
}
|
|
||||||
ast::Param::Named(e) => {
|
|
||||||
let exp = self.check_doc_type_expr(m, e.expr()).unwrap_or(Ty::Any);
|
|
||||||
named.insert(e.name().into(), exp);
|
|
||||||
}
|
|
||||||
// todo: spread left/right
|
|
||||||
ast::Param::Spread(s) => {
|
|
||||||
let Some(i) = s.sink_ident() else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let name = i.get().clone();
|
|
||||||
let rest_ty = self
|
|
||||||
.docs_scope
|
|
||||||
.get(i.get().as_str())
|
|
||||||
.cloned()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
self.generate_var(
|
|
||||||
name.as_str().into(),
|
|
||||||
self.documenting_id.unwrap(),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
self.docs_scope.insert(name, Some(rest_ty.clone()));
|
|
||||||
rest = Some(rest_ty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let body = self.check_doc_type_expr(m, c.body())?;
|
|
||||||
let sig = SigTy::new(pos, named, rest, Some(body)).into();
|
|
||||||
|
|
||||||
Some(Ty::Func(sig))
|
|
||||||
}
|
|
||||||
ast::Expr::Dict(d) => {
|
|
||||||
log::debug!("check doc dict annotation: {d:?}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,12 @@ use typst::syntax::package::{PackageManifest, PackageSpec};
|
||||||
use typst::syntax::{FileId, Span, VirtualPath};
|
use typst::syntax::{FileId, Span, VirtualPath};
|
||||||
use typst::World;
|
use typst::World;
|
||||||
|
|
||||||
pub(crate) use self::tidy::*;
|
|
||||||
use crate::analysis::analyze_dyn_signature;
|
use crate::analysis::analyze_dyn_signature;
|
||||||
use crate::syntax::{find_docs_of, get_non_strict_def_target, IdentRef};
|
use crate::syntax::{find_docs_of, get_non_strict_def_target, IdentRef};
|
||||||
use crate::ty::Ty;
|
use crate::ty::Ty;
|
||||||
use crate::upstream::truncated_doc_repr;
|
use crate::upstream::truncated_doc_repr;
|
||||||
use crate::AnalysisContext;
|
use crate::AnalysisContext;
|
||||||
|
pub(crate) use tidy::*;
|
||||||
|
|
||||||
/// Information about a package.
|
/// Information about a package.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
|
@ -54,6 +54,13 @@ impl From<(PathBuf, PackageSpec)> for PackageInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Kind of a docstring.
|
||||||
|
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum DocStringKind {
|
||||||
|
/// A docstring for a function.
|
||||||
|
Function,
|
||||||
|
}
|
||||||
|
|
||||||
/// Docs about a symbol.
|
/// Docs about a symbol.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(tag = "kind")]
|
#[serde(tag = "kind")]
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
|
use ecow::EcoString;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typst::diag::StrResult;
|
use typst::diag::StrResult;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct TidyParamDocs {
|
pub struct TidyParamDocs {
|
||||||
pub name: String,
|
pub name: EcoString,
|
||||||
pub docs: String,
|
pub docs: EcoString,
|
||||||
pub types: String,
|
pub types: EcoString,
|
||||||
pub default: Option<String>,
|
pub default: Option<EcoString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct TidyFuncDocs {
|
pub struct TidyFuncDocs {
|
||||||
pub docs: String,
|
pub docs: String,
|
||||||
pub return_ty: Option<String>,
|
pub return_ty: Option<EcoString>,
|
||||||
pub params: Vec<TidyParamDocs>,
|
pub params: Vec<TidyParamDocs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ pub fn identify_func_docs(converted: &str) -> StrResult<TidyFuncDocs> {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
return_ty = Some(w.trim().to_string());
|
return_ty = Some(w.trim().into());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,7 +121,7 @@ pub fn identify_func_docs(converted: &str) -> StrResult<TidyFuncDocs> {
|
||||||
name: param_line.0,
|
name: param_line.0,
|
||||||
types: param_line.1,
|
types: param_line.1,
|
||||||
default: None,
|
default: None,
|
||||||
docs: buf.into_iter().join("\n"),
|
docs: buf.into_iter().join("\n").into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1057,8 +1057,8 @@ impl TypeVarBounds {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the name of the type variable
|
/// Get the name of the type variable
|
||||||
pub fn name(&self) -> StrRef {
|
pub fn name(&self) -> &StrRef {
|
||||||
self.var.name.clone()
|
&self.var.name
|
||||||
}
|
}
|
||||||
/// Get the definition id of the type variable
|
/// Get the definition id of the type variable
|
||||||
pub fn id(&self) -> DefId {
|
pub fn id(&self) -> DefId {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue