feat: support goto/find references inside of modules (#34)

* feat: support goto declaration inside of modules

* fix: change goto declarations provider to references provider

* fix: redact uri in references response
This commit is contained in:
Myriad-Dreamin 2024-03-14 20:38:30 +08:00 committed by GitHub
parent 9d344570b4
commit dbd1726d08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 489 additions and 46 deletions

View file

@ -10,6 +10,8 @@ pub mod reference;
pub use reference::*; pub use reference::*;
pub mod def_use; pub mod def_use;
pub use def_use::*; pub use def_use::*;
pub mod matcher;
pub use matcher::*;
#[cfg(test)] #[cfg(test)]
mod lexical_hierarchy_tests { mod lexical_hierarchy_tests {

View file

@ -6,6 +6,7 @@ use std::{
}; };
use comemo::Tracked; use comemo::Tracked;
use log::info;
use parking_lot::Mutex; use parking_lot::Mutex;
use serde::Serialize; use serde::Serialize;
use typst::{syntax::Source, World}; use typst::{syntax::Source, World};
@ -26,8 +27,8 @@ enum Ns {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IdentRef { pub struct IdentRef {
name: String, pub name: String,
range: Range<usize>, pub range: Range<usize>,
} }
impl PartialOrd for IdentRef { impl PartialOrd for IdentRef {
@ -72,6 +73,19 @@ pub struct DefUseInfo {
exports_refs: Vec<DefId>, exports_refs: Vec<DefId>,
} }
impl DefUseInfo {
pub fn get_def(&self, fid: TypstFileId, ident: &IdentRef) -> Option<(DefId, &IdentDef)> {
let (id, _, def) = self.ident_defs.get_full(&(fid, ident.clone()))?;
Some((DefId(id as u64), def))
}
pub fn get_refs(&self, id: DefId) -> impl Iterator<Item = &IdentRef> {
self.ident_refs
.iter()
.filter_map(move |(k, v)| if *v == id { Some(k) } else { None })
}
}
pub fn get_def_use(world: Tracked<'_, dyn World>, source: Source) -> Option<Arc<DefUseInfo>> { pub fn get_def_use(world: Tracked<'_, dyn World>, source: Source) -> Option<Arc<DefUseInfo>> {
let ctx = SearchCtx { let ctx = SearchCtx {
world, world,
@ -171,6 +185,7 @@ impl<'a, 'w> DefUseCollector<'a, 'w> {
let external_info = let external_info =
find_source_by_import_path(self.ctx.world, self.current_id, path) find_source_by_import_path(self.ctx.world, self.current_id, path)
.and_then(|source| { .and_then(|source| {
info!("diving source for def use: {:?}", source.id());
Some(source.id()).zip(get_def_use_inner(self.ctx, source)) Some(source.id()).zip(get_def_use_inner(self.ctx, source))
}); });

View file

@ -15,7 +15,7 @@ use typst::{
}; };
use typst_ts_core::TypstFileId; use typst_ts_core::TypstFileId;
use crate::analysis::find_source_by_import; use crate::analysis::{deref_lvalue, find_source_by_import};
use crate::{prelude::*, TypstSpan}; use crate::{prelude::*, TypstSpan};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -73,13 +73,6 @@ impl Definition<'_> {
} }
} }
fn deref_lvalue(mut node: LinkedNode) -> Option<LinkedNode> {
while let Some(e) = node.cast::<ast::Parenthesized>() {
node = node.find(e.expr().span())?;
}
Some(node)
}
fn advance_prev_adjacent(node: LinkedNode) -> Option<LinkedNode> { fn advance_prev_adjacent(node: LinkedNode) -> Option<LinkedNode> {
// this is aworkaround for a bug in the parser // this is aworkaround for a bug in the parser
if node.len() == 0 { if node.len() == 0 {

View file

@ -0,0 +1,69 @@
use log::debug;
use typst::syntax::{
ast::{self, AstNode},
LinkedNode, SyntaxKind,
};
pub fn deref_lvalue(mut node: LinkedNode) -> Option<LinkedNode> {
while let Some(e) = node.cast::<ast::Parenthesized>() {
node = node.find(e.expr().span())?;
}
Some(node)
}
pub enum DerefTarget<'a> {
VarAccess(LinkedNode<'a>),
Callee(LinkedNode<'a>),
ImportPath(LinkedNode<'a>),
}
impl<'a> DerefTarget<'a> {
pub fn node(&self) -> &LinkedNode {
match self {
DerefTarget::VarAccess(node) => node,
DerefTarget::Callee(node) => node,
DerefTarget::ImportPath(node) => node,
}
}
}
pub fn get_deref_target(node: LinkedNode) -> Option<DerefTarget> {
let mut ancestor = node;
while !ancestor.is::<ast::Expr>() {
ancestor = ancestor.parent()?.clone();
}
debug!("deref expr: {ancestor:?}");
let ancestor = deref_lvalue(ancestor)?;
debug!("deref lvalue: {ancestor:?}");
let may_ident = ancestor.cast::<ast::Expr>()?;
if !may_ident.hash() && !matches!(may_ident, ast::Expr::MathIdent(_)) {
return None;
}
Some(match may_ident {
// todo: label, reference
// todo: import
// todo: include
ast::Expr::FuncCall(call) => DerefTarget::Callee(ancestor.find(call.callee().span())?),
ast::Expr::Set(set) => DerefTarget::Callee(ancestor.find(set.target().span())?),
ast::Expr::Ident(..) | ast::Expr::MathIdent(..) | ast::Expr::FieldAccess(..) => {
DerefTarget::VarAccess(ancestor.find(may_ident.span())?)
}
ast::Expr::Str(..) => {
let parent = ancestor.parent()?;
if parent.kind() != SyntaxKind::ModuleImport {
return None;
}
return Some(DerefTarget::ImportPath(ancestor.find(may_ident.span())?));
}
ast::Expr::Import(..) => {
return None;
}
_ => {
debug!("unsupported kind {kind:?}", kind = ancestor.kind());
return None;
}
})
}

View file

@ -0,0 +1,3 @@
#let /* ident after */ f() = 1;
#(f());
#(f());

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/at_def.typ
---
[
{
"originSelectionRange": "0:23:0:24",
"targetRange": "0:24:0:26",
"targetSelectionRange": "0:24:0:26"
}
]

View file

@ -0,0 +1,3 @@
#let /* ident after */ f() = 1;
#(f());
#(f());

View file

@ -0,0 +1,2 @@
#let x = 1;
#(/* position after */ x);

View file

@ -0,0 +1,10 @@
#let /* ident after */ x = 1;
#let y = {
x + x;
}
#let f(y) = x + y;
#let x = x;
#let f = x;

View file

@ -0,0 +1,13 @@
---
source: crates/tinymist-query/src/references.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/references/at_def.typ
---
[
{
"range": "2:2:2:3"
},
{
"range": "1:2:1:3"
}
]

View file

@ -0,0 +1,6 @@
---
source: crates/tinymist-query/src/goto_declaration.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/goto_declaration/base.typ
---
null

View file

@ -0,0 +1,19 @@
---
source: crates/tinymist-query/src/references.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/references/redefine.typ
---
[
{
"range": "8:9:8:10"
},
{
"range": "3:2:3:3"
},
{
"range": "3:6:3:7"
},
{
"range": "6:12:6:13"
}
]

View file

@ -0,0 +1,69 @@
use std::ops::Range;
use comemo::Track;
use log::debug;
use lsp_types::LocationLink;
use crate::{
analysis::{get_def_use, get_deref_target, DerefTarget},
prelude::*,
};
#[derive(Debug, Clone)]
pub struct GotoDeclarationRequest {
pub path: PathBuf,
pub position: LspPosition,
}
impl GotoDeclarationRequest {
pub fn request(
self,
world: &TypstSystemWorld,
position_encoding: PositionEncoding,
) -> Option<GotoDeclarationResponse> {
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let offset = lsp_to_typst::position(self.position, position_encoding, &source)?;
let cursor = offset + 1;
let w: &dyn World = world;
let ast_node = LinkedNode::new(source.root()).leaf_at(cursor)?;
debug!("ast_node: {ast_node:?}", ast_node = ast_node);
let deref_target = get_deref_target(ast_node)?;
let use_site = deref_target.node();
let origin_selection_range =
typst_to_lsp::range(use_site.range(), &source, position_encoding);
let def_use = get_def_use(w.track(), source.clone())?;
let ref_spans = find_declarations(w, def_use, deref_target)?;
let mut links = vec![];
for ref_range in ref_spans {
let ref_id = source.id();
let ref_source = &source;
let span_path = world.path_for_id(ref_id).ok()?;
let range = typst_to_lsp::range(ref_range, ref_source, position_encoding);
let uri = Url::from_file_path(span_path).ok()?;
links.push(LocationLink {
origin_selection_range: Some(origin_selection_range),
target_uri: uri,
target_range: range,
target_selection_range: range,
});
}
debug!("goto_declartion: {links:?}");
Some(GotoDeclarationResponse::Link(links))
}
}
fn find_declarations(
_w: &dyn World,
_def_use: Arc<crate::analysis::DefUseInfo>,
_deref_target: DerefTarget<'_>,
) -> Option<Vec<Range<usize>>> {
todo!()
}

View file

@ -8,36 +8,40 @@ use std::sync::Arc;
use typst_ts_core::TypstDocument; use typst_ts_core::TypstDocument;
pub use diagnostics::*; pub use diagnostics::*;
pub(crate) mod signature_help; pub(crate) mod code_lens;
pub use signature_help::*; pub use code_lens::*;
pub(crate) mod completion;
pub use completion::*;
pub(crate) mod document_symbol; pub(crate) mod document_symbol;
pub use document_symbol::*; pub use document_symbol::*;
pub(crate) mod symbol; pub(crate) mod folding_range;
pub use symbol::*; pub use folding_range::*;
pub(crate) mod goto_declaration;
pub use goto_declaration::*;
pub(crate) mod goto_definition;
pub use goto_definition::*;
pub(crate) mod hover;
pub use hover::*;
pub(crate) mod inlay_hint;
pub use inlay_hint::*;
pub(crate) mod rename;
pub use rename::*;
pub(crate) mod selection_range;
pub use selection_range::*;
pub(crate) mod semantic_tokens; pub(crate) mod semantic_tokens;
pub use semantic_tokens::*; pub use semantic_tokens::*;
pub(crate) mod semantic_tokens_full; pub(crate) mod semantic_tokens_full;
pub use semantic_tokens_full::*; pub use semantic_tokens_full::*;
pub(crate) mod semantic_tokens_delta; pub(crate) mod semantic_tokens_delta;
pub use semantic_tokens_delta::*; pub use semantic_tokens_delta::*;
pub(crate) mod hover; pub(crate) mod signature_help;
pub use hover::*; pub use signature_help::*;
pub(crate) mod completion; pub(crate) mod symbol;
pub use completion::*; pub use symbol::*;
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(crate) mod inlay_hint;
pub use inlay_hint::*;
pub(crate) mod prepare_rename; pub(crate) mod prepare_rename;
pub use prepare_rename::*; pub use prepare_rename::*;
pub(crate) mod rename; pub(crate) mod references;
pub use rename::*; pub use references::*;
pub(crate) mod code_lens;
pub use code_lens::*;
pub mod lsp_typst_boundary; pub mod lsp_typst_boundary;
pub use lsp_typst_boundary::*; pub use lsp_typst_boundary::*;
@ -78,6 +82,8 @@ mod polymorphic {
OnSaveExport(OnSaveExportRequest), OnSaveExport(OnSaveExportRequest),
Hover(HoverRequest), Hover(HoverRequest),
GotoDefinition(GotoDefinitionRequest), GotoDefinition(GotoDefinitionRequest),
GotoDeclaration(GotoDeclarationRequest),
References(ReferencesRequest),
InlayHint(InlayHintRequest), InlayHint(InlayHintRequest),
CodeLens(CodeLensRequest), CodeLens(CodeLensRequest),
Completion(CompletionRequest), Completion(CompletionRequest),
@ -100,6 +106,8 @@ mod polymorphic {
CompilerQueryRequest::OnSaveExport(..) => Mergable, CompilerQueryRequest::OnSaveExport(..) => Mergable,
CompilerQueryRequest::Hover(..) => PinnedFirst, CompilerQueryRequest::Hover(..) => PinnedFirst,
CompilerQueryRequest::GotoDefinition(..) => PinnedFirst, CompilerQueryRequest::GotoDefinition(..) => PinnedFirst,
CompilerQueryRequest::GotoDeclaration(..) => PinnedFirst,
CompilerQueryRequest::References(..) => PinnedFirst,
CompilerQueryRequest::InlayHint(..) => Unique, CompilerQueryRequest::InlayHint(..) => Unique,
CompilerQueryRequest::CodeLens(..) => Unique, CompilerQueryRequest::CodeLens(..) => Unique,
CompilerQueryRequest::Completion(..) => Mergable, CompilerQueryRequest::Completion(..) => Mergable,
@ -121,6 +129,8 @@ mod polymorphic {
CompilerQueryRequest::OnSaveExport(req) => &req.path, CompilerQueryRequest::OnSaveExport(req) => &req.path,
CompilerQueryRequest::Hover(req) => &req.path, CompilerQueryRequest::Hover(req) => &req.path,
CompilerQueryRequest::GotoDefinition(req) => &req.path, CompilerQueryRequest::GotoDefinition(req) => &req.path,
CompilerQueryRequest::GotoDeclaration(req) => &req.path,
CompilerQueryRequest::References(req) => &req.path,
CompilerQueryRequest::InlayHint(req) => &req.path, CompilerQueryRequest::InlayHint(req) => &req.path,
CompilerQueryRequest::CodeLens(req) => &req.path, CompilerQueryRequest::CodeLens(req) => &req.path,
CompilerQueryRequest::Completion(req) => &req.path, CompilerQueryRequest::Completion(req) => &req.path,
@ -143,6 +153,8 @@ mod polymorphic {
OnSaveExport(()), OnSaveExport(()),
Hover(Option<Hover>), Hover(Option<Hover>),
GotoDefinition(Option<GotoDefinitionResponse>), GotoDefinition(Option<GotoDefinitionResponse>),
GotoDeclaration(Option<GotoDeclarationResponse>),
References(Option<Vec<LspLocation>>),
InlayHint(Option<Vec<InlayHint>>), InlayHint(Option<Vec<InlayHint>>),
CodeLens(Option<Vec<CodeLens>>), CodeLens(Option<Vec<CodeLens>>),
Completion(Option<CompletionResponse>), Completion(Option<CompletionResponse>),
@ -171,7 +183,10 @@ mod tests {
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde::Serialize; use serde::Serialize;
use serde_json::{ser::PrettyFormatter, Serializer, Value}; use serde_json::{ser::PrettyFormatter, Serializer, Value};
use typst::syntax::{LinkedNode, Source, VirtualPath}; use typst::syntax::{
ast::{self, AstNode},
LinkedNode, Source, SyntaxKind, VirtualPath,
};
use typst_ts_compiler::{ use typst_ts_compiler::{
service::{CompileDriver, Compiler, WorkspaceProvider}, service::{CompileDriver, Compiler, WorkspaceProvider},
ShadowApi, ShadowApi,
@ -252,25 +267,86 @@ mod tests {
} }
pub fn find_test_position(s: &Source) -> LspPosition { pub fn find_test_position(s: &Source) -> LspPosition {
let re = s.text().find("/* position */").map(|e| (e, true)); enum AstMatcher {
let re = re.or_else(|| s.text().find("/* position after */").zip(Some(false))); MatchAny { prev: bool },
let (re, prev) = re MatchIdent { prev: bool },
}
use AstMatcher::*;
let re = s
.text()
.find("/* position */")
.map(|e| (e, MatchAny { prev: true }));
let re = re.or_else(|| {
s.text()
.find("/* position after */")
.zip(Some(MatchAny { prev: false }))
});
let re = re.or_else(|| {
s.text()
.find("/* ident */")
.zip(Some(MatchIdent { prev: true }))
});
let re = re.or_else(|| {
s.text()
.find("/* ident after */")
.zip(Some(MatchIdent { prev: false }))
});
let (re, m) = re
.ok_or_else(|| panic!("No position marker found in source:\n{}", s.text())) .ok_or_else(|| panic!("No position marker found in source:\n{}", s.text()))
.unwrap(); .unwrap();
let n = LinkedNode::new(s.root()); let n = LinkedNode::new(s.root());
let mut n = n.leaf_at(re + 1).unwrap(); let mut n = n.leaf_at(re + 1).unwrap();
while n.kind().is_trivia() { let match_prev = match &m {
let m = if prev { MatchAny { prev } => *prev,
n.prev_sibling() MatchIdent { prev } => *prev,
} else { };
n.next_sibling() let match_ident = match m {
}; MatchAny { .. } => false,
n = m.or_else(|| n.parent().cloned()).unwrap(); MatchIdent { .. } => true,
};
'match_loop: loop {
if n.kind().is_trivia() {
let m = if match_prev {
n.prev_sibling()
} else {
n.next_sibling()
};
n = m.or_else(|| n.parent().cloned()).unwrap();
continue;
}
if match_ident {
match n.kind() {
SyntaxKind::Closure => {
let c = n.cast::<ast::Closure>().unwrap();
if let Some(name) = c.name() {
if let Some(m) = n.find(name.span()) {
n = m;
break 'match_loop;
}
}
}
SyntaxKind::LetBinding => {
let c = n.cast::<ast::LetBinding>().unwrap();
if let Some(name) = c.kind().bindings().first() {
if let Some(m) = n.find(name.span()) {
n = m;
break 'match_loop;
}
}
}
_ => {}
}
}
break;
} }
typst_to_lsp::offset_to_position(n.offset() + 1, PositionEncoding::Utf16, s) // eprintln!("position: {:?} -> {:?}", n.offset(), n);
typst_to_lsp::offset_to_position(n.offset(), PositionEncoding::Utf16, s)
} }
// pub static REDACT_URI: Lazy<RedactFields> = Lazy::new(|| // pub static REDACT_URI: Lazy<RedactFields> = Lazy::new(||
@ -278,6 +354,7 @@ mod tests {
pub static REDACT_LOC: Lazy<RedactFields> = Lazy::new(|| { pub static REDACT_LOC: Lazy<RedactFields> = Lazy::new(|| {
RedactFields::from_iter([ RedactFields::from_iter([
"location", "location",
"uri",
"range", "range",
"selectionRange", "selectionRange",
"targetRange", "targetRange",

View file

@ -9,9 +9,9 @@ pub use comemo::{Track, Tracked};
pub use itertools::{Format, Itertools}; pub use itertools::{Format, Itertools};
pub use log::{error, trace}; pub use log::{error, trace};
pub use lsp_types::{ pub use lsp_types::{
CodeLens, CompletionResponse, DiagnosticRelatedInformation, DocumentSymbol, request::GotoDeclarationResponse, CodeLens, CompletionResponse, DiagnosticRelatedInformation,
DocumentSymbolResponse, Documentation, FoldingRange, GotoDefinitionResponse, Hover, InlayHint, DocumentSymbol, DocumentSymbolResponse, Documentation, FoldingRange, GotoDefinitionResponse,
Location as LspLocation, MarkupContent, MarkupKind, Position as LspPosition, Hover, InlayHint, Location as LspLocation, MarkupContent, MarkupKind, Position as LspPosition,
PrepareRenameResponse, SelectionRange, SemanticTokens, SemanticTokensDelta, PrepareRenameResponse, SelectionRange, SemanticTokens, SemanticTokensDelta,
SemanticTokensFullDeltaResult, SemanticTokensResult, SignatureHelp, SignatureInformation, SemanticTokensFullDeltaResult, SemanticTokensResult, SignatureHelp, SignatureInformation,
SymbolInformation, Url, WorkspaceEdit, SymbolInformation, Url, WorkspaceEdit,

View file

@ -0,0 +1,125 @@
use std::ops::Range;
use comemo::Track;
use log::debug;
use crate::{
analysis::{get_def_use, get_deref_target, DerefTarget, IdentRef},
prelude::*,
};
#[derive(Debug, Clone)]
pub struct ReferencesRequest {
pub path: PathBuf,
pub position: LspPosition,
}
impl ReferencesRequest {
pub fn request(
self,
world: &TypstSystemWorld,
position_encoding: PositionEncoding,
) -> Option<Vec<LspLocation>> {
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let offset = lsp_to_typst::position(self.position, position_encoding, &source)?;
let cursor = offset + 1;
let w: &dyn World = world;
let ast_node = LinkedNode::new(source.root()).leaf_at(cursor)?;
debug!("ast_node: {ast_node:?}", ast_node = ast_node);
let deref_target = get_deref_target(ast_node)?;
let def_use = get_def_use(w.track(), source.clone())?;
let ref_spans = find_declarations(w, def_use, deref_target)?;
let mut locations = vec![];
for ref_range in ref_spans {
let ref_id = source.id();
let ref_source = &source;
let span_path = world.path_for_id(ref_id).ok()?;
let range = typst_to_lsp::range(ref_range, ref_source, position_encoding);
let uri = Url::from_file_path(span_path).ok()?;
locations.push(LspLocation { uri, range });
}
debug!("references: {locations:?}");
Some(locations)
}
}
fn find_declarations(
_w: &dyn World,
def_use: Arc<crate::analysis::DefUseInfo>,
deref_target: DerefTarget<'_>,
) -> Option<Vec<Range<usize>>> {
let node = match deref_target {
DerefTarget::VarAccess(node) => node,
DerefTarget::Callee(node) => node,
DerefTarget::ImportPath(..) => {
return None;
}
};
let mut may_ident = node.cast::<ast::Expr>()?;
let name;
loop {
match may_ident {
ast::Expr::Parenthesized(e) => {
may_ident = e.expr();
}
ast::Expr::FieldAccess(e) => {
may_ident = e.target();
}
ast::Expr::MathIdent(e) => {
name = e.get().to_string();
break;
}
ast::Expr::Ident(e) => {
name = e.get().to_string();
break;
}
_ => return None,
}
}
let ident = node.find(may_ident.span())?;
// todo: if it is exported, find all the references in the workspace
let ident_ref = IdentRef {
name,
range: ident.range(),
};
let (id, _) = def_use.get_def(ident.span().id()?, &ident_ref)?;
Some(
def_use
.get_refs(id)
.map(|r| r.range.clone())
.collect::<Vec<_>>(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::*;
#[test]
fn test() {
// goto_definition
snapshot_testing("references", &|world, path| {
let source = get_suitable_source_in_workspace(world, &path).unwrap();
let request = ReferencesRequest {
path: path.clone(),
position: find_test_position(&source),
};
let result = request.request(world, PositionEncoding::Utf16);
assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
});
}
}

View file

@ -679,6 +679,8 @@ impl CompileActor {
} }
Hover(req) => query_state!(self, Hover, req), Hover(req) => query_state!(self, Hover, req),
GotoDefinition(req) => query_world!(self, GotoDefinition, req), GotoDefinition(req) => query_world!(self, GotoDefinition, req),
GotoDeclaration(req) => query_world!(self, GotoDeclaration, req),
References(req) => query_world!(self, References, req),
InlayHint(req) => query_world!(self, InlayHint, req), InlayHint(req) => query_world!(self, InlayHint, req),
CodeLens(req) => query_world!(self, CodeLens, req), CodeLens(req) => query_world!(self, CodeLens, req),
Completion(req) => query_state!(self, Completion, req), Completion(req) => query_state!(self, Completion, req),

View file

@ -27,6 +27,10 @@ trait InitializeParamsExt {
fn supports_document_formatting_dynamic_registration(&self) -> bool; fn supports_document_formatting_dynamic_registration(&self) -> bool;
fn line_folding_only(&self) -> bool; fn line_folding_only(&self) -> bool;
fn root_paths(&self) -> Vec<PathBuf>; fn root_paths(&self) -> Vec<PathBuf>;
// todo: svelte-language-server responds to a Goto Definition request with
// LocationLink[] even if the client does not report the
// textDocument.definition.linkSupport capability.
} }
impl InitializeParamsExt for InitializeParams { impl InitializeParamsExt for InitializeParams {
@ -586,6 +590,7 @@ impl Init {
}, },
}), }),
definition_provider: Some(OneOf::Left(true)), definition_provider: Some(OneOf::Left(true)),
references_provider: Some(OneOf::Left(true)),
completion_provider: Some(CompletionOptions { completion_provider: Some(CompletionOptions {
trigger_characters: Some(vec![ trigger_characters: Some(vec![
String::from("#"), String::from("#"),

View file

@ -48,7 +48,10 @@ use futures::future::BoxFuture;
use log::{error, info, trace, warn}; use log::{error, info, trace, warn};
use lsp_server::{ErrorCode, Message, Notification, Request, ResponseError}; use lsp_server::{ErrorCode, Message, Notification, Request, ResponseError};
use lsp_types::notification::{Notification as NotificationTrait, PublishDiagnostics}; use lsp_types::notification::{Notification as NotificationTrait, PublishDiagnostics};
use lsp_types::request::{RegisterCapability, UnregisterCapability, WorkspaceConfiguration}; use lsp_types::request::{
GotoDeclarationParams, GotoDeclarationResponse, RegisterCapability, UnregisterCapability,
WorkspaceConfiguration,
};
use lsp_types::*; use lsp_types::*;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use paste::paste; use paste::paste;
@ -366,6 +369,8 @@ impl TypstLanguageServer {
request_fn!(PrepareRenameRequest, Self::prepare_rename), request_fn!(PrepareRenameRequest, Self::prepare_rename),
request_fn!(Rename, Self::rename), request_fn!(Rename, Self::rename),
request_fn!(GotoDefinition, Self::goto_definition), request_fn!(GotoDefinition, Self::goto_definition),
request_fn!(GotoDeclaration, Self::goto_declaration),
request_fn!(References, Self::references),
request_fn!(WorkspaceSymbolRequest, Self::symbol), request_fn!(WorkspaceSymbolRequest, Self::symbol),
request_fn!(ExecuteCommand, Self::execute_command), request_fn!(ExecuteCommand, Self::execute_command),
]) ])
@ -842,6 +847,19 @@ impl TypstLanguageServer {
run_query!(self.GotoDefinition(path, position)) run_query!(self.GotoDefinition(path, position))
} }
fn goto_declaration(
&self,
params: GotoDeclarationParams,
) -> LspResult<Option<GotoDeclarationResponse>> {
let (path, position) = as_path_pos(params.text_document_position_params);
run_query!(self.GotoDeclaration(path, position))
}
fn references(&self, params: ReferenceParams) -> LspResult<Option<Vec<Location>>> {
let (path, position) = as_path_pos(params.text_document_position);
run_query!(self.References(path, position))
}
fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> { fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> {
let (path, position) = as_path_pos(params.text_document_position_params); let (path, position) = as_path_pos(params.text_document_position_params);
run_query!(self.Hover(path, position)) run_query!(self.Hover(path, position))