feat: go to definition of module members (#644)

* feat: go to definition of module members

* dev: rename a bit

* ovo
This commit is contained in:
Myriad-Dreamin 2024-10-09 12:17:51 +08:00 committed by GitHub
parent 3aa8f1233d
commit df5dafbf75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 139 additions and 37 deletions

View file

@ -1,14 +1,13 @@
//! Import analysis
use ecow::EcoVec;
use typst::{
foundations::Value,
syntax::{LinkedNode, SyntaxKind},
World,
};
use typst::{foundations::Value, syntax::LinkedNode, World};
use typst_shim::syntax::LinkedNodeExt;
use crate::{analysis::analyze_import_, syntax::resolve_id_by_path};
use crate::{
analysis::analyze_import_,
syntax::{find_expr_in_import, resolve_id_by_path},
};
pub use super::prelude::*;
@ -92,7 +91,10 @@ impl<'a, 'w> ImportCollector<'a, 'w> {
LexicalKind::Mod(LexicalModKind::Module(p)) => {
let id = match p {
ModSrc::Expr(exp) => {
let exp = find_import_expr(self.root.leaf_at_compat(exp.range.end));
let exp = self
.root
.leaf_at_compat(exp.range.end)
.and_then(find_expr_in_import);
let val = exp
.as_ref()
.and_then(|exp| analyze_import_(self.ctx.deref(), exp));
@ -100,9 +102,8 @@ impl<'a, 'w> ImportCollector<'a, 'w> {
match val {
Some(Value::Module(m)) => {
log::debug!(
"current id {:?} exp {:?} => id: {:?}",
"current id {:?} exp {exp:?} => id: {:?}",
self.current_id,
exp,
m.file_id()
);
m.file_id()
@ -132,17 +133,3 @@ impl<'a, 'w> ImportCollector<'a, 'w> {
}
}
}
fn find_import_expr(end: Option<LinkedNode>) -> Option<LinkedNode> {
let mut node = end?;
while let Some(parent) = node.parent() {
if matches!(
parent.kind(),
SyntaxKind::ModuleImport | SyntaxKind::ModuleInclude
) {
return Some(node);
}
node = parent.clone();
}
None
}

View file

@ -12,6 +12,7 @@ use typst::{foundations::Value, syntax::Span};
use typst_shim::syntax::LinkedNodeExt;
use super::prelude::*;
use crate::syntax::find_expr_in_import;
use crate::{
prelude::*,
syntax::{
@ -93,10 +94,12 @@ pub fn find_definition(
fn find_ident_definition(
ctx: &mut AnalysisContext<'_>,
source: Source,
use_site: LinkedNode,
mut use_site: LinkedNode,
) -> Option<DefinitionLink> {
let mut proj = vec![];
// Lexical reference
let ident_ref = match use_site.cast::<ast::Expr>()? {
let ident_store = use_site.clone();
let ident_ref = match ident_store.cast::<ast::Expr>()? {
ast::Expr::Ident(e) => Some(IdentRef {
name: e.get().clone(),
range: use_site.range(),
@ -105,10 +108,32 @@ fn find_ident_definition(
name: e.get().clone(),
range: use_site.range(),
}),
ast::Expr::FieldAccess(..) => {
debug!("find field access");
ast::Expr::FieldAccess(s) => {
proj.push(s.field());
None
let mut i = s.target();
while let ast::Expr::FieldAccess(f) = i {
proj.push(f.field());
i = f.target();
}
match i {
ast::Expr::Ident(e) => {
use_site = use_site.find(e.span())?;
Some(IdentRef {
name: e.get().clone(),
range: use_site.range(),
})
}
ast::Expr::MathIdent(e) => {
use_site = use_site.find(e.span())?;
Some(IdentRef {
name: e.get().clone(),
range: use_site.range(),
})
}
_ => None,
}
}
_ => {
debug!("unsupported kind {kind:?}", kind = use_site.kind());
@ -136,10 +161,39 @@ fn find_ident_definition(
});
};
match def.kind {
match &def.kind {
LexicalKind::Var(LexicalVarKind::BibKey)
| LexicalKind::Heading(..)
| LexicalKind::Block => unreachable!(),
LexicalKind::Mod(
LexicalModKind::Module(..) | LexicalModKind::PathVar | LexicalModKind::ModuleAlias,
) => {
if !proj.is_empty() {
proj.reverse();
let def_src = ctx.source_by_id(def_fid).ok()?;
let def_root = LinkedNode::new(def_src.root());
let cursor = def.range.start + 1;
let mod_exp = find_expr_in_import(def_root.leaf_at_compat(cursor)?)?;
let mod_import = mod_exp.parent()?.clone();
let mod_import_node = mod_import.cast::<ast::ModuleImport>()?;
let import_path = mod_import.find(mod_import_node.source().span())?;
let m = ctx.analyze_import(&import_path)?;
let val = project_value(&m, proj.as_slice())?;
// todo: name range
let name = proj.last().map(|e| e.get().clone());
return value_to_def(ctx, val.clone(), || name, None);
}
Some(DefinitionLink {
kind: def.kind.clone(),
name: def.name.clone(),
value: None,
def_at: Some((def_fid, def.range.clone())),
name_range: Some(def.range.clone()),
})
}
LexicalKind::Var(
LexicalVarKind::Variable
| LexicalVarKind::ValRef
@ -147,12 +201,7 @@ fn find_ident_definition(
| LexicalVarKind::LabelRef,
)
| LexicalKind::Mod(
LexicalModKind::Module(..)
| LexicalModKind::PathVar
| LexicalModKind::PathInclude
| LexicalModKind::ModuleAlias
| LexicalModKind::Alias { .. }
| LexicalModKind::Ident,
LexicalModKind::PathInclude | LexicalModKind::Alias { .. } | LexicalModKind::Ident,
) => Some(DefinitionLink {
kind: def.kind.clone(),
name: def.name.clone(),
@ -179,12 +228,22 @@ fn find_ident_definition(
})
}
LexicalKind::Mod(LexicalModKind::Star) => {
log::info!("unimplemented star import {:?}", ident_ref);
log::info!("unimplemented star import {ident_ref:?}");
None
}
}
}
fn project_value<'a>(m: &'a Value, proj: &[ast::Ident<'_>]) -> Option<&'a Value> {
if proj.is_empty() {
return Some(m);
}
let scope = m.scope()?;
let (ident, proj) = proj.split_first()?;
let v = scope.get(ident.as_str())?;
project_value(v, proj)
}
fn find_bib_definition(
ctx: &mut AnalysisContext,
introspector: &Introspector,

View file

@ -0,0 +1,6 @@
// path: variable.typ
#let f(x) = 2;
-----
#import "variable.typ"
#(variable.f /* position after */ );

View file

@ -0,0 +1,6 @@
// path: variable.typ
#let f(x) = 2;
-----
#import "variable.typ" as this-module
#(this-module.f /* position after */ );

View file

@ -0,0 +1,12 @@
---
source: crates/tinymist-query/src/goto_definition.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/goto_definition/module_select.typ
---
[
{
"originSelectionRange": "1:2:1:12",
"targetRange": "0:6:0:9",
"targetSelectionRange": "0:6:0:9"
}
]

View file

@ -0,0 +1,12 @@
---
source: crates/tinymist-query/src/goto_definition.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/goto_definition/module_select_alias.typ
---
[
{
"originSelectionRange": "1:2:1:15",
"targetRange": "0:6:0:9",
"targetSelectionRange": "0:6:0:9"
}
]

View file

@ -14,9 +14,29 @@ pub fn deref_lvalue(mut node: LinkedNode) -> Option<LinkedNode> {
while let Some(e) = node.cast::<ast::Parenthesized>() {
node = node.find(e.expr().span())?;
}
if let Some(e) = node.parent() {
if let Some(f) = e.cast::<ast::FieldAccess>() {
if node.span() == f.field().span() {
return Some(e.clone());
}
}
}
Some(node)
}
pub(crate) fn find_expr_in_import(mut node: LinkedNode) -> Option<LinkedNode> {
while let Some(parent) = node.parent() {
if matches!(
parent.kind(),
SyntaxKind::ModuleImport | SyntaxKind::ModuleInclude
) {
return Some(node);
}
node = parent.clone();
}
None
}
fn is_mark(sk: SyntaxKind) -> bool {
use SyntaxKind::*;
matches!(