feat: support goto_definition api

This commit is contained in:
Myriad-Dreamin 2024-03-08 00:51:18 +08:00
parent edd3ad260f
commit c19197178c
5 changed files with 126 additions and 10 deletions

View file

@ -0,0 +1,94 @@
use crate::prelude::*;
#[derive(Debug, Clone)]
pub struct GotoDefinitionRequest {
pub path: PathBuf,
pub position: LspPosition,
}
impl GotoDefinitionRequest {
pub fn request(
self,
world: &TypstSystemWorld,
position_encoding: PositionEncoding,
) -> Option<GotoDefinitionResponse> {
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let typst_offset =
lsp_to_typst::position_to_offset(self.position, position_encoding, &source);
let ast_node = LinkedNode::new(source.root()).leaf_at(typst_offset)?;
let mut ancestor = &ast_node;
while !ancestor.is::<ast::Expr>() {
ancestor = ancestor.parent()?;
}
let may_ident = ancestor.cast::<ast::Expr>()?;
if !may_ident.hash() && !matches!(may_ident, ast::Expr::MathIdent(_)) {
return None;
}
let mut is_ident_only = false;
trace!("got ast_node kind {kind:?}", kind = ancestor.kind());
let callee_node = match may_ident {
// todo: label, reference
// todo: import
// todo: include
ast::Expr::FuncCall(call) => call.callee(),
ast::Expr::Set(set) => set.target(),
ast::Expr::Ident(..) | ast::Expr::MathIdent(..) | ast::Expr::FieldAccess(..) => {
is_ident_only = true;
may_ident
}
_ => return None,
};
trace!("got callee_node {callee_node:?} {is_ident_only:?}");
let callee_link = if is_ident_only {
ancestor.clone()
} else {
ancestor.find(callee_node.span())?
};
let values = analyze_expr(world, &callee_link);
let func_or_module = values.into_iter().find_map(|v| match &v {
Value::Args(a) => {
trace!("got args {a:?}");
None
}
Value::Func(..) | Value::Module(..) => Some(v),
_ => None,
});
let span = match func_or_module {
Some(Value::Func(f)) => f.span(),
Some(Value::Module(m)) => {
trace!("find module. {m:?}");
// todo
return None;
}
_ => {
trace!("find value by lexical result. {callee_link:?}");
return None;
}
};
if span.is_detached() {
return None;
}
let Some(id) = span.id() else {
return None;
};
let span_path = world.path_for_id(id).ok()?;
let span_source = world.source(id).ok()?;
let offset = span_source.find(span)?;
let typst_range = offset.range();
let range = typst_to_lsp::range(typst_range, &span_source, position_encoding).raw_range;
let uri = Url::from_file_path(span_path).ok()?;
Some(GotoDefinitionResponse::Scalar(LspLocation { uri, range }))
}
}

View file

@ -22,6 +22,8 @@ pub(crate) mod folding_range;
pub use folding_range::*;
pub(crate) mod selection_range;
pub use selection_range::*;
pub(crate) mod goto_definition;
pub use goto_definition::*;
pub mod lsp_typst_boundary;
pub use lsp_typst_boundary::*;

View file

@ -9,10 +9,10 @@ pub use itertools::{Format, Itertools};
pub use log::{error, trace};
pub use tower_lsp::lsp_types::{
CompletionResponse, DiagnosticRelatedInformation, DocumentSymbol, DocumentSymbolResponse,
Documentation, FoldingRange, Hover, Location as LspLocation, MarkupContent, MarkupKind,
Position as LspPosition, SelectionRange, SemanticTokens, SemanticTokensDelta,
SemanticTokensFullDeltaResult, SemanticTokensResult, SignatureHelp, SignatureInformation,
SymbolInformation, Url,
Documentation, FoldingRange, GotoDefinitionResponse, Hover, Location as LspLocation,
MarkupContent, MarkupKind, Position as LspPosition, SelectionRange, SemanticTokens,
SemanticTokensDelta, SemanticTokensFullDeltaResult, SemanticTokensResult, SignatureHelp,
SignatureInformation, SymbolInformation, Url,
};
pub use typst::diag::{EcoString, FileError, FileResult, Tracepoint};
pub use typst::foundations::{Func, ParamInfo, Value};