use ra_db::{FileId, Cancelable, SyntaxDatabase}; use ra_syntax::{TextRange, AstNode, ast, SyntaxKind::{NAME, MODULE}}; use ra_editor::find_node_at_offset; use crate::{FilePosition, NavigationTarget, db::RootDatabase}; pub(crate) fn goto_defenition( db: &RootDatabase, position: FilePosition, ) -> Cancelable>> { let file = db.source_file(position.file_id); let syntax = file.syntax(); if let Some(name_ref) = find_node_at_offset::(syntax, position.offset) { return Ok(Some(reference_defenition(db, position.file_id, name_ref)?)); } if let Some(name) = find_node_at_offset::(syntax, position.offset) { return name_defenition(db, position.file_id, name); } Ok(None) } fn reference_defenition( db: &RootDatabase, file_id: FileId, name_ref: ast::NameRef, ) -> Cancelable> { if let Some(fn_descr) = hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())? { let scope = fn_descr.scopes(db); // First try to resolve the symbol locally if let Some(entry) = scope.resolve_local_name(name_ref) { let nav = NavigationTarget { file_id, name: entry.name().to_string().into(), range: entry.ptr().range(), kind: NAME, ptr: None, }; return Ok(vec![nav]); }; } // If that fails try the index based approach. let navs = db .index_resolve(name_ref)? .into_iter() .map(NavigationTarget::from_symbol) .collect(); Ok(navs) } fn name_defenition( db: &RootDatabase, file_id: FileId, name: ast::Name, ) -> Cancelable>> { if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { if module.has_semi() { if let Some(child_module) = hir::source_binder::module_from_declaration(db, file_id, module)? { let file_id = child_module.file_id(); let name = match child_module.name() { Some(name) => name.to_string().into(), None => "".into(), }; let nav = NavigationTarget { file_id, name, range: TextRange::offset_len(0.into(), 0.into()), kind: MODULE, ptr: None, }; return Ok(Some(vec![nav])); } } } Ok(None) }