diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index b2f4bdbb3a..97b6dfca68 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -18,10 +18,14 @@ use std::{ path::{PathBuf, Path}, }; -use libsyntax2::ast; +use libsyntax2::{ + TextUnit, + ast::{self, AstNode}, + algo::{find_leaf_at_offset, ancestors}, +}; use libeditor::{LineIndex, FileSymbol}; -use self::symbol_index::{FileSymbols}; +use self::symbol_index::FileSymbols; pub use self::symbol_index::Query; pub type Result = ::std::result::Result; @@ -90,8 +94,7 @@ impl World { Ok(index.clone()) } - pub fn world_symbols<'a>(&'a self, query: Query) -> impl Iterator + 'a - { + pub fn world_symbols<'a>(&'a self, query: Query) -> impl Iterator + 'a { self.data.file_map.iter() .flat_map(move |(path, data)| { let path: &'a Path = path.as_path(); @@ -100,6 +103,31 @@ impl World { }) } + pub fn approximately_resolve_symbol<'a>( + &'a self, + path: &Path, + offset: TextUnit, + ) -> Result> { + let file = self.file_syntax(path)?; + let syntax = file.syntax(); + let syntax = syntax.as_ref(); + let name_ref = + find_leaf_at_offset(syntax, offset) + .left_biased() + .into_iter() + .flat_map(|node| ancestors(node)) + .flat_map(ast::NameRef::cast) + .next(); + let name = match name_ref { + None => return Ok(vec![]), + Some(name_ref) => name_ref.text(), + }; + + let mut query = Query::new(name.to_string()); + query.exact(); + Ok(self.world_symbols(query).take(4).collect()) + } + fn file_data(&self, path: &Path) -> Result> { match self.data.file_map.get(path) { Some(data) => Ok(data.clone()), diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index 31f5ecc442..b1fd0a8adf 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -123,6 +123,24 @@ impl AstNode for Name { impl Name {} +// NameRef +#[derive(Debug, Clone, Copy)] +pub struct NameRef> { + syntax: SyntaxNode, +} + +impl AstNode for NameRef { + fn cast(syntax: SyntaxNode) -> Option { + match syntax.kind() { + NAME_REF => Some(NameRef { syntax }), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} + +impl NameRef {} + // StaticItem #[derive(Debug, Clone, Copy)] pub struct StaticItem> { diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs index e9362d0487..2e1fb2d1c1 100644 --- a/crates/libsyntax2/src/ast/mod.rs +++ b/crates/libsyntax2/src/ast/mod.rs @@ -73,3 +73,11 @@ impl Name { ident.leaf_text().unwrap() } } + +impl NameRef { + pub fn text(&self) -> SmolStr { + let ident = self.syntax().first_child() + .unwrap(); + ident.leaf_text().unwrap() + } +} diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index d4e8c53d3f..ee231931e1 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -228,5 +228,6 @@ Grammar( "StaticItem": ( traits: ["NameOwner"] ), "TypeItem": ( traits: ["NameOwner"] ), "Name": (), + "NameRef": (), }, ) diff --git a/crates/server/src/caps.rs b/crates/server/src/caps.rs index 4fd28b7c88..ffebd9b47d 100644 --- a/crates/server/src/caps.rs +++ b/crates/server/src/caps.rs @@ -20,7 +20,7 @@ pub fn server_capabilities() -> ServerCapabilities { hover_provider: None, completion_provider: None, signature_help_provider: None, - definition_provider: None, + definition_provider: Some(true), type_definition_provider: None, implementation_provider: None, references_provider: None, diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs index 0ed989b32f..1c31d32fef 100644 --- a/crates/server/src/conv.rs +++ b/crates/server/src/conv.rs @@ -1,7 +1,11 @@ -use languageserver_types::{Range, SymbolKind, Position, TextEdit}; +use std::path::Path; + +use languageserver_types::{Range, SymbolKind, Position, TextEdit, Location, Url}; use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; use libsyntax2::{SyntaxKind, TextUnit, TextRange}; +use Result; + pub trait Conv { type Output; fn conv(self) -> Self::Output; @@ -13,6 +17,12 @@ pub trait ConvWith { fn conv_with(self, ctx: &Self::Ctx) -> Self::Output; } +pub trait TryConvWith { + type Ctx; + type Output; + fn try_conv_with(self, ctx: &Self::Ctx) -> Result; +} + impl Conv for SyntaxKind { type Output = SymbolKind; @@ -104,6 +114,20 @@ impl ConvWith for AtomEdit { } } +impl<'a> TryConvWith for (&'a Path, TextRange) { + type Ctx = LineIndex; + type Output = Location; + + fn try_conv_with(self, line_index: &LineIndex) -> Result { + let loc = Location::new( + Url::from_file_path(self.0) + .map_err(|()| format_err!("can't convert path to url: {}", self.0.display()))?, + self.1.conv_with(line_index), + ); + Ok(loc) + } +} + pub trait MapConvWith<'a>: Sized { type Ctx; diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index f51909280a..e9dc784205 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use languageserver_types::{ Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, Command, TextDocumentIdentifier, WorkspaceEdit, - SymbolInformation, Location, + SymbolInformation, }; use libanalysis::{World, Query}; use libeditor; @@ -13,7 +13,7 @@ use serde_json::{to_value, from_value}; use ::{ req::{self, Decoration}, Result, util::FilePath, - conv::{Conv, ConvWith, MapConvWith}, + conv::{Conv, ConvWith, TryConvWith, MapConvWith}, }; pub fn handle_syntax_tree( @@ -115,15 +115,10 @@ pub fn handle_workspace_symbol( for (path, symbol) in world.world_symbols(query).take(128) { let line_index = world.file_line_index(path)?; - let info = SymbolInformation { name: symbol.name.to_string(), kind: symbol.kind.conv(), - location: Location::new( - Url::from_file_path(path) - .map_err(|()| format_err!("invalid url"))?, - symbol.node_range.conv_with(&line_index), - ), + location: (path, symbol.node_range).try_conv_with(&line_index)?, container_name: None, }; acc.push(info); @@ -132,6 +127,22 @@ pub fn handle_workspace_symbol( Ok(Some(acc)) } +pub fn handle_goto_definition( + world: World, + params: req::TextDocumentPositionParams, +) -> Result> { + let path = params.text_document.file_path()?; + let line_index = world.file_line_index(&path)?; + let offset = params.position.conv_with(&line_index); + let mut res = Vec::new(); + for (path, symbol) in world.approximately_resolve_symbol(&path, offset)? { + let line_index = world.file_line_index(path)?; + let location = (path, symbol.node_range).try_conv_with(&line_index)?; + res.push(location) + } + Ok(Some(req::GotoDefinitionResponse::Array(res))) +} + pub fn handle_execute_command( world: World, mut params: req::ExecuteCommandParams, diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index e8b24355c5..bc898c17bd 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs @@ -26,6 +26,7 @@ use { handle_code_action, handle_execute_command, handle_workspace_symbol, + handle_goto_definition, }, }; @@ -152,6 +153,9 @@ fn on_request( handle_request_on_threadpool::( &mut req, pool, world, sender, handle_workspace_symbol, )?; + handle_request_on_threadpool::( + &mut req, pool, world, sender, handle_goto_definition, + )?; dispatch::handle_request::(&mut req, |params, resp| { io.send(RawMsg::Response(resp.into_response(Ok(None))?)); diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index a8cc9b537b..17ef10e439 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs @@ -9,6 +9,7 @@ pub use languageserver_types::{ CodeActionParams, ApplyWorkspaceEditParams, ExecuteCommandParams, WorkspaceSymbolParams, + TextDocumentPositionParams, };