mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 13:13:43 +00:00
dev: collect func.with
type information for signature help (#759)
* feat: static analysis on `func.with` bindings * test: update snapshot
This commit is contained in:
parent
67367b03bf
commit
f1f77065d7
16 changed files with 163 additions and 230 deletions
|
@ -376,7 +376,6 @@ mod signature_tests {
|
|||
|
||||
use core::fmt;
|
||||
|
||||
use typst::foundations::Repr;
|
||||
use typst::syntax::LinkedNode;
|
||||
use typst_shim::syntax::LinkedNodeExt;
|
||||
|
||||
|
@ -424,11 +423,9 @@ mod signature_tests {
|
|||
if let Some(name) = &arg.name {
|
||||
write!(f, "{name}: ")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"{}, ",
|
||||
arg.value.as_ref().map(|v| v.repr()).unwrap_or_default()
|
||||
)?;
|
||||
let term = arg.term.as_ref();
|
||||
let term = term.and_then(|v| v.describe()).unwrap_or_default();
|
||||
write!(f, "{term}, ")?;
|
||||
}
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
|
|
|
@ -813,7 +813,7 @@ 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)
|
||||
crate::package::get_manifest(&self.world, toml_id)
|
||||
}
|
||||
|
||||
/// Compute the signature of a function.
|
||||
|
|
|
@ -4,12 +4,14 @@ use itertools::Either;
|
|||
use tinymist_derive::BindTyCtx;
|
||||
use typst::foundations::{Closure, ParamInfo};
|
||||
|
||||
use super::{prelude::*, BoundChecker, Definition, DocSource, SharedContext, SigTy, TypeVar};
|
||||
use super::{
|
||||
prelude::*, BoundChecker, Definition, DocSource, SharedContext, SigTy, SigWithTy, TypeVar,
|
||||
};
|
||||
use crate::analysis::PostTypeChecker;
|
||||
use crate::docs::{UntypedDefDocs, UntypedSignatureDocs, UntypedVarDocs};
|
||||
use crate::syntax::get_non_strict_def_target;
|
||||
use crate::ty::TyCtx;
|
||||
use crate::ty::TypeBounds;
|
||||
use crate::ty::{InsTy, TyCtx};
|
||||
use crate::upstream::truncated_repr;
|
||||
|
||||
/// Describes a function parameter.
|
||||
|
@ -121,6 +123,17 @@ impl Signature {
|
|||
// todo: with stack
|
||||
primary
|
||||
}
|
||||
|
||||
pub(crate) fn param_shift(&self, _ctx: &mut LocalContext) -> usize {
|
||||
match self {
|
||||
Signature::Primary(_) => 0,
|
||||
Signature::Partial(sig) => sig
|
||||
.with_stack
|
||||
.iter()
|
||||
.map(|ws| ws.items.len())
|
||||
.sum::<usize>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a primary function signature.
|
||||
|
@ -200,9 +213,9 @@ impl PrimarySignature {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct ArgInfo {
|
||||
/// The argument's name.
|
||||
pub name: Option<EcoString>,
|
||||
/// The argument's value.
|
||||
pub value: Option<Value>,
|
||||
pub name: Option<StrRef>,
|
||||
/// The argument's term.
|
||||
pub term: Option<Ty>,
|
||||
}
|
||||
|
||||
/// Describes a function argument list.
|
||||
|
@ -289,7 +302,7 @@ 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()) {
|
||||
let (var_with, docstring) = match type_info.var_docs.get(&v.def).map(|x| x.as_ref()) {
|
||||
Some(UntypedDefDocs::Function(sig)) => (vec![], Either::Left(sig.as_ref())),
|
||||
Some(UntypedDefDocs::Variable(d)) => find_alias_stack(&mut ty_ctx, &v, d)?,
|
||||
_ => return None,
|
||||
|
@ -297,7 +310,7 @@ fn analyze_type_signature(
|
|||
|
||||
let docstring = match docstring {
|
||||
Either::Left(docstring) => docstring,
|
||||
Either::Right(f) => return Some(ctx.type_of_func(f)),
|
||||
Either::Right(f) => return Some(wind_stack(var_with, ctx.type_of_func(f))),
|
||||
};
|
||||
|
||||
let mut param_specs = Vec::new();
|
||||
|
@ -356,13 +369,14 @@ fn analyze_type_signature(
|
|||
});
|
||||
}
|
||||
|
||||
Some(Signature::Primary(Arc::new(PrimarySignature {
|
||||
let sig = Signature::Primary(Arc::new(PrimarySignature {
|
||||
docs: Some(docstring.docs.clone()),
|
||||
param_specs,
|
||||
has_fill_or_size_or_stroke,
|
||||
sig_ty,
|
||||
_broken,
|
||||
})))
|
||||
}));
|
||||
Some(wind_stack(var_with, sig))
|
||||
}
|
||||
src @ (DocSource::Builtin(..) | DocSource::Ins(..)) => {
|
||||
Some(ctx.type_of_func(src.as_func()?))
|
||||
|
@ -370,17 +384,54 @@ fn analyze_type_signature(
|
|||
}
|
||||
}
|
||||
|
||||
fn wind_stack(var_with: Vec<WithElem>, sig: Signature) -> Signature {
|
||||
if var_with.is_empty() {
|
||||
return sig;
|
||||
}
|
||||
|
||||
let (primary, mut base_args) = match sig {
|
||||
Signature::Primary(primary) => (primary, eco_vec![]),
|
||||
Signature::Partial(partial) => (partial.signature.clone(), partial.with_stack.clone()),
|
||||
};
|
||||
|
||||
let mut accepting = primary.pos().iter().skip(base_args.len());
|
||||
|
||||
// Ignoring docs at the moment
|
||||
for (_d, w) in var_with {
|
||||
if let Some(w) = w {
|
||||
let mut items = eco_vec![];
|
||||
for pos in w.with.positional_params() {
|
||||
let Some(arg) = accepting.next() else {
|
||||
break;
|
||||
};
|
||||
items.push(ArgInfo {
|
||||
name: Some(arg.name.clone()),
|
||||
term: Some(pos.clone()),
|
||||
});
|
||||
}
|
||||
// todo: ignored spread arguments
|
||||
if !items.is_empty() {
|
||||
base_args.push(ArgsInfo { items });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Signature::Partial(Arc::new(PartialSignature {
|
||||
signature: primary,
|
||||
with_stack: base_args,
|
||||
}))
|
||||
}
|
||||
|
||||
type WithElem<'a> = (&'a UntypedVarDocs, Option<Interned<SigWithTy>>);
|
||||
|
||||
fn find_alias_stack<'a>(
|
||||
ctx: &'a mut PostTypeChecker,
|
||||
v: &Interned<TypeVar>,
|
||||
d: &'a UntypedVarDocs,
|
||||
) -> Option<(
|
||||
Vec<&'a UntypedVarDocs>,
|
||||
Either<&'a UntypedSignatureDocs, Func>,
|
||||
)> {
|
||||
) -> Option<(Vec<WithElem<'a>>, Either<&'a UntypedSignatureDocs, Func>)> {
|
||||
let mut checker = AliasStackChecker {
|
||||
ctx,
|
||||
stack: vec![d],
|
||||
stack: vec![(d, None)],
|
||||
res: None,
|
||||
checking_with: true,
|
||||
};
|
||||
|
@ -393,7 +444,7 @@ fn find_alias_stack<'a>(
|
|||
#[bind(ctx)]
|
||||
struct AliasStackChecker<'a, 'b> {
|
||||
ctx: &'a mut PostTypeChecker<'b>,
|
||||
stack: Vec<&'a UntypedVarDocs>,
|
||||
stack: Vec<WithElem<'a>>,
|
||||
res: Option<Either<&'a UntypedSignatureDocs, Func>>,
|
||||
checking_with: bool,
|
||||
}
|
||||
|
@ -420,7 +471,7 @@ impl<'a, 'b> BoundChecker for AliasStackChecker<'a, 'b> {
|
|||
}
|
||||
Some(UntypedDefDocs::Variable(d)) => {
|
||||
self.checking_with = true;
|
||||
self.stack.push(d);
|
||||
self.stack.push((d, None));
|
||||
self.check_var_rec(u, pol);
|
||||
self.stack.pop();
|
||||
self.checking_with = false;
|
||||
|
@ -437,6 +488,7 @@ impl<'a, 'b> BoundChecker for AliasStackChecker<'a, 'b> {
|
|||
match (self.checking_with, ty) {
|
||||
(true, Ty::With(w)) => {
|
||||
log::debug!("collecting with {ty:?} {pol:?}");
|
||||
self.stack.last_mut().unwrap().1 = Some(w.clone());
|
||||
self.checking_with = false;
|
||||
w.sig.bounds(pol, self);
|
||||
self.checking_with = true;
|
||||
|
@ -485,7 +537,7 @@ fn analyze_dyn_signature(
|
|||
.iter()
|
||||
.map(|arg| ArgInfo {
|
||||
name: arg.name.clone().map(From::from),
|
||||
value: Some(arg.value.v.clone()),
|
||||
term: Some(Ty::Value(InsTy::new(arg.value.v.clone()))),
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
|
|
|
@ -11,11 +11,12 @@ use typst::syntax::package::PackageSpec;
|
|||
use typst::syntax::FileId;
|
||||
|
||||
use crate::docs::file_id_repr;
|
||||
use crate::package::{get_manifest_id, PackageInfo};
|
||||
use crate::syntax::{Decl, DefKind, Expr, ExprInfo};
|
||||
use crate::ty::Interned;
|
||||
use crate::LocalContext;
|
||||
|
||||
use super::{get_manifest_id, DefDocs, PackageInfo};
|
||||
use super::DefDocs;
|
||||
|
||||
/// Get documentation of definitions in a package.
|
||||
pub fn package_module_docs(ctx: &mut LocalContext, pkg: &PackageInfo) -> StrResult<PackageDefInfo> {
|
||||
|
|
|
@ -6,24 +6,13 @@ use ecow::{EcoString, EcoVec};
|
|||
use indexmap::IndexSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typst::diag::{eco_format, StrResult};
|
||||
use typst::syntax::package::{PackageManifest, PackageSpec};
|
||||
use typst::syntax::{FileId, Span, VirtualPath};
|
||||
use typst::World;
|
||||
use typst::syntax::package::PackageManifest;
|
||||
use typst::syntax::{FileId, Span};
|
||||
|
||||
use crate::docs::{file_id_repr, module_docs, DefDocs, PackageDefInfo};
|
||||
use crate::package::{get_manifest_id, PackageInfo};
|
||||
use crate::LocalContext;
|
||||
|
||||
/// Check Package.
|
||||
pub fn check_package(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<()> {
|
||||
let toml_id = get_manifest_id(spec)?;
|
||||
let manifest = ctx.get_manifest(toml_id)?;
|
||||
|
||||
let entry_point = toml_id.join(&manifest.package.entrypoint);
|
||||
|
||||
ctx.shared_().preload_package(entry_point);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate full documents in markdown format
|
||||
pub fn package_docs(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<String> {
|
||||
log::info!("generate_md_docs {spec:?}");
|
||||
|
@ -326,55 +315,6 @@ pub fn package_docs(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<Str
|
|||
Ok(md)
|
||||
}
|
||||
|
||||
/// Parses the manifest of the package located at `package_path`.
|
||||
pub fn get_manifest_id(spec: &PackageInfo) -> StrResult<FileId> {
|
||||
Ok(FileId::new(
|
||||
Some(PackageSpec {
|
||||
namespace: spec.namespace.clone(),
|
||||
name: spec.name.clone(),
|
||||
version: spec.version.parse()?,
|
||||
}),
|
||||
VirtualPath::new("typst.toml"),
|
||||
))
|
||||
}
|
||||
|
||||
/// Parses the manifest of the package located at `package_path`.
|
||||
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))?;
|
||||
|
||||
let string = std::str::from_utf8(&toml_data)
|
||||
.map_err(|err| eco_format!("package manifest is not valid UTF-8 ({})", err))?;
|
||||
|
||||
toml::from_str(string)
|
||||
.map_err(|err| eco_format!("package manifest is malformed ({})", err.message()))
|
||||
}
|
||||
|
||||
/// Information about a package.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PackageInfo {
|
||||
/// The path to the package if any.
|
||||
pub path: PathBuf,
|
||||
/// The namespace the package lives in.
|
||||
pub namespace: EcoString,
|
||||
/// The name of the package within its namespace.
|
||||
pub name: EcoString,
|
||||
/// The package's version.
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl From<(PathBuf, PackageSpec)> for PackageInfo {
|
||||
fn from((path, spec): (PathBuf, PackageSpec)) -> Self {
|
||||
Self {
|
||||
path,
|
||||
namespace: spec.namespace,
|
||||
name: spec.name,
|
||||
version: spec.version.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn jbase64<T: Serialize>(s: &T) -> String {
|
||||
use base64::Engine;
|
||||
let content = serde_json::to_string(s).unwrap();
|
||||
|
|
|
@ -3,6 +3,7 @@ source: crates/tinymist-query/src/analysis.rs
|
|||
expression: SignatureSnapshot(result.as_ref())
|
||||
input_file: crates/tinymist-query/src/fixtures/signature/builtin_with.typ
|
||||
---
|
||||
with red: 50%, green: 50%, blue: 50%,
|
||||
fn(
|
||||
red,
|
||||
green,
|
||||
|
|
|
@ -3,6 +3,7 @@ source: crates/tinymist-query/src/analysis.rs
|
|||
expression: SignatureSnapshot(result.as_ref())
|
||||
input_file: crates/tinymist-query/src/fixtures/signature/user_with.typ
|
||||
---
|
||||
with u: 1,
|
||||
fn(
|
||||
u,
|
||||
v,
|
||||
|
|
|
@ -196,12 +196,6 @@ fn def_tooltip(
|
|||
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(&def) {
|
||||
actions.push(link);
|
||||
}
|
||||
|
@ -294,8 +288,7 @@ fn render_actions(results: &mut Vec<MarkedString>, actions: Vec<CommandLink>) {
|
|||
return;
|
||||
}
|
||||
|
||||
let g = actions.into_iter().join(" | ");
|
||||
results.push(MarkedString::String(g));
|
||||
results.push(MarkedString::String(actions.into_iter().join(" | ")));
|
||||
}
|
||||
|
||||
struct ExternalDocLink;
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use lsp_types::{InlayHintKind, InlayHintLabel};
|
||||
|
||||
use crate::{
|
||||
analysis::{analyze_call, ParamKind},
|
||||
prelude::*,
|
||||
SemanticRequest,
|
||||
};
|
||||
|
||||
/// Configuration for inlay hints.
|
||||
|
|
|
@ -5,10 +5,76 @@ use std::path::PathBuf;
|
|||
use std::sync::OnceLock;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use reflexo_typst::package::PackageSpec;
|
||||
use reflexo_typst::typst::prelude::*;
|
||||
use reflexo_typst::{package::PackageSpec, TypstFileId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tinymist_world::https::HttpsRegistry;
|
||||
use typst::diag::EcoString;
|
||||
use typst::diag::{EcoString, StrResult};
|
||||
use typst::syntax::package::PackageManifest;
|
||||
use typst::syntax::VirtualPath;
|
||||
use typst::World;
|
||||
|
||||
use crate::LocalContext;
|
||||
|
||||
/// Information about a package.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PackageInfo {
|
||||
/// The path to the package if any.
|
||||
pub path: PathBuf,
|
||||
/// The namespace the package lives in.
|
||||
pub namespace: EcoString,
|
||||
/// The name of the package within its namespace.
|
||||
pub name: EcoString,
|
||||
/// The package's version.
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl From<(PathBuf, PackageSpec)> for PackageInfo {
|
||||
fn from((path, spec): (PathBuf, PackageSpec)) -> Self {
|
||||
Self {
|
||||
path,
|
||||
namespace: spec.namespace,
|
||||
name: spec.name,
|
||||
version: spec.version.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the manifest of the package located at `package_path`.
|
||||
pub fn get_manifest_id(spec: &PackageInfo) -> StrResult<TypstFileId> {
|
||||
Ok(TypstFileId::new(
|
||||
Some(PackageSpec {
|
||||
namespace: spec.namespace.clone(),
|
||||
name: spec.name.clone(),
|
||||
version: spec.version.parse()?,
|
||||
}),
|
||||
VirtualPath::new("typst.toml"),
|
||||
))
|
||||
}
|
||||
|
||||
/// Parses the manifest of the package located at `package_path`.
|
||||
pub fn get_manifest(world: &dyn World, toml_id: TypstFileId) -> StrResult<PackageManifest> {
|
||||
let toml_data = world
|
||||
.file(toml_id)
|
||||
.map_err(|err| eco_format!("failed to read package manifest ({})", err))?;
|
||||
|
||||
let string = std::str::from_utf8(&toml_data)
|
||||
.map_err(|err| eco_format!("package manifest is not valid UTF-8 ({})", err))?;
|
||||
|
||||
toml::from_str(string)
|
||||
.map_err(|err| eco_format!("package manifest is malformed ({})", err.message()))
|
||||
}
|
||||
|
||||
/// Check Package.
|
||||
pub fn check_package(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<()> {
|
||||
let toml_id = get_manifest_id(spec)?;
|
||||
let manifest = ctx.get_manifest(toml_id)?;
|
||||
|
||||
let entry_point = toml_id.join(&manifest.package.entrypoint);
|
||||
|
||||
ctx.shared_().preload_package(entry_point);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the packages in namespaces and their descriptions.
|
||||
pub fn list_package_by_namespace(
|
||||
|
|
|
@ -3,10 +3,8 @@ use typst_shim::syntax::LinkedNodeExt;
|
|||
|
||||
use crate::{
|
||||
adt::interner::Interned,
|
||||
analysis::Definition,
|
||||
prelude::*,
|
||||
syntax::{find_docs_before, get_check_target, get_deref_target, CheckTarget, ParamTarget},
|
||||
upstream::plain_docs_sentence,
|
||||
syntax::{get_check_target, get_deref_target, CheckTarget, ParamTarget},
|
||||
LspParamInfo, SemanticRequest,
|
||||
};
|
||||
|
||||
|
@ -41,33 +39,14 @@ impl SemanticRequest for SignatureHelpRequest {
|
|||
};
|
||||
|
||||
let deref_target = get_deref_target(callee, cursor)?;
|
||||
|
||||
let def_link = ctx.def_of_syntax(&source, None, deref_target)?;
|
||||
|
||||
let documentation = DocTooltip::get(ctx, &def_link)
|
||||
.as_deref()
|
||||
.map(markdown_docs);
|
||||
|
||||
let Some(Value::Func(function)) = def_link.value() else {
|
||||
return None;
|
||||
};
|
||||
log::trace!("got function {function:?}");
|
||||
|
||||
let mut function = &function;
|
||||
use typst::foundations::func::Repr;
|
||||
let mut param_shift = 0;
|
||||
while let Repr::With(inner) = function.inner() {
|
||||
param_shift += inner.1.items.iter().filter(|x| x.name.is_none()).count();
|
||||
function = &inner.0;
|
||||
}
|
||||
|
||||
let sig = ctx.sig_of_func(function.clone());
|
||||
|
||||
let def = ctx.def_of_syntax(&source, None, deref_target)?;
|
||||
let sig = ctx.sig_of_def(def.clone())?;
|
||||
log::debug!("got signature {sig:?}");
|
||||
|
||||
let param_shift = sig.param_shift(ctx);
|
||||
let mut active_parameter = None;
|
||||
|
||||
let mut label = def_link.name().as_ref().to_owned();
|
||||
let mut label = def.name().as_ref().to_owned();
|
||||
let mut params = Vec::new();
|
||||
|
||||
label.push('(');
|
||||
|
@ -137,7 +116,7 @@ impl SemanticRequest for SignatureHelpRequest {
|
|||
Some(SignatureHelp {
|
||||
signatures: vec![SignatureInformation {
|
||||
label: label.to_string(),
|
||||
documentation,
|
||||
documentation: sig.primary().docs.as_deref().map(markdown_docs),
|
||||
parameters: Some(params),
|
||||
active_parameter: active_parameter.map(|x| x as u32),
|
||||
}],
|
||||
|
@ -147,54 +126,6 @@ impl SemanticRequest for SignatureHelpRequest {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DocTooltip;
|
||||
|
||||
impl DocTooltip {
|
||||
pub fn get(ctx: &mut LocalContext, def: &Definition) -> Option<String> {
|
||||
self::DocTooltip::get_inner(ctx, def).map(|s| "\n\n".to_owned() + &s)
|
||||
}
|
||||
|
||||
fn get_inner(ctx: &mut LocalContext, 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,
|
||||
|
|
|
@ -1,50 +1,4 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use typst::syntax::SyntaxNode;
|
||||
use typst_shim::syntax::LinkedNodeExt;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::syntax::get_def_target;
|
||||
|
||||
use super::DefTarget;
|
||||
|
||||
pub fn find_docs_before(src: &Source, cursor: usize) -> Option<String> {
|
||||
log::debug!("finding docs at: {id:?}, {cursor}", id = src.id());
|
||||
|
||||
let root = LinkedNode::new(src.root());
|
||||
let leaf = root.leaf_at_compat(cursor)?;
|
||||
let def_target = get_def_target(leaf.clone())?;
|
||||
find_docs_of(src, def_target)
|
||||
}
|
||||
|
||||
pub fn find_docs_of(src: &Source, def_target: DefTarget) -> Option<String> {
|
||||
let root = LinkedNode::new(src.root());
|
||||
log::debug!("found docs target: {:?}", def_target.node().kind());
|
||||
// todo: import node
|
||||
let target = def_target.node().clone();
|
||||
let mut node = target.clone();
|
||||
while let Some(prev) = node.prev_sibling() {
|
||||
node = prev;
|
||||
if node.kind() == SyntaxKind::Hash {
|
||||
continue;
|
||||
}
|
||||
|
||||
let start = node.range().end;
|
||||
let end = target.range().start;
|
||||
|
||||
if end <= start {
|
||||
return None;
|
||||
}
|
||||
|
||||
return extract_document_between(node.parent()?, start..end, false);
|
||||
}
|
||||
|
||||
if node.parent()?.range() == root.range() && node.prev_sibling().is_none() {
|
||||
return extract_document_between(node.parent()?, root.offset()..node.range().start, false);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_module_level_docs(src: &Source) -> Option<String> {
|
||||
log::debug!("finding docs at: {id:?}", id = src.id());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue