Implement goto_declaration support
This commit is contained in:
parent
9239943b84
commit
5a74e93c33
107
crates/ide/src/goto_declaration.rs
Normal file
107
crates/ide/src/goto_declaration.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use hir::Semantics;
|
||||||
|
use ide_db::{
|
||||||
|
defs::{Definition, NameClass, NameRefClass},
|
||||||
|
RootDatabase,
|
||||||
|
};
|
||||||
|
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, T};
|
||||||
|
|
||||||
|
use crate::{goto_definition, FilePosition, NavigationTarget, RangeInfo};
|
||||||
|
|
||||||
|
// Feature: Go to Declaration
|
||||||
|
//
|
||||||
|
// Navigates to the declaration of an identifier. This is the same as the definition except for
|
||||||
|
// modules where this goes to the identifier of the declaration instead of the contents.
|
||||||
|
pub(crate) fn goto_declaration(
|
||||||
|
db: &RootDatabase,
|
||||||
|
position: FilePosition,
|
||||||
|
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||||
|
let sema = Semantics::new(db);
|
||||||
|
let file = sema.parse(position.file_id).syntax().clone();
|
||||||
|
let res = (|| {
|
||||||
|
// try
|
||||||
|
let original_token = file
|
||||||
|
.token_at_offset(position.offset)
|
||||||
|
.find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate]))?;
|
||||||
|
let token = sema.descend_into_macros(original_token.clone());
|
||||||
|
let parent = token.parent()?;
|
||||||
|
let def = match_ast! {
|
||||||
|
match parent {
|
||||||
|
ast::NameRef(name_ref) => {
|
||||||
|
let name_kind = NameRefClass::classify(&sema, &name_ref)?;
|
||||||
|
name_kind.referenced(sema.db)
|
||||||
|
},
|
||||||
|
ast::Name(name) => {
|
||||||
|
NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db)
|
||||||
|
},
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match def {
|
||||||
|
Definition::ModuleDef(hir::ModuleDef::Module(module)) => Some(RangeInfo::new(
|
||||||
|
original_token.text_range(),
|
||||||
|
vec![NavigationTarget::from_module_to_decl(db, module)],
|
||||||
|
)),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
res.or_else(|| goto_definition::goto_definition(db, position))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ide_db::base_db::FileRange;
|
||||||
|
|
||||||
|
use crate::fixture;
|
||||||
|
|
||||||
|
fn check(ra_fixture: &str) {
|
||||||
|
let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture);
|
||||||
|
let mut navs = analysis
|
||||||
|
.goto_declaration(position)
|
||||||
|
.unwrap()
|
||||||
|
.expect("no declaration or definition found")
|
||||||
|
.info;
|
||||||
|
if navs.len() == 0 {
|
||||||
|
panic!("unresolved reference")
|
||||||
|
}
|
||||||
|
assert_eq!(navs.len(), 1);
|
||||||
|
|
||||||
|
let nav = navs.pop().unwrap();
|
||||||
|
assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_decl_module_outline() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
mod foo;
|
||||||
|
// ^^^
|
||||||
|
//- /foo.rs
|
||||||
|
use self$0;
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_decl_module_inline() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
mod foo {
|
||||||
|
// ^^^
|
||||||
|
use self$0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_decl_falls_back_to_goto_def() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
// ^^^
|
||||||
|
use self::Foo$0;
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@ pub(crate) fn goto_definition(
|
|||||||
let file = sema.parse(position.file_id).syntax().clone();
|
let file = sema.parse(position.file_id).syntax().clone();
|
||||||
let original_token =
|
let original_token =
|
||||||
pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
|
pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
|
||||||
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2,
|
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | COMMENT => 2,
|
||||||
kind if kind.is_trivia() => 0,
|
kind if kind.is_trivia() => 0,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
})?;
|
})?;
|
||||||
|
@ -28,6 +28,7 @@ mod expand_macro;
|
|||||||
mod extend_selection;
|
mod extend_selection;
|
||||||
mod file_structure;
|
mod file_structure;
|
||||||
mod folding_ranges;
|
mod folding_ranges;
|
||||||
|
mod goto_declaration;
|
||||||
mod goto_definition;
|
mod goto_definition;
|
||||||
mod goto_implementation;
|
mod goto_implementation;
|
||||||
mod goto_type_definition;
|
mod goto_type_definition;
|
||||||
@ -374,6 +375,14 @@ impl Analysis {
|
|||||||
self.with_db(|db| goto_definition::goto_definition(db, position))
|
self.with_db(|db| goto_definition::goto_definition(db, position))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the declaration from the symbol at `position`.
|
||||||
|
pub fn goto_declaration(
|
||||||
|
&self,
|
||||||
|
position: FilePosition,
|
||||||
|
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
|
||||||
|
self.with_db(|db| goto_declaration::goto_declaration(db, position))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the impls from the symbol at `position`.
|
/// Returns the impls from the symbol at `position`.
|
||||||
pub fn goto_implementation(
|
pub fn goto_implementation(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Advertises the capabilities of the LSP Server.
|
//! Advertises the capabilities of the LSP Server.
|
||||||
use lsp_types::{
|
use lsp_types::{
|
||||||
CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
|
CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
|
||||||
CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
|
CodeActionProviderCapability, CodeLensOptions, CompletionOptions, DeclarationCapability,
|
||||||
DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
|
DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
|
||||||
FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
|
FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
|
||||||
HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
|
HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
|
||||||
@ -38,7 +38,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
|
|||||||
retrigger_characters: None,
|
retrigger_characters: None,
|
||||||
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
|
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
|
||||||
}),
|
}),
|
||||||
declaration_provider: None,
|
declaration_provider: Some(DeclarationCapability::Simple(true)),
|
||||||
definition_provider: Some(OneOf::Left(true)),
|
definition_provider: Some(OneOf::Left(true)),
|
||||||
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
|
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
|
||||||
implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
|
implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
|
||||||
|
@ -556,6 +556,21 @@ pub(crate) fn handle_goto_definition(
|
|||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn handle_goto_declaration(
|
||||||
|
snap: GlobalStateSnapshot,
|
||||||
|
params: lsp_types::request::GotoDeclarationParams,
|
||||||
|
) -> Result<Option<lsp_types::request::GotoDeclarationResponse>> {
|
||||||
|
let _p = profile::span("handle_goto_declaration");
|
||||||
|
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
|
||||||
|
let nav_info = match snap.analysis.goto_declaration(position)? {
|
||||||
|
None => return Ok(None),
|
||||||
|
Some(it) => it,
|
||||||
|
};
|
||||||
|
let src = FileRange { file_id: position.file_id, range: nav_info.range };
|
||||||
|
let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
|
||||||
|
Ok(Some(res))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_goto_implementation(
|
pub(crate) fn handle_goto_implementation(
|
||||||
snap: GlobalStateSnapshot,
|
snap: GlobalStateSnapshot,
|
||||||
params: lsp_types::request::GotoImplementationParams,
|
params: lsp_types::request::GotoImplementationParams,
|
||||||
|
@ -531,6 +531,7 @@ impl GlobalState {
|
|||||||
.on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)
|
.on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)
|
||||||
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
|
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
|
||||||
.on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
|
.on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
|
||||||
|
.on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration)
|
||||||
.on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)
|
.on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)
|
||||||
.on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
|
.on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
|
||||||
.on::<lsp_types::request::Completion>(handlers::handle_completion)
|
.on::<lsp_types::request::Completion>(handlers::handle_completion)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user