dev: more consistent way to get docs of decls (#752)

* dev: more consistent way to get docs of decls

* test: update snapshot
This commit is contained in:
Myriad-Dreamin 2024-10-30 22:29:07 +08:00 committed by GitHub
parent 8129c6741e
commit 1c1bc19caf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 462 additions and 451 deletions

View file

@ -5,7 +5,7 @@ use typst::introspection::Introspector;
use typst::model::BibliographyElem;
use super::{prelude::*, BuiltinTy, InsTy, SharedContext};
use crate::syntax::{get_deref_target, Decl, DeclExpr, DerefTarget, Expr, ExprInfo};
use crate::syntax::{Decl, DeclExpr, DerefTarget, Expr, ExprInfo};
use crate::VersionedDocument;
/// A linked definition in the source code
@ -59,7 +59,7 @@ pub fn definition(
ctx: &Arc<SharedContext>,
source: &Source,
document: Option<&VersionedDocument>,
deref_target: DerefTarget<'_>,
deref_target: DerefTarget,
) -> Option<Definition> {
match deref_target {
// todi: field access
@ -249,10 +249,7 @@ impl CallConvention {
pub fn resolve_call_target(ctx: &Arc<SharedContext>, node: &SyntaxNode) -> Option<CallConvention> {
let callee = (|| {
let source = ctx.source_by_id(node.span().id()?).ok()?;
let node = source.find(node.span())?;
let cursor = node.offset();
let deref_target = get_deref_target(node, cursor)?;
let def = ctx.definition(&source, None, deref_target)?;
let def = ctx.def_of_span(&source, None, node.span())?;
let func_ptr = match def.term.and_then(|val| val.value()) {
Some(Value::Func(f)) => Some(f),
Some(Value::Type(ty)) => ty.constructor().ok(),

View file

@ -13,12 +13,13 @@ use reflexo_typst::{EntryReader, WorldDeps};
use rustc_hash::FxHashMap;
use tinymist_world::LspWorld;
use tinymist_world::DETACHED_ENTRY;
use typst::diag::{eco_format, At, FileError, FileResult, SourceResult};
use typst::diag::{eco_format, At, FileError, FileResult, SourceResult, StrResult};
use typst::engine::{Route, Sink, Traced};
use typst::eval::Eval;
use typst::foundations::{Bytes, Module, Styles};
use typst::layout::Position;
use typst::model::Document;
use typst::syntax::package::PackageManifest;
use typst::syntax::{package::PackageSpec, Span, VirtualPath};
use crate::analysis::prelude::*;
@ -26,10 +27,11 @@ use crate::analysis::{
analyze_bib, analyze_import_, analyze_signature, post_type_check, BibInfo, PathPreference,
Signature, SignatureTarget, Ty, TypeScheme,
};
use crate::docs::{SignatureDocs, VarDocs};
use crate::docs::{DefDocs, TidyModuleDocs};
use crate::syntax::{
construct_module_dependencies, find_expr_in_import, get_deref_target, resolve_id_by_path,
scan_workspace_files, DerefTarget, ExprInfo, LexicalScope, ModuleDependency, Processing,
scan_workspace_files, Decl, DefKind, DerefTarget, ExprInfo, LexicalScope, ModuleDependency,
Processing,
};
use crate::upstream::{tooltip_, Tooltip};
use crate::{
@ -200,10 +202,6 @@ impl<'w> AnalysisContext<'w> {
}
}
pub(crate) fn variable_docs(&mut self, pos: &LinkedNode) -> Option<VarDocs> {
crate::docs::variable_docs(self, pos)
}
pub(crate) fn preload_package(&self, entry_point: TypstFileId) {
self.shared_().preload_package(entry_point);
}
@ -376,6 +374,11 @@ impl LocalContext {
}
}
/// Get the expression information of a source file.
pub(crate) fn expr_stage_by_id(&mut self, fid: TypstFileId) -> Option<Arc<ExprInfo>> {
Some(self.expr_stage(&self.source_by_id(fid).ok()?))
}
/// Get the expression information of a source file.
pub(crate) fn expr_stage(&mut self, source: &Source) -> Arc<ExprInfo> {
let id = source.id();
@ -389,6 +392,29 @@ impl LocalContext {
let cache = &self.caches.modules.entry(id).or_default().type_check;
cache.get_or_init(|| self.shared.type_check(source)).clone()
}
pub(crate) fn def_docs(&mut self, def: &Definition) -> Option<DefDocs> {
// let plain_docs = sym.head.docs.as_deref();
// let plain_docs = plain_docs.or(sym.head.oneliner.as_deref());
match def.decl.kind() {
DefKind::Function => {
let sig = self.sig_of_def(def.clone())?;
let docs = crate::docs::signature_docs(&sig, None)?;
Some(DefDocs::Function(Box::new(docs)))
}
DefKind::Struct | DefKind::Constant | DefKind::Variable => {
let docs = crate::docs::variable_docs(self, def.decl.span())?;
Some(DefDocs::Variable(docs))
}
DefKind::Module => {
let ei = self.expr_stage_by_id(def.decl.file_id()?)?;
Some(DefDocs::Module(TidyModuleDocs {
docs: ei.module_docstring.docs.clone().unwrap_or_default(),
}))
}
DefKind::Reference => None,
}
}
}
/// The shared analysis context for analyzers.
@ -594,6 +620,11 @@ impl SharedContext {
res
}
/// Get the expression information of a source file.
pub(crate) fn expr_stage_by_id(self: &Arc<Self>, fid: TypstFileId) -> Option<Arc<ExprInfo>> {
Some(self.expr_stage(&self.source_by_id(fid).ok()?))
}
/// Get the expression information of a source file.
pub(crate) fn expr_stage(self: &Arc<Self>, source: &Source) -> Arc<ExprInfo> {
let mut route = Processing::default();
@ -660,7 +691,25 @@ impl SharedContext {
})
}
pub(crate) fn definition(
pub(crate) fn def_of_span(
self: &Arc<Self>,
source: &Source,
doc: Option<&VersionedDocument>,
span: Span,
) -> Option<Definition> {
let target = self.deref_syntax(source, span)?;
definition(self, source, doc, target)
}
pub(crate) fn def_of_decl(&self, decl: &Interned<Decl>) -> Option<Definition> {
match decl.as_ref() {
Decl::Func(..) => Some(Definition::new(decl.clone(), None)),
Decl::Module(..) => None,
_ => None,
}
}
pub(crate) fn def_of_syntax(
self: &Arc<Self>,
source: &Source,
doc: Option<&VersionedDocument>,
@ -669,22 +718,17 @@ impl SharedContext {
definition(self, source, doc, deref_target)
}
pub(crate) fn signature_def(self: &Arc<Self>, def: Definition) -> Option<Signature> {
pub(crate) fn sig_of_def(self: &Arc<Self>, def: Definition) -> Option<Signature> {
log::debug!("check definition func {def:?}");
let source = def.decl.file_id().and_then(|f| self.source_by_id(f).ok());
analyze_signature(self, SignatureTarget::Def(source, def))
}
pub(crate) fn signature_dyn(self: &Arc<Self>, func: Func) -> Signature {
pub(crate) fn sig_of_func(self: &Arc<Self>, func: Func) -> Signature {
log::debug!("check runtime func {func:?}");
analyze_signature(self, SignatureTarget::Runtime(func)).unwrap()
}
pub(crate) fn signature_docs(self: &Arc<Self>, def: &Definition) -> Option<SignatureDocs> {
let sig = self.signature_def(def.clone())?;
crate::docs::signature_docs(&sig, None)
}
/// Try to find imported target from the current source file.
/// This function will try to resolves target statically.
///
@ -789,6 +833,11 @@ impl SharedContext {
}
}
/// Get the manifest of a package by file id.
pub fn get_manifest(&self, toml_id: TypstFileId) -> StrResult<PackageManifest> {
crate::docs::get_manifest(&self.world, toml_id)
}
/// Compute the signature of a function.
pub fn compute_signature(
self: &Arc<Self>,

View file

@ -6,7 +6,7 @@ use typst::foundations::{Closure, ParamInfo};
use super::{prelude::*, BoundChecker, Definition, DocSource, SharedContext, SigTy, TypeVar};
use crate::analysis::PostTypeChecker;
use crate::docs::{UntypedSignatureDocs, UntypedSymbolDocs, UntypedVarDocs};
use crate::docs::{UntypedDefDocs, UntypedSignatureDocs, UntypedVarDocs};
use crate::syntax::get_non_strict_def_target;
use crate::ty::TyCtx;
use crate::ty::TypeBounds;
@ -290,8 +290,8 @@ fn analyze_type_signature(
// todo: this will affect inlay hint: _var_with
let (_var_with, docstring) = match type_info.var_docs.get(&v.def).map(|x| x.as_ref()) {
Some(UntypedSymbolDocs::Function(sig)) => (vec![], Either::Left(sig.as_ref())),
Some(UntypedSymbolDocs::Variable(d)) => find_alias_stack(&mut ty_ctx, &v, d)?,
Some(UntypedDefDocs::Function(sig)) => (vec![], Either::Left(sig.as_ref())),
Some(UntypedDefDocs::Variable(d)) => find_alias_stack(&mut ty_ctx, &v, d)?,
_ => return None,
};
@ -415,10 +415,10 @@ impl<'a, 'b> BoundChecker for AliasStackChecker<'a, 'b> {
log::debug!("collecting var {u:?} {pol:?} => {docs:?}");
// todo: bind builtin functions
match docs {
Some(UntypedSymbolDocs::Function(sig)) => {
Some(UntypedDefDocs::Function(sig)) => {
self.res = Some(Either::Left(sig));
}
Some(UntypedSymbolDocs::Variable(d)) => {
Some(UntypedDefDocs::Variable(d)) => {
self.checking_with = true;
self.stack.push(d);
self.check_var_rec(u, pol);
@ -468,8 +468,7 @@ fn analyze_dyn_signature(
SignatureTarget::Def(_source, def) => def.value()?.to_func()?,
SignatureTarget::SyntaxFast(..) => return None,
SignatureTarget::Syntax(source, span) => {
let target = ctx.deref_syntax(source, *span)?;
let def = ctx.definition(source, None, target)?;
let def = ctx.def_of_span(source, None, *span)?;
def.value()?.to_func()?
}
SignatureTarget::Convert(func) | SignatureTarget::Runtime(func) => func.clone(),

View file

@ -121,13 +121,13 @@ impl<'a> TypeChecker<'a> {
log::debug!("import_ty {name} from {fid:?}");
let source = self.ctx.source_by_id(fid).ok()?;
let ext_def_use_info = self.ctx.expr_stage(&source);
let ext_def_use_info = self.ctx.expr_stage_by_id(fid)?;
let source = &ext_def_use_info.source;
// todo: check types in cycle
let ext_type_info = if let Some(route) = self.route.get(&source.id()) {
route.clone()
} else {
self.ctx.type_check_(&source, self.route)
self.ctx.type_check_(source, self.route)
};
let ext_def = ext_def_use_info.exports.get(&name)?;

View file

@ -2,7 +2,7 @@
use super::*;
use crate::analysis::ParamAttrs;
use crate::docs::{SignatureDocsT, TypelessParamDocs, UntypedSymbolDocs};
use crate::docs::{SignatureDocsT, TypelessParamDocs, UntypedDefDocs};
use crate::syntax::{def::*, DocString, VarDoc};
use crate::ty::*;
@ -261,13 +261,13 @@ impl<'a> TypeChecker<'a> {
if let Some(base) = base {
self.info.var_docs.insert(
base.clone(),
Arc::new(UntypedSymbolDocs::Function(Box::new(SignatureDocsT {
Arc::new(UntypedDefDocs::Function(Box::new(SignatureDocsT {
docs: docstring.docs.clone().unwrap_or_default(),
pos: pos_docs,
named: named_docs,
rest: rest_docs,
ret_ty: (),
def_docs: Default::default(),
hover_docs: Default::default(),
}))),
);
}

View file

@ -7,7 +7,7 @@ mod symbol;
mod tidy;
use reflexo::path::unix_slash;
use typst::{foundations::Value, syntax::FileId};
use typst::syntax::FileId;
pub use module::*;
pub use package::*;
@ -21,13 +21,3 @@ fn file_id_repr(k: FileId) -> String {
unix_slash(k.vpath().as_rooted_path())
}
}
fn kind_of(val: &Value) -> DocStringKind {
match val {
Value::Module(_) => DocStringKind::Module,
Value::Type(_) => DocStringKind::Struct,
Value::Func(_) => DocStringKind::Function,
Value::Label(_) => DocStringKind::Reference,
_ => DocStringKind::Constant,
}
}

View file

@ -1,27 +1,26 @@
//! Module documentation.
use std::collections::HashMap;
use std::ops::Range;
use std::sync::Arc;
use ecow::{eco_vec, EcoString, EcoVec};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use typst::diag::{eco_format, StrResult};
use typst::foundations::{Module, Value};
use typst::diag::StrResult;
use typst::syntax::package::PackageSpec;
use typst::syntax::{FileId, Span};
use typst::syntax::FileId;
use crate::docs::file_id_repr;
use crate::syntax::{find_docs_of, get_non_strict_def_target};
use crate::upstream::truncated_doc_repr;
use crate::syntax::{Decl, DefKind, Expr, ExprInfo};
use crate::ty::Interned;
use crate::AnalysisContext;
use super::{get_manifest, get_manifest_id, kind_of, DocStringKind, PackageInfo, SymbolDocs};
use super::{get_manifest_id, DefDocs, PackageInfo};
/// Get documentation of symbols in a package.
pub fn package_module_docs(ctx: &mut AnalysisContext, pkg: &PackageInfo) -> StrResult<SymbolsInfo> {
let toml_id = get_manifest_id(pkg)?;
let manifest = get_manifest(ctx.world(), toml_id)?;
let manifest = ctx.get_manifest(toml_id)?;
let entry_point = toml_id.join(&manifest.package.entrypoint);
module_docs(ctx, entry_point)
@ -40,11 +39,11 @@ pub fn module_docs(ctx: &mut AnalysisContext, entry_point: FileId) -> StrResult<
extras: &mut extras,
};
let src = scan_ctx
let ei = scan_ctx
.ctx
.module_by_id(entry_point)
.map_err(|e| eco_format!("failed to get module by id {entry_point:?}: {e:?}"))?;
let mut symbols = scan_ctx.module_sym(eco_vec![], src);
.expr_stage_by_id(entry_point)
.ok_or("entry point not found")?;
let mut symbols = scan_ctx.module_sym(eco_vec![], ei);
let module_uses = aliases
.into_iter()
@ -70,7 +69,7 @@ pub struct SymbolInfoHead {
/// The name of the symbol.
pub name: EcoString,
/// The kind of the symbol.
pub kind: DocStringKind,
pub kind: DefKind,
/// The location (file, start, end) of the symbol.
pub loc: Option<(usize, usize, usize)>,
/// Is the symbol reexport
@ -80,24 +79,16 @@ pub struct SymbolInfoHead {
/// The one-line documentation of the symbol.
pub oneliner: Option<String>,
/// The raw documentation of the symbol.
pub docs: Option<String>,
pub docs: Option<EcoString>,
/// The parsed documentation of the symbol.
pub parsed_docs: Option<SymbolDocs>,
pub parsed_docs: Option<DefDocs>,
/// The value of the symbol.
#[serde(skip)]
pub constant: Option<EcoString>,
/// The file owning the symbol.
#[serde(skip)]
pub fid: Option<FileId>,
/// The span of the symbol.
#[serde(skip)]
pub span: Option<Span>,
/// The name range of the symbol.
#[serde(skip)]
pub name_range: Option<Range<usize>>,
/// The value of the symbol.
#[serde(skip)]
pub value: Option<Value>,
pub decl: Option<Interned<Decl>>,
}
/// Information about a symbol.
@ -129,11 +120,59 @@ struct ScanSymbolCtx<'a, 'w> {
}
impl ScanSymbolCtx<'_, '_> {
fn module_sym(&mut self, path: EcoVec<&str>, module: Module) -> SymbolInfo {
let key = module.name().to_owned();
fn module_sym(&mut self, path: EcoVec<&str>, ei: Arc<ExprInfo>) -> SymbolInfo {
let name = {
let stem = ei.fid.vpath().as_rooted_path().file_stem();
stem.and_then(|s| Some(Interned::new_str(s.to_str()?)))
.unwrap_or_default()
};
let module_decl = Decl::module(name.clone(), ei.fid).into();
let site = Some(self.root);
let p = path.clone();
self.sym(&key, p, site.as_ref(), &Value::Module(module))
self.sym(&name, p, site.as_ref(), &module_decl, None)
}
fn expr(
&mut self,
key: &str,
path: EcoVec<&str>,
site: Option<&FileId>,
val: &Expr,
) -> SymbolInfo {
match val {
Expr::Decl(d) => self.sym(key, path, site, d, Some(val)),
Expr::Ref(r) if r.root.is_some() => {
self.expr(key, path, site, r.root.as_ref().unwrap())
}
// todo: select
Expr::Select(..) => {
let mut path = path.clone();
path.push(key);
let head = SymbolInfoHead {
name: key.to_string().into(),
kind: DefKind::Module,
..Default::default()
};
SymbolInfo {
head,
children: eco_vec![],
}
}
// v => panic!("unexpected export: {key} -> {v}"),
_ => {
let mut path = path.clone();
path.push(key);
let head = SymbolInfoHead {
name: key.to_string().into(),
kind: DefKind::Constant,
..Default::default()
};
SymbolInfo {
head,
children: eco_vec![],
}
}
}
}
fn sym(
@ -141,12 +180,13 @@ impl ScanSymbolCtx<'_, '_> {
key: &str,
path: EcoVec<&str>,
site: Option<&FileId>,
val: &Value,
val: &Interned<Decl>,
expr: Option<&Expr>,
) -> SymbolInfo {
let mut head = create_head(self.ctx, key, val);
let mut head = create_head(self.ctx, key, val, expr);
if !matches!(&val, Value::Module(..)) {
if let Some((span, mod_fid)) = head.span.and_then(Span::id).zip(site) {
if !matches!(val.as_ref(), Decl::Module(..)) {
if let Some((span, mod_fid)) = head.decl.as_ref().and_then(|d| d.file_id()).zip(site) {
if span != *mod_fid {
head.export_again = true;
head.oneliner = head.docs.as_deref().map(oneliner).map(|e| e.to_owned());
@ -155,8 +195,8 @@ impl ScanSymbolCtx<'_, '_> {
}
}
let children = match val {
Value::Module(module) => module.file_id().and_then(|fid| {
let children = match val.as_ref() {
Decl::Module(..) => val.file_id().and_then(|fid| {
// only generate docs for the same package
if fid.package() != self.for_spec {
return None;
@ -174,12 +214,15 @@ impl ScanSymbolCtx<'_, '_> {
log::debug!("found module: {path:?}");
let symbols = module.scope().iter();
let symbols = symbols
.map(|(k, v, _)| {
let ei = self.ctx.expr_stage_by_id(fid)?;
let symbols = ei
.exports
.iter()
.map(|(k, v)| {
let mut path = path.clone();
path.push(k);
self.sym(k, path.clone(), Some(&fid), v)
self.expr(k, path.clone(), Some(&fid), v)
})
.collect();
Some(symbols)
@ -188,18 +231,18 @@ impl ScanSymbolCtx<'_, '_> {
};
// Insert module that is not exported
if let Some(fid) = head.fid {
if let Some(fid) = head.decl.as_ref().and_then(|d| d.file_id()) {
// only generate docs for the same package
if fid.package() == self.for_spec {
let av = self.aliases.entry(fid).or_default();
if av.is_empty() {
let m = self.ctx.module_by_id(fid);
let src = self.ctx.expr_stage_by_id(fid);
let mut path = path.clone();
path.push("-");
path.push(key);
log::debug!("found internal module: {path:?}");
if let Ok(m) = m {
if let Some(m) = src {
let msym = self.module_sym(path, m);
self.extras.push(msym)
}
@ -212,44 +255,24 @@ impl ScanSymbolCtx<'_, '_> {
}
}
fn create_head(world: &mut AnalysisContext, k: &str, v: &Value) -> SymbolInfoHead {
let kind = kind_of(v);
let (docs, name_range, fid, span) = match v {
Value::Func(f) => {
let mut span = None;
let mut name_range = None;
let docs = None.or_else(|| {
let source = world.source_by_id(f.span().id()?).ok()?;
let node = source.find(f.span())?;
log::debug!("node: {k} -> {:?}", node.parent());
// use parent of params, todo: reliable way to get the def target
let def = get_non_strict_def_target(node.parent()?.clone())?;
span = Some(def.node().span());
name_range = def.name_range();
fn create_head(
ctx: &mut AnalysisContext,
k: &str,
decl: &Interned<Decl>,
expr: Option<&Expr>,
) -> SymbolInfoHead {
let kind = decl.kind();
find_docs_of(&source, def)
});
let s = span.or(Some(f.span()));
(docs, name_range, s.and_then(Span::id), s)
}
Value::Module(m) => (None, None, m.file_id(), None),
_ => Default::default(),
};
let parsed_docs = ctx.def_of_decl(decl).and_then(|def| ctx.def_docs(&def));
let docs = parsed_docs.as_ref().map(|d| d.docs().clone());
SymbolInfoHead {
name: k.to_string().into(),
kind,
constant: None.or_else(|| match v {
Value::Func(_) => None,
t => Some(truncated_doc_repr(t)),
}),
constant: expr.map(|e| e.repr()),
docs,
name_range,
fid,
span,
value: Some(v.clone()),
parsed_docs,
decl: Some(decl.clone()),
..Default::default()
}
}

View file

@ -5,21 +5,18 @@ use std::path::PathBuf;
use ecow::{EcoString, EcoVec};
use indexmap::IndexSet;
use serde::{Deserialize, Serialize};
use tinymist_world::LspWorld;
use typst::diag::{eco_format, StrResult};
use typst::foundations::Value;
use typst::syntax::package::{PackageManifest, PackageSpec};
use typst::syntax::{FileId, Span, VirtualPath};
use typst::World;
use crate::docs::{file_id_repr, module_docs, symbol_docs, SymbolDocs, SymbolsInfo};
use crate::ty::Ty;
use crate::docs::{file_id_repr, module_docs, DefDocs, SymbolsInfo};
use crate::AnalysisContext;
/// Check Package.
pub fn check_package(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<()> {
let toml_id = get_manifest_id(spec)?;
let manifest = get_manifest(ctx.world(), toml_id)?;
let manifest = ctx.get_manifest(toml_id)?;
let entry_point = toml_id.join(&manifest.package.entrypoint);
@ -33,7 +30,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
let mut md = String::new();
let toml_id = get_manifest_id(spec)?;
let manifest = get_manifest(ctx.world(), toml_id)?;
let manifest = ctx.get_manifest(toml_id)?;
let for_spec = toml_id.package().unwrap();
let entry_point = toml_id.join(&manifest.package.entrypoint);
@ -54,7 +51,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
md.push('\n');
md.push('\n');
let manifest = get_manifest(&ctx.world, toml_id)?;
let manifest = ctx.get_manifest(toml_id)?;
let meta = PackageMeta {
namespace: spec.namespace.clone(),
@ -84,32 +81,13 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
.clone()
};
// todo: extend this cache idea for all crate?
#[allow(clippy::mutable_key_type)]
let mut describe_cache = HashMap::<Ty, String>::new();
let mut doc_ty = |ty: Option<&Ty>| {
let ty = ty?;
let short = {
describe_cache
.entry(ty.clone())
.or_insert_with(|| ty.describe().unwrap_or_else(|| "unknown".to_string()))
.clone()
};
Some((short, format!("{ty:?}")))
};
while !modules_to_generate.is_empty() {
for (parent_ident, sym) in std::mem::take(&mut modules_to_generate) {
// parent_ident, symbols
let symbols = sym.children;
let module_val = sym.head.value.as_ref().unwrap();
let module = match module_val {
Value::Module(m) => m,
_ => todo!(),
};
let fid = module.file_id();
let module_val = sym.head.decl.as_ref().unwrap();
let fid = module_val.file_id();
let aka = fid.map(&mut akas).unwrap_or_default();
// It is (primary) known to safe as a part of HTML string, so we don't have to
@ -141,7 +119,8 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
let _ = writeln!(md, "<!-- begin:module {primary} {m} -->");
for mut sym in symbols {
let span = sym.head.span.and_then(|v| {
let span = sym.head.decl.as_ref().map(|d| d.span());
let fid_range = span.and_then(|v| {
v.id().and_then(|e| {
let fid = file_ids.insert_full(e).0;
let src = ctx.source_by_id(e).ok()?;
@ -149,34 +128,42 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
Some((fid, rng.start, rng.end))
})
});
let sym_fid = sym.head.fid;
let sym_fid = sym_fid.or_else(|| sym.head.span.and_then(Span::id)).or(fid);
let span = span.or_else(|| {
let sym_fid = sym.head.decl.as_ref().and_then(|d| d.file_id());
let sym_fid = sym_fid.or_else(|| span.and_then(Span::id)).or(fid);
let span = fid_range.or_else(|| {
let fid = sym_fid?;
Some((file_ids.insert_full(fid).0, 0, 0))
});
sym.head.loc = span;
// .ok_or_else(|| {
// let err = format!("failed to convert docs in {title}").replace(
// "-->", "—>", // avoid markdown comment
// );
// log::error!("{err}");
// err
// })
let docs = sym.head.parsed_docs.clone();
// Err(e) => {
// let err = format!("failed to convert docs: {e}").replace(
// "-->", "—>", // avoid markdown comment
// );
// log::error!("{err}");
// return Err(err);
// }
let docs = symbol_docs(
ctx,
sym.head.kind,
sym.head.value.as_ref(),
sym.head.docs.as_deref(),
Some(&mut doc_ty),
);
let mut convert_err = None;
let convert_err = None::<EcoString>;
match &docs {
Ok(docs) => {
Some(docs) => {
sym.head.parsed_docs = Some(docs.clone());
sym.head.docs = None;
}
Err(e) => {
let err = format!("failed to convert docs in {title}: {e}").replace(
"-->", "—>", // avoid markdown comment
);
log::error!("{err}");
convert_err = Some(err);
None => {
// let err = format!("failed to convert docs in {title}:
// {e}").replace( "-->",
// "—>", // avoid markdown comment
// );
// log::error!("{err}");
// convert_err = Some(err);
}
}
@ -188,8 +175,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
let _ = writeln!(md, "### {}: {} in {primary}", sym.head.kind, sym.head.name);
if sym.head.export_again {
let sub_fid = sym.head.fid;
if let Some(fid) = sub_fid {
if let Some(fid) = sym_fid {
let lnk = if fid.package() == Some(for_spec) {
let sub_aka = akas(fid);
let sub_primary = sub_aka.first().cloned().unwrap_or_default();
@ -218,7 +204,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
let head = jbase64(&sym.head);
let _ = writeln!(md, "<!-- begin:symbol {ident} {head} -->");
if let Some(SymbolDocs::Function(sig)) = &sym.head.parsed_docs {
if let Some(DefDocs::Function(sig)) = &sym.head.parsed_docs {
let _ = writeln!(md, "<!-- begin:sig -->");
let _ = writeln!(md, "```typc");
let _ = write!(md, "let {}", sym.head.name);
@ -238,7 +224,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
}
(Some(docs), _) => {
let _ = writeln!(md, "{}", remove_list_annotations(docs.docs()));
if let SymbolDocs::Function(f) = docs {
if let DefDocs::Function(f) = docs {
for param in f.pos.iter().chain(f.named.values()).chain(f.rest.as_ref())
{
let _ = writeln!(md, "<!-- begin:param {} -->", param.name);
@ -273,9 +259,8 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
}
if !sym.children.is_empty() {
let sub_fid = sym.head.fid;
log::debug!("sub_fid: {sub_fid:?}");
match sub_fid {
log::debug!("sub_fid: {sym_fid:?}");
match sym_fid {
Some(fid) => {
let aka = akas(fid);
let primary = aka.first().cloned().unwrap_or_default();
@ -352,7 +337,7 @@ pub fn get_manifest_id(spec: &PackageInfo) -> StrResult<FileId> {
}
/// Parses the manifest of the package located at `package_path`.
pub fn get_manifest(world: &LspWorld, toml_id: FileId) -> StrResult<PackageManifest> {
pub fn get_manifest(world: &dyn World, toml_id: FileId) -> StrResult<PackageManifest> {
let toml_data = world
.file(toml_id)
.map_err(|err| eco_format!("failed to read package manifest ({})", err))?;

View file

@ -7,64 +7,32 @@ 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::syntax::LinkedNode;
use typst::foundations::Bytes;
use typst::{
diag::StrResult,
syntax::{FileId, VirtualPath},
syntax::{FileId, Span, VirtualPath},
};
use super::tidy::*;
use crate::analysis::{ParamAttrs, ParamSpec, Signature, ToFunc};
use crate::analysis::{ParamAttrs, ParamSpec, Signature};
use crate::docs::library;
use crate::prelude::*;
use crate::ty::Ty;
use crate::ty::{DocSource, Interned};
use crate::upstream::plain_docs_sentence;
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 (without type information).
pub type UntypedSymbolDocs = SymbolDocsT<()>;
pub type UntypedDefDocs = DefDocsT<()>;
/// Documentation about a symbol.
pub type SymbolDocs = SymbolDocsT<TypeRepr>;
pub type DefDocs = DefDocsT<TypeRepr>;
/// Documentation about a symbol.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum SymbolDocsT<T> {
pub enum DefDocsT<T> {
/// Documentation about a function.
#[serde(rename = "func")]
Function(Box<SignatureDocsT<T>>),
@ -82,7 +50,7 @@ pub enum SymbolDocsT<T> {
},
}
impl<T> SymbolDocsT<T> {
impl<T> DefDocsT<T> {
/// Get the markdown representation of the documentation.
pub fn docs(&self) -> &EcoString {
match self {
@ -94,37 +62,14 @@ impl<T> SymbolDocsT<T> {
}
}
pub(crate) fn symbol_docs(
ctx: &mut AnalysisContext,
kind: DocStringKind,
sym_value: Option<&Value>,
docs: Option<&str>,
doc_ty: Option<ShowTypeRepr>,
) -> Result<SymbolDocs, String> {
let signature =
sym_value.and_then(|e| signature_docs(&ctx.signature_dyn(e.to_func()?), 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);
}
impl DefDocs {
/// Get full documentation for the signature.
pub fn hover_docs(&self) -> EcoString {
match self {
DefDocs::Function(docs) => docs.hover_docs().clone(),
_ => plain_docs_sentence(self.docs()),
}
}
Ok(SymbolDocs::Plain { docs: "".into() })
}
/// Describes a primary function signature.
@ -142,20 +87,20 @@ pub struct SignatureDocsT<T> {
pub ret_ty: T,
/// The full documentation for the signature.
#[serde(skip)]
pub def_docs: OnceLock<String>,
pub hover_docs: OnceLock<EcoString>,
}
impl SignatureDocsT<TypeRepr> {
/// Get full documentation for the signature.
pub fn def_docs(&self) -> &String {
self.def_docs
.get_or_init(|| plain_docs_sentence(&format!("{}", DefDocs(self))).into())
pub fn hover_docs(&self) -> &EcoString {
self.hover_docs
.get_or_init(|| plain_docs_sentence(&format!("{}", SigDefDocs(self))))
}
}
struct DefDocs<'a>(&'a SignatureDocs);
struct SigDefDocs<'a>(&'a SignatureDocs);
impl fmt::Display for DefDocs<'_> {
impl fmt::Display for SigDefDocs<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let docs = self.0;
let base_docs = docs.docs.trim();
@ -318,10 +263,10 @@ fn format_ty(ty: Option<&Ty>, doc_ty: Option<&mut ShowTypeRepr>) -> TypeRepr {
}
}
pub(crate) fn variable_docs(ctx: &mut AnalysisContext, pos: &LinkedNode) -> Option<VarDocs> {
let source = ctx.source_by_id(pos.span().id()?).ok()?;
pub(crate) fn variable_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs> {
let source = ctx.source_by_id(pos.id()?).ok()?;
let type_info = ctx.type_check(&source);
let ty = type_info.type_of_span(pos.span())?;
let ty = type_info.type_of_span(pos)?;
// todo multiple sources
let mut srcs = ty.sources();
@ -396,7 +341,7 @@ pub(crate) fn signature_docs(
named,
rest,
ret_ty,
def_docs: OnceLock::new(),
hover_docs: OnceLock::new(),
})
}
@ -430,14 +375,3 @@ pub(crate) fn convert_docs(world: &LspWorld, content: &str) -> StrResult<EcoStri
Ok(conv.replace("```example", "```typ"))
}
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 }),
}
}

View file

@ -35,7 +35,7 @@ impl StatefulRequest for GotoDefinitionRequest {
let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?;
let origin_selection_range = ctx.to_lsp_range(deref_target.node().range(), &source);
let def = ctx.definition(&source, doc.as_ref(), deref_target)?;
let def = ctx.def_of_syntax(&source, doc.as_ref(), deref_target)?;
let (fid, def_range) = def.def_at(ctx.shared())?;

View file

@ -1,15 +1,15 @@
use core::fmt;
use core::fmt::Write;
use typst_shim::syntax::LinkedNodeExt;
use crate::{
analysis::{get_link_exprs_in, Definition},
docs::SignatureDocs,
docs::DefDocs,
jump_from_cursor,
prelude::*,
syntax::{find_docs_before, get_deref_target, Decl},
syntax::{get_deref_target, Decl, DefKind},
ty::PathPreference,
upstream::{expr_tooltip, plain_docs_sentence, route_of_value, truncated_repr, Tooltip},
upstream::{expr_tooltip, route_of_value, truncated_repr, Tooltip},
LspHoverContents, StatefulRequest,
};
@ -223,10 +223,8 @@ fn def_tooltip(
cursor: usize,
) -> Option<LspHoverContents> {
let leaf = LinkedNode::new(source.root()).leaf_at_compat(cursor)?;
let deref_target = get_deref_target(leaf.clone(), cursor)?;
let def = ctx.definition(source, document, deref_target.clone())?;
let def = ctx.def_of_syntax(source, document, deref_target.clone())?;
let mut results = vec![];
let mut actions = vec![];
@ -250,76 +248,66 @@ fn def_tooltip(
Some(LspHoverContents::Array(results))
}
Func(..) | Closure(..) => {
let sig = ctx.signature_docs(&def);
_ => {
let sym_docs = ctx.def_docs(&def);
results.push(MarkedString::LanguageString(LanguageString {
language: "typc".to_owned(),
value: format!(
"let {name}{params}{result};",
name = def.name(),
params = ParamTooltip(sig.as_ref()),
result =
ResultTooltip(def.name(), sig.as_ref().and_then(|sig| sig.ret_ty.as_ref()))
),
}));
if let Some(doc) = sig {
results.push(MarkedString::String(doc.def_docs().into()));
}
if let Some(link) = ExternalDocLink::get(ctx, &def) {
actions.push(link);
}
render_actions(&mut results, actions);
Some(LspHoverContents::Array(results))
}
ModuleAlias(..) | Module(..) | PathStem(..) | ImportPath(..) | IncludePath(..) => {
let id = def.decl.file_id()?;
let src = ctx.source_by_id(id).ok()?;
let ei = ctx.expr_stage(&src);
let docs = ei.module_docstring.docs.clone()?;
results.push(MarkedString::String(docs.as_str().into()));
Some(LspHoverContents::Array(results))
}
IdentRef(..) | ImportAlias(..) | Import(..) | Var(..) => {
let deref_node = deref_target.node();
let sig = ctx.variable_docs(deref_target.node());
// todo: check sensible length, value highlighting
if let Some(values) = expr_tooltip(ctx.world(), deref_node) {
match values {
Tooltip::Text(values) => {
results.push(MarkedString::String(values.into()));
}
Tooltip::Code(values) => {
results.push(MarkedString::LanguageString(LanguageString {
language: "typc".to_owned(),
value: values.into(),
}));
if matches!(def.decl.kind(), DefKind::Variable | DefKind::Constant) {
// todo: check sensible length, value highlighting
if let Some(values) = expr_tooltip(ctx.world(), deref_target.node()) {
match values {
Tooltip::Text(values) => {
results.push(MarkedString::String(values.into()));
}
Tooltip::Code(values) => {
results.push(MarkedString::LanguageString(LanguageString {
language: "typc".to_owned(),
value: values.into(),
}));
}
}
}
}
results.push(MarkedString::LanguageString(LanguageString {
language: "typc".to_owned(),
value: format!(
"let {name}{result};",
name = def.name(),
result = ResultTooltip(
def.name(),
sig.as_ref().and_then(|sig| sig.return_ty.as_ref())
)
),
}));
// todo: hover with `with_stack`
if let Some(doc) = sig {
results.push(MarkedString::String(doc.def_docs().clone()));
} else if let Some(doc) = DocTooltip::get(ctx, &def) {
results.push(MarkedString::String(doc));
if matches!(
def.decl.kind(),
DefKind::Function | DefKind::Variable | DefKind::Constant
) && !def.name().is_empty()
{
results.push(MarkedString::LanguageString(LanguageString {
language: "typc".to_owned(),
value: {
let mut type_doc = String::new();
type_doc.push_str("let ");
type_doc.push_str(def.name());
match &sym_docs {
Some(DefDocs::Variable(docs)) => {
push_result_ty(def.name(), docs.return_ty.as_ref(), &mut type_doc);
}
Some(DefDocs::Function(docs)) => {
let _ = docs.print(&mut type_doc);
push_result_ty(def.name(), docs.ret_ty.as_ref(), &mut type_doc);
}
_ => {}
}
type_doc.push(';');
type_doc
},
}));
}
if let Some(doc) = sym_docs {
results.push(MarkedString::String(doc.hover_docs().into()));
}
// if let Some(doc) = sig {
// results.push(MarkedString::String(doc.def_docs().clone()));
// } else if let Some(doc) = DocTooltip::get(ctx, &def) {
// results.push(MarkedString::String(doc));
// }
if let Some(link) = ExternalDocLink::get(ctx, &def) {
actions.push(link);
}
@ -327,11 +315,20 @@ fn def_tooltip(
render_actions(&mut results, actions);
Some(LspHoverContents::Array(results))
}
Pattern(..) | Docs(..) | Generated(..) | Constant(..) | ContentRef(..) | StrName(..)
| ModuleImport(..) | Content(..) | Spread(..) => None,
}
}
fn push_result_ty(name: &str, ty_repr: Option<&(String, String)>, type_doc: &mut String) {
let Some((short, _)) = ty_repr else {
return;
};
if short == name {
return;
}
let _ = write!(type_doc, " = {short}");
}
fn render_actions(results: &mut Vec<MarkedString>, actions: Vec<CommandLink>) {
if actions.is_empty() {
return;
@ -371,33 +368,6 @@ fn render_actions(results: &mut Vec<MarkedString>, actions: Vec<CommandLink>) {
results.push(MarkedString::String(g));
}
// todo: hover with `with_stack`
struct ParamTooltip<'a>(Option<&'a SignatureDocs>);
impl fmt::Display for ParamTooltip<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Some(sig) = self.0 else {
return Ok(());
};
sig.print(f)
}
}
struct ResultTooltip<'a>(&'a str, Option<&'a (String, String)>);
impl fmt::Display for ResultTooltip<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Some((short, _)) = self.1 else {
return Ok(());
};
if short == self.0 {
return Ok(());
}
write!(f, " = {short}")
}
}
struct ExternalDocLink;
impl ExternalDocLink {
@ -452,54 +422,6 @@ impl ExternalDocLink {
}
}
pub(crate) struct DocTooltip;
impl DocTooltip {
pub fn get(ctx: &mut AnalysisContext, def: &Definition) -> Option<String> {
self::DocTooltip::get_inner(ctx, def).map(|s| "\n\n".to_owned() + &s)
}
fn get_inner(ctx: &mut AnalysisContext, def: &Definition) -> Option<String> {
let value = def.value();
if matches!(value, Some(Value::Func(..))) {
if let Some(builtin) = Self::builtin_func_tooltip(def) {
return Some(plain_docs_sentence(builtin).into());
}
};
let (fid, def_range) = def.def_at(ctx.shared()).clone()?;
let src = ctx.source_by_id(fid).ok()?;
find_docs_before(&src, def_range.start)
}
}
impl DocTooltip {
fn builtin_func_tooltip(def: &Definition) -> Option<&'_ str> {
let value = def.value();
let Some(Value::Func(func)) = &value else {
return None;
};
use typst::foundations::func::Repr;
let mut func = func;
let docs = 'search: loop {
match func.inner() {
Repr::Native(n) => break 'search n.docs,
Repr::Element(e) => break 'search e.docs(),
Repr::With(w) => {
func = &w.0;
}
Repr::Closure(..) => {
return None;
}
}
};
Some(docs)
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -47,7 +47,7 @@ impl StatefulRequest for PrepareRenameRequest {
}
let origin_selection_range = ctx.to_lsp_range(deref_target.node().range(), &source);
let def = ctx.definition(&source, doc.as_ref(), deref_target.clone())?;
let def = ctx.def_of_syntax(&source, doc.as_ref(), deref_target.clone())?;
let (name, range) = prepare_renaming(ctx, &deref_target, &def)?;

View file

@ -53,7 +53,7 @@ pub(crate) fn find_references(
}
};
let def = ctx.definition(source, doc, target)?;
let def = ctx.def_of_syntax(source, doc, target)?;
let worker = ReferencesWorker {
ctx: ctx.fork_for_search(),
@ -103,14 +103,13 @@ impl<'a, 'w> ReferencesWorker<'a, 'w> {
fn file(&mut self, ref_fid: TypstFileId) -> Option<()> {
log::debug!("references: file: {ref_fid:?}");
let ref_source = self.ctx.ctx.source_by_id(ref_fid).ok()?;
let expr_info = self.ctx.ctx.expr_stage(&ref_source);
let ei = self.ctx.ctx.expr_stage_by_id(ref_fid)?;
let uri = self.ctx.ctx.uri_for_id(ref_fid).ok()?;
let t = expr_info.get_refs(self.def.decl.clone());
self.push_idents(&ref_source, &uri, t);
let t = ei.get_refs(self.def.decl.clone());
self.push_idents(&ei.source, &uri, t);
if expr_info.is_exported(&self.def.decl) {
if ei.is_exported(&self.def.decl) {
self.ctx.push_dependents(ref_fid);
}

View file

@ -41,7 +41,7 @@ impl StatefulRequest for RenameRequest {
let source = ctx.source_by_path(&self.path).ok()?;
let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?;
let def = ctx.definition(&source, doc.as_ref(), deref_target.clone())?;
let def = ctx.def_of_syntax(&source, doc.as_ref(), deref_target.clone())?;
prepare_renaming(ctx, &deref_target, &def)?;

View file

@ -3,9 +3,11 @@ use typst_shim::syntax::LinkedNodeExt;
use crate::{
adt::interner::Interned,
analysis::Definition,
prelude::*,
syntax::{get_check_target, get_deref_target, CheckTarget, ParamTarget},
DocTooltip, LspParamInfo, SemanticRequest,
syntax::{find_docs_before, get_check_target, get_deref_target, CheckTarget, ParamTarget},
upstream::plain_docs_sentence,
LspParamInfo, SemanticRequest,
};
/// The [`textDocument/signatureHelp`] request is sent from the client to the
@ -40,7 +42,7 @@ impl SemanticRequest for SignatureHelpRequest {
let deref_target = get_deref_target(callee, cursor)?;
let def_link = ctx.definition(&source, None, deref_target)?;
let def_link = ctx.def_of_syntax(&source, None, deref_target)?;
let documentation = DocTooltip::get(ctx, &def_link)
.as_deref()
@ -59,7 +61,7 @@ impl SemanticRequest for SignatureHelpRequest {
function = &inner.0;
}
let sig = ctx.signature_dyn(function.clone());
let sig = ctx.sig_of_func(function.clone());
log::debug!("got signature {sig:?}");
@ -145,6 +147,54 @@ impl SemanticRequest for SignatureHelpRequest {
}
}
pub(crate) struct DocTooltip;
impl DocTooltip {
pub fn get(ctx: &mut AnalysisContext, def: &Definition) -> Option<String> {
self::DocTooltip::get_inner(ctx, def).map(|s| "\n\n".to_owned() + &s)
}
fn get_inner(ctx: &mut AnalysisContext, def: &Definition) -> Option<String> {
let value = def.value();
if matches!(value, Some(Value::Func(..))) {
if let Some(builtin) = Self::builtin_func_tooltip(def) {
return Some(plain_docs_sentence(builtin).into());
}
};
let (fid, def_range) = def.def_at(ctx.shared()).clone()?;
let src = ctx.source_by_id(fid).ok()?;
find_docs_before(&src, def_range.start)
}
}
impl DocTooltip {
fn builtin_func_tooltip(def: &Definition) -> Option<&'_ str> {
let value = def.value();
let Some(Value::Func(func)) = &value else {
return None;
};
use typst::foundations::func::Repr;
let mut func = func;
let docs = 'search: loop {
match func.inner() {
Repr::Native(n) => break 'search n.docs,
Repr::Element(e) => break 'search e.docs(),
Repr::With(w) => {
func = &w.0;
}
Repr::Closure(..) => {
return None;
}
}
};
Some(docs)
}
}
fn markdown_docs(docs: &str) -> Documentation {
Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,

View file

@ -2,6 +2,7 @@ use core::fmt;
use std::{collections::BTreeMap, ops::Range};
use reflexo_typst::package::PackageSpec;
use serde::{Deserialize, Serialize};
use tinymist_derive::DeclEnum;
use typst::{
foundations::{Element, Func, Module, Type, Value},
@ -71,6 +72,12 @@ pub enum Expr {
Star,
}
impl Expr {
pub(crate) fn repr(&self) -> EcoString {
let mut s = EcoString::new();
let _ = ExprFormatter::new(&mut s, true).write_expr(self);
s
}
pub(crate) fn span(&self) -> Span {
match self {
Expr::Decl(d) => d.span(),
@ -171,6 +178,38 @@ fn select_of(source: Interned<Ty>, name: Interned<str>) -> Expr {
Expr::Type(Ty::Select(SelectTy::new(source, name)))
}
/// Kind of a definition.
#[derive(Debug, Default, Clone, Copy, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum DefKind {
/// A definition for some constant.
#[default]
Constant,
/// A definition for some function.
Function,
/// A definition for some variable.
Variable,
/// A definition for some module.
Module,
/// A definition for some struct.
Struct,
/// A definition for some reference.
Reference,
}
impl fmt::Display for DefKind {
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"),
}
}
}
pub type DeclExpr = Interned<Decl>;
#[derive(Clone, PartialEq, Eq, Hash, DeclEnum)]
@ -369,6 +408,21 @@ impl Decl {
)
}
pub fn kind(&self) -> DefKind {
use Decl::*;
match self {
ModuleAlias(..) | Module(..) | PathStem(..) | ImportPath(..) | IncludePath(..) => {
DefKind::Module
}
// Type(_) => DocStringKind::Struct,
Func(..) | Closure(..) => DefKind::Function,
Label(..) | BibEntry(..) | ContentRef(..) => DefKind::Reference,
IdentRef(..) | ImportAlias(..) | Import(..) | Var(..) => DefKind::Variable,
Pattern(..) | Docs(..) | Generated(..) | Constant(..) | StrName(..)
| ModuleImport(..) | Content(..) | Spread(..) => DefKind::Constant,
}
}
pub fn file_id(&self) -> Option<TypstFileId> {
match self {
Self::Module(ModuleDecl { fid, .. }) => Some(*fid),

View file

@ -10,10 +10,10 @@ use crate::{
analysis::SharedContext,
docs::{
convert_docs, identify_func_docs, identify_tidy_module_docs, identify_var_docs,
DocStringKind, UntypedSymbolDocs, VarDocsT,
UntypedDefDocs, VarDocsT,
},
prelude::*,
syntax::Decl,
syntax::{Decl, DefKind},
ty::{BuiltinTy, Interned, PackageId, SigTy, StrRef, Ty, TypeBounds, TypeVar, TypeVarBounds},
};
@ -62,8 +62,8 @@ pub struct VarDoc {
impl VarDoc {
/// Convert the variable doc to an untyped version
pub fn to_untyped(&self) -> Arc<UntypedSymbolDocs> {
Arc::new(UntypedSymbolDocs::Variable(VarDocsT {
pub fn to_untyped(&self) -> Arc<UntypedDefDocs> {
Arc::new(UntypedDefDocs::Variable(VarDocsT {
docs: self.docs.clone(),
return_ty: (),
def_docs: OnceLock::new(),
@ -75,7 +75,7 @@ pub(crate) fn compute_docstring(
ctx: &Arc<SharedContext>,
fid: TypstFileId,
docs: String,
kind: DocStringKind,
kind: DefKind,
) -> Option<DocString> {
let checker = DocsChecker {
fid,
@ -86,12 +86,12 @@ pub(crate) fn compute_docstring(
next_id: 0,
};
match kind {
DocStringKind::Function => checker.check_func_docs(docs),
DocStringKind::Variable => checker.check_var_docs(docs),
DocStringKind::Module => checker.check_module_docs(docs),
DocStringKind::Constant => None,
DocStringKind::Struct => None,
DocStringKind::Reference => None,
DefKind::Function => checker.check_func_docs(docs),
DefKind::Variable => checker.check_var_docs(docs),
DefKind::Module => checker.check_module_docs(docs),
DefKind::Constant => None,
DefKind::Struct => None,
DefKind::Reference => None,
}
}

View file

@ -15,9 +15,8 @@ use typst::{
use crate::{
analysis::{QueryStatGuard, SharedContext},
docs::DocStringKind,
prelude::*,
syntax::find_module_level_docs,
syntax::{find_module_level_docs, DefKind},
ty::{BuiltinTy, InsTy, Interned, Ty},
};
@ -82,7 +81,7 @@ pub(crate) fn expr_of(
let module_docstring = Arc::new(
find_module_level_docs(&source)
.and_then(|docs| compute_docstring(&ctx, source.id(), docs, DocStringKind::Module))
.and_then(|docs| compute_docstring(&ctx, source.id(), docs, DefKind::Module))
.unwrap_or_default(),
);
@ -275,7 +274,7 @@ impl<'a> ExprWorker<'a> {
}
}
fn check_docstring(&mut self, decl: &DeclExpr, kind: DocStringKind) {
fn check_docstring(&mut self, decl: &DeclExpr, kind: DefKind) {
if let Some(docs) = self.comment_matcher.collect() {
let docstring = compute_docstring(&self.ctx, self.fid, docs, kind);
if let Some(docstring) = docstring {
@ -427,7 +426,7 @@ impl<'a> ExprWorker<'a> {
let span = p.span();
let decl = Decl::pattern(span).into();
self.check_docstring(&decl, DocStringKind::Variable);
self.check_docstring(&decl, DefKind::Variable);
let pattern = self.check_pattern(p);
Expr::Let(Interned::new(LetExpr {
span,
@ -443,7 +442,7 @@ impl<'a> ExprWorker<'a> {
Some(name) => Decl::func(name).into(),
None => Decl::closure(typed.span()).into(),
};
self.check_docstring(&decl, DocStringKind::Function);
self.check_docstring(&decl, DefKind::Function);
self.resolve_as(Decl::as_def(&decl, None));
let (params, body) = self.with_scope(|this| {

View file

@ -21,7 +21,7 @@ use super::PackageId;
use crate::{
adt::{interner::impl_internable, snapshot_map},
analysis::BuiltinTy,
docs::UntypedSymbolDocs,
docs::UntypedDefDocs,
syntax::{DeclExpr, UnaryOp},
};
@ -962,7 +962,7 @@ pub struct TypeScheme {
/// The typing on definitions
pub vars: FxHashMap<DeclExpr, TypeVarBounds>,
/// The checked documentation of definitions
pub var_docs: FxHashMap<DeclExpr, Arc<UntypedSymbolDocs>>,
pub var_docs: FxHashMap<DeclExpr, Arc<UntypedDefDocs>>,
/// The local binding of the type variable
pub local_binds: snapshot_map::SnapshotMap<DeclExpr, Ty>,
/// The typing on syntax structures

View file

@ -26,6 +26,21 @@ impl Ty {
let mut worker = TypeDescriber::default();
worker.describe_root(self)
}
// todo: extend this cache idea for all crate?
// #[allow(clippy::mutable_key_type)]
// let mut describe_cache = HashMap::<Ty, String>::new();
// let doc_ty = |ty: Option<&Ty>| {
// let ty = ty?;
// let short = {
// describe_cache
// .entry(ty.clone())
// .or_insert_with(|| ty.describe().unwrap_or_else(||
// "unknown".to_string())) .clone()
// };
// Some((short, format!("{ty:?}")))
// };
}
#[derive(Default)]

View file

@ -464,7 +464,7 @@ fn describe_value(ctx: &mut AnalysisContext, v: &Value) -> EcoString {
f = &with_f.0;
}
let sig = ctx.signature_dyn(f.clone());
let sig = ctx.sig_of_func(f.clone());
sig.primary()
.ty()
.describe()
@ -582,7 +582,7 @@ pub fn param_completions<'a>(
let pos_index =
param_index_at_leaf(&ctx.leaf, &func, args).map(|i| if this.is_some() { i + 1 } else { i });
let signature = ctx.ctx.signature_dyn(func.clone());
let signature = ctx.ctx.sig_of_func(func.clone());
let leaf_type = ctx.ctx.literal_type_of_node(ctx.leaf.clone());
log::debug!("pos_param_completion_by_type: {:?}", leaf_type);
@ -998,7 +998,7 @@ pub fn named_param_value_completions<'a>(
func = f.0.clone();
}
let signature = ctx.ctx.signature_dyn(func.clone());
let signature = ctx.ctx.sig_of_func(func.clone());
let primary_sig = signature.primary();

View file

@ -424,11 +424,6 @@ pub fn truncated_repr(value: &Value) -> EcoString {
truncated_repr_::<_10MB>(value)
}
pub fn truncated_doc_repr(value: &Value) -> EcoString {
const _128B: usize = 128;
truncated_repr_::<_128B>(value)
}
/// Run a function with a VM instance in the world
pub fn with_vm<T>(world: Tracked<dyn World + '_>, f: impl FnOnce(&mut typst::eval::Vm) -> T) -> T {
use comemo::Track;