mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-23 12:46:43 +00:00
refactor: refactor hover, analysis/global, and docs crates (#755)
* dev: refactor hover.rs * refactor refactor AnalysisContext * refactor: refactor docs crate
This commit is contained in:
parent
1c1bc19caf
commit
8f3566366e
44 changed files with 694 additions and 790 deletions
62
crates/tinymist-query/src/docs/convert.rs
Normal file
62
crates/tinymist-query/src/docs/convert.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use ecow::{eco_format, EcoString};
|
||||
use parking_lot::Mutex;
|
||||
use tinymist_world::base::{EntryState, ShadowApi, TaskInputs};
|
||||
use tinymist_world::LspWorld;
|
||||
use typlite::scopes::Scopes;
|
||||
use typlite::value::{Value, *};
|
||||
use typst::foundations::Bytes;
|
||||
use typst::{
|
||||
diag::StrResult,
|
||||
syntax::{FileId, VirtualPath},
|
||||
};
|
||||
|
||||
// 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: LazyLock<Mutex<FileId>> =
|
||||
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: LazyLock<Arc<Scopes<Value>>> = LazyLock::new(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.replace("```example", "```typ"))
|
||||
}
|
||||
|
||||
pub(super) fn lib() -> Arc<Scopes<Value>> {
|
||||
let mut scopes = typlite::library::library();
|
||||
|
||||
// todo: how to import this function correctly?
|
||||
scopes.define("example", example as RawFunc);
|
||||
|
||||
Arc::new(scopes)
|
||||
}
|
||||
|
||||
/// Evaluate a `example`.
|
||||
pub fn example(mut args: Args) -> typlite::Result<Value> {
|
||||
let body = get_pos_named!(args, body: Content).0;
|
||||
let body = body.trim();
|
||||
let ticks = body.chars().take_while(|t| *t == '`').collect::<String>();
|
||||
let body = &body[ticks.len()..];
|
||||
let body = eco_format!("{ticks}typ{body}");
|
||||
|
||||
Ok(Value::Content(body))
|
||||
}
|
||||
|
|
@ -1,21 +1,13 @@
|
|||
use core::fmt;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use ecow::{eco_format, EcoString};
|
||||
use parking_lot::Mutex;
|
||||
use ecow::EcoString;
|
||||
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, Span, VirtualPath},
|
||||
};
|
||||
use typst::syntax::Span;
|
||||
|
||||
use super::tidy::*;
|
||||
use crate::analysis::{ParamAttrs, ParamSpec, Signature};
|
||||
use crate::docs::library;
|
||||
use crate::prelude::*;
|
||||
use crate::ty::Ty;
|
||||
use crate::ty::{DocSource, Interned};
|
||||
|
|
@ -24,12 +16,12 @@ use crate::upstream::plain_docs_sentence;
|
|||
type TypeRepr = Option<(/* short */ String, /* long */ String)>;
|
||||
type ShowTypeRepr<'a> = &'a mut dyn FnMut(Option<&Ty>) -> TypeRepr;
|
||||
|
||||
/// Documentation about a symbol (without type information).
|
||||
/// Documentation about a definition (without type information).
|
||||
pub type UntypedDefDocs = DefDocsT<()>;
|
||||
/// Documentation about a symbol.
|
||||
/// Documentation about a definition.
|
||||
pub type DefDocs = DefDocsT<TypeRepr>;
|
||||
|
||||
/// Documentation about a symbol.
|
||||
/// Documentation about a definition.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum DefDocsT<T> {
|
||||
|
|
@ -263,7 +255,7 @@ fn format_ty(ty: Option<&Ty>, doc_ty: Option<&mut ShowTypeRepr>) -> TypeRepr {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn variable_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs> {
|
||||
pub(crate) fn var_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs> {
|
||||
let source = ctx.source_by_id(pos.id()?).ok()?;
|
||||
let type_info = ctx.type_check(&source);
|
||||
let ty = type_info.type_of_span(pos)?;
|
||||
|
|
@ -299,10 +291,7 @@ pub(crate) fn variable_docs(ctx: &mut LocalContext, pos: Span) -> Option<VarDocs
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn signature_docs(
|
||||
sig: &Signature,
|
||||
mut doc_ty: Option<ShowTypeRepr>,
|
||||
) -> Option<SignatureDocs> {
|
||||
pub(crate) fn sig_docs(sig: &Signature, mut doc_ty: Option<ShowTypeRepr>) -> Option<SignatureDocs> {
|
||||
let type_sig = sig.type_sig().clone();
|
||||
|
||||
let pos_in = sig
|
||||
|
|
@ -344,34 +333,3 @@ pub(crate) fn signature_docs(
|
|||
hover_docs: OnceLock::new(),
|
||||
})
|
||||
}
|
||||
|
||||
// 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.replace("```example", "```typ"))
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use ecow::eco_format;
|
||||
use typlite::value::*;
|
||||
|
||||
pub(super) fn lib() -> Arc<typlite::scopes::Scopes<Value>> {
|
||||
let mut scopes = typlite::library::library();
|
||||
|
||||
// todo: how to import this function correctly?
|
||||
scopes.define("example", example as RawFunc);
|
||||
|
||||
Arc::new(scopes)
|
||||
}
|
||||
|
||||
/// Evaluate a `example`.
|
||||
pub fn example(mut args: Args) -> typlite::Result<Value> {
|
||||
let body = get_pos_named!(args, body: Content).0;
|
||||
let body = body.trim();
|
||||
let ticks = body.chars().take_while(|t| *t == '`').collect::<String>();
|
||||
let body = &body[ticks.len()..];
|
||||
let body = eco_format!("{ticks}typ{body}");
|
||||
|
||||
Ok(Value::Content(body))
|
||||
}
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
//! Documentation generation utilities.
|
||||
//! Documentation utilities.
|
||||
|
||||
mod library;
|
||||
mod convert;
|
||||
mod def;
|
||||
mod module;
|
||||
mod package;
|
||||
mod symbol;
|
||||
mod tidy;
|
||||
|
||||
use reflexo::path::unix_slash;
|
||||
use typst::syntax::FileId;
|
||||
|
||||
pub(crate) use convert::convert_docs;
|
||||
pub use def::*;
|
||||
pub use module::*;
|
||||
pub use package::*;
|
||||
pub use symbol::*;
|
||||
pub(crate) use tidy::*;
|
||||
|
||||
fn file_id_repr(k: FileId) -> String {
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ use typst::syntax::FileId;
|
|||
use crate::docs::file_id_repr;
|
||||
use crate::syntax::{Decl, DefKind, Expr, ExprInfo};
|
||||
use crate::ty::Interned;
|
||||
use crate::AnalysisContext;
|
||||
use crate::LocalContext;
|
||||
|
||||
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> {
|
||||
/// Get documentation of definitions in a package.
|
||||
pub fn package_module_docs(ctx: &mut LocalContext, pkg: &PackageInfo) -> StrResult<PackageDefInfo> {
|
||||
let toml_id = get_manifest_id(pkg)?;
|
||||
let manifest = ctx.get_manifest(toml_id)?;
|
||||
|
||||
|
|
@ -26,12 +26,12 @@ pub fn package_module_docs(ctx: &mut AnalysisContext, pkg: &PackageInfo) -> StrR
|
|||
module_docs(ctx, entry_point)
|
||||
}
|
||||
|
||||
/// Get documentation of symbols in a module.
|
||||
pub fn module_docs(ctx: &mut AnalysisContext, entry_point: FileId) -> StrResult<SymbolsInfo> {
|
||||
/// Get documentation of definitions in a module.
|
||||
pub fn module_docs(ctx: &mut LocalContext, entry_point: FileId) -> StrResult<PackageDefInfo> {
|
||||
let mut aliases = HashMap::new();
|
||||
let mut extras = vec![];
|
||||
|
||||
let mut scan_ctx = ScanSymbolCtx {
|
||||
let mut scan_ctx = ScanDefCtx {
|
||||
ctx,
|
||||
root: entry_point,
|
||||
for_spec: entry_point.package(),
|
||||
|
|
@ -43,7 +43,7 @@ pub fn module_docs(ctx: &mut AnalysisContext, entry_point: FileId) -> StrResult<
|
|||
.ctx
|
||||
.expr_stage_by_id(entry_point)
|
||||
.ok_or("entry point not found")?;
|
||||
let mut symbols = scan_ctx.module_sym(eco_vec![], ei);
|
||||
let mut defs = scan_ctx.defs(eco_vec![], ei);
|
||||
|
||||
let module_uses = aliases
|
||||
.into_iter()
|
||||
|
|
@ -55,72 +55,64 @@ pub fn module_docs(ctx: &mut AnalysisContext, entry_point: FileId) -> StrResult<
|
|||
|
||||
log::debug!("module_uses: {module_uses:#?}",);
|
||||
|
||||
symbols.children.extend(extras);
|
||||
defs.children.extend(extras);
|
||||
|
||||
Ok(SymbolsInfo {
|
||||
root: symbols,
|
||||
Ok(PackageDefInfo {
|
||||
root: defs,
|
||||
module_uses,
|
||||
})
|
||||
}
|
||||
|
||||
/// Information about a symbol.
|
||||
/// Information about a definition.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct SymbolInfoHead {
|
||||
/// The name of the symbol.
|
||||
pub struct DefInfo {
|
||||
/// The name of the definition.
|
||||
pub name: EcoString,
|
||||
/// The kind of the symbol.
|
||||
/// The kind of the definition.
|
||||
pub kind: DefKind,
|
||||
/// The location (file, start, end) of the symbol.
|
||||
/// The location (file, start, end) of the definition.
|
||||
pub loc: Option<(usize, usize, usize)>,
|
||||
/// Is the symbol reexport
|
||||
pub export_again: bool,
|
||||
/// Is the symbol reexport
|
||||
/// Whether the definition external to the module.
|
||||
pub is_external: bool,
|
||||
/// The link to the definition if it is external.
|
||||
pub external_link: Option<String>,
|
||||
/// The one-line documentation of the symbol.
|
||||
/// The one-line documentation of the definition.
|
||||
pub oneliner: Option<String>,
|
||||
/// The raw documentation of the symbol.
|
||||
/// The raw documentation of the definition.
|
||||
pub docs: Option<EcoString>,
|
||||
/// The parsed documentation of the symbol.
|
||||
/// The parsed documentation of the definition.
|
||||
pub parsed_docs: Option<DefDocs>,
|
||||
/// The value of the symbol.
|
||||
/// The value of the definition.
|
||||
#[serde(skip)]
|
||||
pub constant: Option<EcoString>,
|
||||
/// The name range of the symbol.
|
||||
/// The value of the symbol.
|
||||
/// The name range of the definition.
|
||||
/// The value of the definition.
|
||||
#[serde(skip)]
|
||||
pub decl: Option<Interned<Decl>>,
|
||||
/// The children of the definition.
|
||||
pub children: EcoVec<DefInfo>,
|
||||
}
|
||||
|
||||
/// Information about a symbol.
|
||||
/// Information about the definitions in a package.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SymbolInfo {
|
||||
/// The primary information about the symbol.
|
||||
#[serde(flatten)]
|
||||
pub head: SymbolInfoHead,
|
||||
/// The children of the symbol.
|
||||
pub children: EcoVec<SymbolInfo>,
|
||||
}
|
||||
|
||||
/// Information about the symbols in a package.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SymbolsInfo {
|
||||
pub struct PackageDefInfo {
|
||||
/// The root module information.
|
||||
#[serde(flatten)]
|
||||
pub root: SymbolInfo,
|
||||
pub root: DefInfo,
|
||||
/// The module accessible paths.
|
||||
pub module_uses: HashMap<String, EcoVec<String>>,
|
||||
}
|
||||
|
||||
struct ScanSymbolCtx<'a, 'w> {
|
||||
ctx: &'a mut AnalysisContext<'w>,
|
||||
struct ScanDefCtx<'a> {
|
||||
ctx: &'a mut LocalContext,
|
||||
for_spec: Option<&'a PackageSpec>,
|
||||
aliases: &'a mut HashMap<FileId, Vec<String>>,
|
||||
extras: &'a mut Vec<SymbolInfo>,
|
||||
extras: &'a mut Vec<DefInfo>,
|
||||
root: FileId,
|
||||
}
|
||||
|
||||
impl ScanSymbolCtx<'_, '_> {
|
||||
fn module_sym(&mut self, path: EcoVec<&str>, ei: Arc<ExprInfo>) -> SymbolInfo {
|
||||
impl ScanDefCtx<'_> {
|
||||
fn defs(&mut self, path: EcoVec<&str>, ei: Arc<ExprInfo>) -> DefInfo {
|
||||
let name = {
|
||||
let stem = ei.fid.vpath().as_rooted_path().file_stem();
|
||||
stem.and_then(|s| Some(Interned::new_str(s.to_str()?)))
|
||||
|
|
@ -129,7 +121,7 @@ impl ScanSymbolCtx<'_, '_> {
|
|||
let module_decl = Decl::module(name.clone(), ei.fid).into();
|
||||
let site = Some(self.root);
|
||||
let p = path.clone();
|
||||
self.sym(&name, p, site.as_ref(), &module_decl, None)
|
||||
self.def(&name, p, site.as_ref(), &module_decl, None)
|
||||
}
|
||||
|
||||
fn expr(
|
||||
|
|
@ -138,9 +130,9 @@ impl ScanSymbolCtx<'_, '_> {
|
|||
path: EcoVec<&str>,
|
||||
site: Option<&FileId>,
|
||||
val: &Expr,
|
||||
) -> SymbolInfo {
|
||||
) -> DefInfo {
|
||||
match val {
|
||||
Expr::Decl(d) => self.sym(key, path, site, d, Some(val)),
|
||||
Expr::Decl(d) => self.def(key, path, site, d, Some(val)),
|
||||
Expr::Ref(r) if r.root.is_some() => {
|
||||
self.expr(key, path, site, r.root.as_ref().unwrap())
|
||||
}
|
||||
|
|
@ -148,55 +140,38 @@ impl ScanSymbolCtx<'_, '_> {
|
|||
Expr::Select(..) => {
|
||||
let mut path = path.clone();
|
||||
path.push(key);
|
||||
let head = SymbolInfoHead {
|
||||
DefInfo {
|
||||
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 {
|
||||
DefInfo {
|
||||
name: key.to_string().into(),
|
||||
kind: DefKind::Constant,
|
||||
..Default::default()
|
||||
};
|
||||
SymbolInfo {
|
||||
head,
|
||||
children: eco_vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sym(
|
||||
fn def(
|
||||
&mut self,
|
||||
key: &str,
|
||||
path: EcoVec<&str>,
|
||||
site: Option<&FileId>,
|
||||
val: &Interned<Decl>,
|
||||
decl: &Interned<Decl>,
|
||||
expr: Option<&Expr>,
|
||||
) -> SymbolInfo {
|
||||
let mut head = create_head(self.ctx, key, val, expr);
|
||||
|
||||
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());
|
||||
head.docs = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let children = match val.as_ref() {
|
||||
Decl::Module(..) => val.file_id().and_then(|fid| {
|
||||
) -> DefInfo {
|
||||
let def = self.ctx.def_of_decl(decl);
|
||||
let def_docs = def.and_then(|def| self.ctx.def_docs(&def));
|
||||
let docs = def_docs.as_ref().map(|d| d.docs().clone());
|
||||
let children = match decl.as_ref() {
|
||||
Decl::Module(..) => decl.file_id().and_then(|fid| {
|
||||
// only generate docs for the same package
|
||||
if fid.package() != self.for_spec {
|
||||
return None;
|
||||
|
|
@ -230,6 +205,28 @@ impl ScanSymbolCtx<'_, '_> {
|
|||
_ => None,
|
||||
};
|
||||
|
||||
let mut head = DefInfo {
|
||||
name: key.to_string().into(),
|
||||
kind: decl.kind(),
|
||||
constant: expr.map(|e| e.repr()),
|
||||
docs,
|
||||
parsed_docs: def_docs,
|
||||
decl: Some(decl.clone()),
|
||||
children: children.unwrap_or_default(),
|
||||
loc: None,
|
||||
is_external: false,
|
||||
external_link: None,
|
||||
oneliner: None,
|
||||
};
|
||||
|
||||
if let Some((span, mod_fid)) = head.decl.as_ref().and_then(|d| d.file_id()).zip(site) {
|
||||
if span != *mod_fid {
|
||||
head.is_external = true;
|
||||
head.oneliner = head.docs.as_deref().map(oneliner).map(|e| e.to_owned());
|
||||
head.docs = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert module that is not exported
|
||||
if let Some(fid) = head.decl.as_ref().and_then(|d| d.file_id()) {
|
||||
// only generate docs for the same package
|
||||
|
|
@ -243,37 +240,14 @@ impl ScanSymbolCtx<'_, '_> {
|
|||
|
||||
log::debug!("found internal module: {path:?}");
|
||||
if let Some(m) = src {
|
||||
let msym = self.module_sym(path, m);
|
||||
let msym = self.defs(path, m);
|
||||
self.extras.push(msym)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let children = children.unwrap_or_default();
|
||||
SymbolInfo { head, children }
|
||||
}
|
||||
}
|
||||
|
||||
fn create_head(
|
||||
ctx: &mut AnalysisContext,
|
||||
k: &str,
|
||||
decl: &Interned<Decl>,
|
||||
expr: Option<&Expr>,
|
||||
) -> SymbolInfoHead {
|
||||
let kind = decl.kind();
|
||||
|
||||
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: expr.map(|e| e.repr()),
|
||||
docs,
|
||||
parsed_docs,
|
||||
decl: Some(decl.clone()),
|
||||
..Default::default()
|
||||
head
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ use typst::syntax::package::{PackageManifest, PackageSpec};
|
|||
use typst::syntax::{FileId, Span, VirtualPath};
|
||||
use typst::World;
|
||||
|
||||
use crate::docs::{file_id_repr, module_docs, DefDocs, SymbolsInfo};
|
||||
use crate::AnalysisContext;
|
||||
use crate::docs::{file_id_repr, module_docs, DefDocs, PackageDefInfo};
|
||||
use crate::LocalContext;
|
||||
|
||||
/// Check Package.
|
||||
pub fn check_package(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<()> {
|
||||
pub fn check_package(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<()> {
|
||||
let toml_id = get_manifest_id(spec)?;
|
||||
let manifest = ctx.get_manifest(toml_id)?;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ pub fn check_package(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult
|
|||
}
|
||||
|
||||
/// Generate full documents in markdown format
|
||||
pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<String> {
|
||||
pub fn package_docs(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<String> {
|
||||
log::info!("generate_md_docs {spec:?}");
|
||||
|
||||
let mut md = String::new();
|
||||
|
|
@ -37,7 +37,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
|
||||
ctx.preload_package(entry_point);
|
||||
|
||||
let SymbolsInfo { root, module_uses } = module_docs(ctx, entry_point)?;
|
||||
let PackageDefInfo { root, module_uses } = module_docs(ctx, entry_point)?;
|
||||
|
||||
log::debug!("module_uses: {module_uses:#?}");
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
let package_meta = jbase64(&meta);
|
||||
let _ = writeln!(md, "<!-- begin:package {package_meta} -->");
|
||||
|
||||
let mut modules_to_generate = vec![(root.head.name.clone(), root)];
|
||||
let mut modules_to_generate = vec![(root.name.clone(), root)];
|
||||
let mut generated_modules = HashSet::new();
|
||||
let mut file_ids: IndexSet<FileId> = IndexSet::new();
|
||||
|
||||
|
|
@ -82,11 +82,11 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
};
|
||||
|
||||
while !modules_to_generate.is_empty() {
|
||||
for (parent_ident, sym) in std::mem::take(&mut modules_to_generate) {
|
||||
for (parent_ident, def) in std::mem::take(&mut modules_to_generate) {
|
||||
// parent_ident, symbols
|
||||
let symbols = sym.children;
|
||||
let children = def.children;
|
||||
|
||||
let module_val = sym.head.decl.as_ref().unwrap();
|
||||
let module_val = def.decl.as_ref().unwrap();
|
||||
let fid = module_val.file_id();
|
||||
let aka = fid.map(&mut akas).unwrap_or_default();
|
||||
|
||||
|
|
@ -111,15 +111,15 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
}
|
||||
let m = jbase64(&ModuleInfo {
|
||||
prefix: primary.as_str().into(),
|
||||
name: sym.head.name.clone(),
|
||||
name: def.name.clone(),
|
||||
loc: persist_fid,
|
||||
parent_ident: parent_ident.clone(),
|
||||
aka,
|
||||
});
|
||||
let _ = writeln!(md, "<!-- begin:module {primary} {m} -->");
|
||||
|
||||
for mut sym in symbols {
|
||||
let span = sym.head.decl.as_ref().map(|d| d.span());
|
||||
for mut child in children {
|
||||
let span = child.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;
|
||||
|
|
@ -128,13 +128,13 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
Some((fid, rng.start, rng.end))
|
||||
})
|
||||
});
|
||||
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 child_fid = child.decl.as_ref().and_then(|d| d.file_id());
|
||||
let child_fid = child_fid.or_else(|| span.and_then(Span::id)).or(fid);
|
||||
let span = fid_range.or_else(|| {
|
||||
let fid = sym_fid?;
|
||||
let fid = child_fid?;
|
||||
Some((file_ids.insert_full(fid).0, 0, 0))
|
||||
});
|
||||
sym.head.loc = span;
|
||||
child.loc = span;
|
||||
// .ok_or_else(|| {
|
||||
// let err = format!("failed to convert docs in {title}").replace(
|
||||
// "-->", "—>", // avoid markdown comment
|
||||
|
|
@ -142,7 +142,7 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
// log::error!("{err}");
|
||||
// err
|
||||
// })
|
||||
let docs = sym.head.parsed_docs.clone();
|
||||
let docs = child.parsed_docs.clone();
|
||||
// Err(e) => {
|
||||
// let err = format!("failed to convert docs: {e}").replace(
|
||||
// "-->", "—>", // avoid markdown comment
|
||||
|
|
@ -154,8 +154,8 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
let convert_err = None::<EcoString>;
|
||||
match &docs {
|
||||
Some(docs) => {
|
||||
sym.head.parsed_docs = Some(docs.clone());
|
||||
sym.head.docs = None;
|
||||
child.parsed_docs = Some(docs.clone());
|
||||
child.docs = None;
|
||||
}
|
||||
None => {
|
||||
// let err = format!("failed to convert docs in {title}:
|
||||
|
|
@ -168,53 +168,54 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
}
|
||||
|
||||
let ident = if !primary.is_empty() {
|
||||
eco_format!("symbol-{}-{primary}.{}", sym.head.kind, sym.head.name)
|
||||
eco_format!("symbol-{}-{primary}.{}", child.kind, child.name)
|
||||
} else {
|
||||
eco_format!("symbol-{}-{}", sym.head.kind, sym.head.name)
|
||||
eco_format!("symbol-{}-{}", child.kind, child.name)
|
||||
};
|
||||
let _ = writeln!(md, "### {}: {} in {primary}", sym.head.kind, sym.head.name);
|
||||
let _ = writeln!(md, "### {}: {} in {primary}", child.kind, child.name);
|
||||
|
||||
if sym.head.export_again {
|
||||
if let Some(fid) = sym_fid {
|
||||
if child.is_external {
|
||||
if let Some(fid) = child_fid {
|
||||
let lnk = if fid.package() == Some(for_spec) {
|
||||
let sub_aka = akas(fid);
|
||||
let sub_primary = sub_aka.first().cloned().unwrap_or_default();
|
||||
sym.head.external_link = Some(format!(
|
||||
child.external_link = Some(format!(
|
||||
"#symbol-{}-{sub_primary}.{}",
|
||||
sym.head.kind, sym.head.name
|
||||
child.kind, child.name
|
||||
));
|
||||
format!("#{}-{}-in-{sub_primary}", sym.head.kind, sym.head.name)
|
||||
format!("#{}-{}-in-{sub_primary}", child.kind, child.name)
|
||||
.replace(".", "")
|
||||
} else if let Some(spec) = fid.package() {
|
||||
let lnk = format!(
|
||||
"https://typst.app/universe/package/{}/{}",
|
||||
spec.name, spec.version
|
||||
);
|
||||
sym.head.external_link = Some(lnk.clone());
|
||||
child.external_link = Some(lnk.clone());
|
||||
lnk
|
||||
} else {
|
||||
let lnk: String = "https://typst.app/docs".into();
|
||||
sym.head.external_link = Some(lnk.clone());
|
||||
child.external_link = Some(lnk.clone());
|
||||
lnk
|
||||
};
|
||||
let _ = writeln!(md, "[Symbol Docs]({lnk})\n");
|
||||
}
|
||||
}
|
||||
|
||||
let head = jbase64(&sym.head);
|
||||
let child_children = std::mem::take(&mut child.children);
|
||||
let head = jbase64(&child);
|
||||
let _ = writeln!(md, "<!-- begin:symbol {ident} {head} -->");
|
||||
|
||||
if let Some(DefDocs::Function(sig)) = &sym.head.parsed_docs {
|
||||
if let Some(DefDocs::Function(sig)) = &child.parsed_docs {
|
||||
let _ = writeln!(md, "<!-- begin:sig -->");
|
||||
let _ = writeln!(md, "```typc");
|
||||
let _ = write!(md, "let {}", sym.head.name);
|
||||
let _ = write!(md, "let {}", child.name);
|
||||
let _ = sig.print(&mut md);
|
||||
let _ = writeln!(md, ";");
|
||||
let _ = writeln!(md, "```");
|
||||
let _ = writeln!(md, "<!-- end:sig -->");
|
||||
}
|
||||
|
||||
match (&sym.head.parsed_docs, convert_err) {
|
||||
match (&child.parsed_docs, convert_err) {
|
||||
(_, Some(err)) => {
|
||||
let err = format!("failed to convert docs in {title}: {err}").replace(
|
||||
"-->", "—>", // avoid markdown comment
|
||||
|
|
@ -244,8 +245,8 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
(None, None) => {}
|
||||
}
|
||||
|
||||
let plain_docs = sym.head.docs.as_deref();
|
||||
let plain_docs = plain_docs.or(sym.head.oneliner.as_deref());
|
||||
let plain_docs = child.docs.as_deref();
|
||||
let plain_docs = plain_docs.or(child.oneliner.as_deref());
|
||||
|
||||
if let Some(docs) = plain_docs {
|
||||
let contains_code = docs.contains("```");
|
||||
|
|
@ -258,9 +259,9 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
}
|
||||
}
|
||||
|
||||
if !sym.children.is_empty() {
|
||||
log::debug!("sub_fid: {sym_fid:?}");
|
||||
match sym_fid {
|
||||
if !child_children.is_empty() {
|
||||
log::debug!("sub_fid: {child_fid:?}");
|
||||
match child_fid {
|
||||
Some(fid) => {
|
||||
let aka = akas(fid);
|
||||
let primary = aka.first().cloned().unwrap_or_default();
|
||||
|
|
@ -268,7 +269,8 @@ pub fn package_docs(ctx: &mut AnalysisContext, spec: &PackageInfo) -> StrResult<
|
|||
let _ = writeln!(md, "[Module Docs](#{link})\n");
|
||||
|
||||
if generated_modules.insert(fid) {
|
||||
modules_to_generate.push((ident.clone(), sym));
|
||||
child.children = child_children;
|
||||
modules_to_generate.push((ident.clone(), child));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue