diff options
author | Aleksey Kladov <[email protected]> | 2018-08-13 14:35:17 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-13 14:35:17 +0100 |
commit | 8ae56fa6d0e8a03d6ad75919d6be953f5fc27083 (patch) | |
tree | d93a4f3e1d279a27cc851546796bb488edfe2c65 | |
parent | 7fc91f41d8bd948cef3085d7c0d0ec92d1b2bc53 (diff) |
Stupid goto definition
-rw-r--r-- | crates/libanalysis/src/lib.rs | 36 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/generated.rs | 18 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/mod.rs | 8 | ||||
-rw-r--r-- | crates/libsyntax2/src/grammar.ron | 1 | ||||
-rw-r--r-- | crates/server/src/caps.rs | 2 | ||||
-rw-r--r-- | crates/server/src/conv.rs | 26 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 27 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 4 | ||||
-rw-r--r-- | crates/server/src/req.rs | 1 |
9 files changed, 109 insertions, 14 deletions
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index b2f4bdbb3..97b6dfca6 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs | |||
@@ -18,10 +18,14 @@ use std::{ | |||
18 | path::{PathBuf, Path}, | 18 | path::{PathBuf, Path}, |
19 | }; | 19 | }; |
20 | 20 | ||
21 | use libsyntax2::ast; | 21 | use libsyntax2::{ |
22 | TextUnit, | ||
23 | ast::{self, AstNode}, | ||
24 | algo::{find_leaf_at_offset, ancestors}, | ||
25 | }; | ||
22 | use libeditor::{LineIndex, FileSymbol}; | 26 | use libeditor::{LineIndex, FileSymbol}; |
23 | 27 | ||
24 | use self::symbol_index::{FileSymbols}; | 28 | use self::symbol_index::FileSymbols; |
25 | pub use self::symbol_index::Query; | 29 | pub use self::symbol_index::Query; |
26 | 30 | ||
27 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 31 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
@@ -90,8 +94,7 @@ impl World { | |||
90 | Ok(index.clone()) | 94 | Ok(index.clone()) |
91 | } | 95 | } |
92 | 96 | ||
93 | pub fn world_symbols<'a>(&'a self, query: Query) -> impl Iterator<Item=(&'a Path, &'a FileSymbol)> + 'a | 97 | pub fn world_symbols<'a>(&'a self, query: Query) -> impl Iterator<Item=(&'a Path, &'a FileSymbol)> + 'a { |
94 | { | ||
95 | self.data.file_map.iter() | 98 | self.data.file_map.iter() |
96 | .flat_map(move |(path, data)| { | 99 | .flat_map(move |(path, data)| { |
97 | let path: &'a Path = path.as_path(); | 100 | let path: &'a Path = path.as_path(); |
@@ -100,6 +103,31 @@ impl World { | |||
100 | }) | 103 | }) |
101 | } | 104 | } |
102 | 105 | ||
106 | pub fn approximately_resolve_symbol<'a>( | ||
107 | &'a self, | ||
108 | path: &Path, | ||
109 | offset: TextUnit, | ||
110 | ) -> Result<Vec<(&'a Path, &'a FileSymbol)>> { | ||
111 | let file = self.file_syntax(path)?; | ||
112 | let syntax = file.syntax(); | ||
113 | let syntax = syntax.as_ref(); | ||
114 | let name_ref = | ||
115 | find_leaf_at_offset(syntax, offset) | ||
116 | .left_biased() | ||
117 | .into_iter() | ||
118 | .flat_map(|node| ancestors(node)) | ||
119 | .flat_map(ast::NameRef::cast) | ||
120 | .next(); | ||
121 | let name = match name_ref { | ||
122 | None => return Ok(vec![]), | ||
123 | Some(name_ref) => name_ref.text(), | ||
124 | }; | ||
125 | |||
126 | let mut query = Query::new(name.to_string()); | ||
127 | query.exact(); | ||
128 | Ok(self.world_symbols(query).take(4).collect()) | ||
129 | } | ||
130 | |||
103 | fn file_data(&self, path: &Path) -> Result<Arc<FileData>> { | 131 | fn file_data(&self, path: &Path) -> Result<Arc<FileData>> { |
104 | match self.data.file_map.get(path) { | 132 | match self.data.file_map.get(path) { |
105 | Some(data) => Ok(data.clone()), | 133 | Some(data) => Ok(data.clone()), |
diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index 31f5ecc44..b1fd0a8ad 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs | |||
@@ -123,6 +123,24 @@ impl<R: TreeRoot> AstNode<R> for Name<R> { | |||
123 | 123 | ||
124 | impl<R: TreeRoot> Name<R> {} | 124 | impl<R: TreeRoot> Name<R> {} |
125 | 125 | ||
126 | // NameRef | ||
127 | #[derive(Debug, Clone, Copy)] | ||
128 | pub struct NameRef<R: TreeRoot = Arc<SyntaxRoot>> { | ||
129 | syntax: SyntaxNode<R>, | ||
130 | } | ||
131 | |||
132 | impl<R: TreeRoot> AstNode<R> for NameRef<R> { | ||
133 | fn cast(syntax: SyntaxNode<R>) -> Option<Self> { | ||
134 | match syntax.kind() { | ||
135 | NAME_REF => Some(NameRef { syntax }), | ||
136 | _ => None, | ||
137 | } | ||
138 | } | ||
139 | fn syntax(&self) -> &SyntaxNode<R> { &self.syntax } | ||
140 | } | ||
141 | |||
142 | impl<R: TreeRoot> NameRef<R> {} | ||
143 | |||
126 | // StaticItem | 144 | // StaticItem |
127 | #[derive(Debug, Clone, Copy)] | 145 | #[derive(Debug, Clone, Copy)] |
128 | pub struct StaticItem<R: TreeRoot = Arc<SyntaxRoot>> { | 146 | pub struct StaticItem<R: TreeRoot = Arc<SyntaxRoot>> { |
diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs index e9362d048..2e1fb2d1c 100644 --- a/crates/libsyntax2/src/ast/mod.rs +++ b/crates/libsyntax2/src/ast/mod.rs | |||
@@ -73,3 +73,11 @@ impl<R: TreeRoot> Name<R> { | |||
73 | ident.leaf_text().unwrap() | 73 | ident.leaf_text().unwrap() |
74 | } | 74 | } |
75 | } | 75 | } |
76 | |||
77 | impl<R: TreeRoot> NameRef<R> { | ||
78 | pub fn text(&self) -> SmolStr { | ||
79 | let ident = self.syntax().first_child() | ||
80 | .unwrap(); | ||
81 | ident.leaf_text().unwrap() | ||
82 | } | ||
83 | } | ||
diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index d4e8c53d3..ee231931e 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron | |||
@@ -228,5 +228,6 @@ Grammar( | |||
228 | "StaticItem": ( traits: ["NameOwner"] ), | 228 | "StaticItem": ( traits: ["NameOwner"] ), |
229 | "TypeItem": ( traits: ["NameOwner"] ), | 229 | "TypeItem": ( traits: ["NameOwner"] ), |
230 | "Name": (), | 230 | "Name": (), |
231 | "NameRef": (), | ||
231 | }, | 232 | }, |
232 | ) | 233 | ) |
diff --git a/crates/server/src/caps.rs b/crates/server/src/caps.rs index 4fd28b7c8..ffebd9b47 100644 --- a/crates/server/src/caps.rs +++ b/crates/server/src/caps.rs | |||
@@ -20,7 +20,7 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
20 | hover_provider: None, | 20 | hover_provider: None, |
21 | completion_provider: None, | 21 | completion_provider: None, |
22 | signature_help_provider: None, | 22 | signature_help_provider: None, |
23 | definition_provider: None, | 23 | definition_provider: Some(true), |
24 | type_definition_provider: None, | 24 | type_definition_provider: None, |
25 | implementation_provider: None, | 25 | implementation_provider: None, |
26 | references_provider: None, | 26 | references_provider: None, |
diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs index 0ed989b32..1c31d32fe 100644 --- a/crates/server/src/conv.rs +++ b/crates/server/src/conv.rs | |||
@@ -1,7 +1,11 @@ | |||
1 | use languageserver_types::{Range, SymbolKind, Position, TextEdit}; | 1 | use std::path::Path; |
2 | |||
3 | use languageserver_types::{Range, SymbolKind, Position, TextEdit, Location, Url}; | ||
2 | use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; | 4 | use libeditor::{LineIndex, LineCol, Edit, AtomEdit}; |
3 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; | 5 | use libsyntax2::{SyntaxKind, TextUnit, TextRange}; |
4 | 6 | ||
7 | use Result; | ||
8 | |||
5 | pub trait Conv { | 9 | pub trait Conv { |
6 | type Output; | 10 | type Output; |
7 | fn conv(self) -> Self::Output; | 11 | fn conv(self) -> Self::Output; |
@@ -13,6 +17,12 @@ pub trait ConvWith { | |||
13 | fn conv_with(self, ctx: &Self::Ctx) -> Self::Output; | 17 | fn conv_with(self, ctx: &Self::Ctx) -> Self::Output; |
14 | } | 18 | } |
15 | 19 | ||
20 | pub trait TryConvWith { | ||
21 | type Ctx; | ||
22 | type Output; | ||
23 | fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output>; | ||
24 | } | ||
25 | |||
16 | impl Conv for SyntaxKind { | 26 | impl Conv for SyntaxKind { |
17 | type Output = SymbolKind; | 27 | type Output = SymbolKind; |
18 | 28 | ||
@@ -104,6 +114,20 @@ impl ConvWith for AtomEdit { | |||
104 | } | 114 | } |
105 | } | 115 | } |
106 | 116 | ||
117 | impl<'a> TryConvWith for (&'a Path, TextRange) { | ||
118 | type Ctx = LineIndex; | ||
119 | type Output = Location; | ||
120 | |||
121 | fn try_conv_with(self, line_index: &LineIndex) -> Result<Location> { | ||
122 | let loc = Location::new( | ||
123 | Url::from_file_path(self.0) | ||
124 | .map_err(|()| format_err!("can't convert path to url: {}", self.0.display()))?, | ||
125 | self.1.conv_with(line_index), | ||
126 | ); | ||
127 | Ok(loc) | ||
128 | } | ||
129 | } | ||
130 | |||
107 | 131 | ||
108 | pub trait MapConvWith<'a>: Sized { | 132 | pub trait MapConvWith<'a>: Sized { |
109 | type Ctx; | 133 | type Ctx; |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index f51909280..e9dc78420 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; | |||
3 | use languageserver_types::{ | 3 | use languageserver_types::{ |
4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, | 4 | Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, |
5 | Command, TextDocumentIdentifier, WorkspaceEdit, | 5 | Command, TextDocumentIdentifier, WorkspaceEdit, |
6 | SymbolInformation, Location, | 6 | SymbolInformation, |
7 | }; | 7 | }; |
8 | use libanalysis::{World, Query}; | 8 | use libanalysis::{World, Query}; |
9 | use libeditor; | 9 | use libeditor; |
@@ -13,7 +13,7 @@ use serde_json::{to_value, from_value}; | |||
13 | use ::{ | 13 | use ::{ |
14 | req::{self, Decoration}, Result, | 14 | req::{self, Decoration}, Result, |
15 | util::FilePath, | 15 | util::FilePath, |
16 | conv::{Conv, ConvWith, MapConvWith}, | 16 | conv::{Conv, ConvWith, TryConvWith, MapConvWith}, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | pub fn handle_syntax_tree( | 19 | pub fn handle_syntax_tree( |
@@ -115,15 +115,10 @@ pub fn handle_workspace_symbol( | |||
115 | 115 | ||
116 | for (path, symbol) in world.world_symbols(query).take(128) { | 116 | for (path, symbol) in world.world_symbols(query).take(128) { |
117 | let line_index = world.file_line_index(path)?; | 117 | let line_index = world.file_line_index(path)?; |
118 | |||
119 | let info = SymbolInformation { | 118 | let info = SymbolInformation { |
120 | name: symbol.name.to_string(), | 119 | name: symbol.name.to_string(), |
121 | kind: symbol.kind.conv(), | 120 | kind: symbol.kind.conv(), |
122 | location: Location::new( | 121 | location: (path, symbol.node_range).try_conv_with(&line_index)?, |
123 | Url::from_file_path(path) | ||
124 | .map_err(|()| format_err!("invalid url"))?, | ||
125 | symbol.node_range.conv_with(&line_index), | ||
126 | ), | ||
127 | container_name: None, | 122 | container_name: None, |
128 | }; | 123 | }; |
129 | acc.push(info); | 124 | acc.push(info); |
@@ -132,6 +127,22 @@ pub fn handle_workspace_symbol( | |||
132 | Ok(Some(acc)) | 127 | Ok(Some(acc)) |
133 | } | 128 | } |
134 | 129 | ||
130 | pub fn handle_goto_definition( | ||
131 | world: World, | ||
132 | params: req::TextDocumentPositionParams, | ||
133 | ) -> Result<Option<req::GotoDefinitionResponse>> { | ||
134 | let path = params.text_document.file_path()?; | ||
135 | let line_index = world.file_line_index(&path)?; | ||
136 | let offset = params.position.conv_with(&line_index); | ||
137 | let mut res = Vec::new(); | ||
138 | for (path, symbol) in world.approximately_resolve_symbol(&path, offset)? { | ||
139 | let line_index = world.file_line_index(path)?; | ||
140 | let location = (path, symbol.node_range).try_conv_with(&line_index)?; | ||
141 | res.push(location) | ||
142 | } | ||
143 | Ok(Some(req::GotoDefinitionResponse::Array(res))) | ||
144 | } | ||
145 | |||
135 | pub fn handle_execute_command( | 146 | pub fn handle_execute_command( |
136 | world: World, | 147 | world: World, |
137 | mut params: req::ExecuteCommandParams, | 148 | mut params: req::ExecuteCommandParams, |
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index e8b24355c..bc898c17b 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -26,6 +26,7 @@ use { | |||
26 | handle_code_action, | 26 | handle_code_action, |
27 | handle_execute_command, | 27 | handle_execute_command, |
28 | handle_workspace_symbol, | 28 | handle_workspace_symbol, |
29 | handle_goto_definition, | ||
29 | }, | 30 | }, |
30 | }; | 31 | }; |
31 | 32 | ||
@@ -152,6 +153,9 @@ fn on_request( | |||
152 | handle_request_on_threadpool::<req::WorkspaceSymbol>( | 153 | handle_request_on_threadpool::<req::WorkspaceSymbol>( |
153 | &mut req, pool, world, sender, handle_workspace_symbol, | 154 | &mut req, pool, world, sender, handle_workspace_symbol, |
154 | )?; | 155 | )?; |
156 | handle_request_on_threadpool::<req::GotoDefinition>( | ||
157 | &mut req, pool, world, sender, handle_goto_definition, | ||
158 | )?; | ||
155 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { | 159 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { |
156 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); | 160 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); |
157 | 161 | ||
diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index a8cc9b537..17ef10e43 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs | |||
@@ -9,6 +9,7 @@ pub use languageserver_types::{ | |||
9 | CodeActionParams, ApplyWorkspaceEditParams, | 9 | CodeActionParams, ApplyWorkspaceEditParams, |
10 | ExecuteCommandParams, | 10 | ExecuteCommandParams, |
11 | WorkspaceSymbolParams, | 11 | WorkspaceSymbolParams, |
12 | TextDocumentPositionParams, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | 15 | ||