diff options
-rw-r--r-- | crates/ra_analysis/src/completion.rs | 31 | ||||
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 74 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 52 | ||||
-rw-r--r-- | crates/ra_analysis/src/mock_analysis.rs | 9 | ||||
-rw-r--r-- | crates/ra_analysis/tests/tests.rs | 26 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 13 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 76 |
7 files changed, 124 insertions, 157 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index 766df1d96..7c3476e5c 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -14,7 +14,7 @@ use crate::{ | |||
14 | descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource}, | 14 | descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource}, |
15 | descriptors::DescriptorDatabase, | 15 | descriptors::DescriptorDatabase, |
16 | input::FilesDatabase, | 16 | input::FilesDatabase, |
17 | Cancelable, FileId, | 17 | Cancelable, FilePosition, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | #[derive(Debug)] | 20 | #[derive(Debug)] |
@@ -29,21 +29,21 @@ pub struct CompletionItem { | |||
29 | 29 | ||
30 | pub(crate) fn resolve_based_completion( | 30 | pub(crate) fn resolve_based_completion( |
31 | db: &db::RootDatabase, | 31 | db: &db::RootDatabase, |
32 | file_id: FileId, | 32 | position: FilePosition, |
33 | offset: TextUnit, | ||
34 | ) -> Cancelable<Option<Vec<CompletionItem>>> { | 33 | ) -> Cancelable<Option<Vec<CompletionItem>>> { |
35 | let source_root_id = db.file_source_root(file_id); | 34 | let source_root_id = db.file_source_root(position.file_id); |
36 | let file = db.file_syntax(file_id); | 35 | let file = db.file_syntax(position.file_id); |
37 | let module_tree = db.module_tree(source_root_id)?; | 36 | let module_tree = db.module_tree(source_root_id)?; |
38 | let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) { | 37 | let module_id = match module_tree.any_module_for_source(ModuleSource::File(position.file_id)) { |
39 | None => return Ok(None), | 38 | None => return Ok(None), |
40 | Some(it) => it, | 39 | Some(it) => it, |
41 | }; | 40 | }; |
42 | let file = { | 41 | let file = { |
43 | let edit = AtomEdit::insert(offset, "intellijRulezz".to_string()); | 42 | let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string()); |
44 | file.reparse(&edit) | 43 | file.reparse(&edit) |
45 | }; | 44 | }; |
46 | let target_module_id = match find_target_module(&module_tree, module_id, &file, offset) { | 45 | let target_module_id = match find_target_module(&module_tree, module_id, &file, position.offset) |
46 | { | ||
47 | None => return Ok(None), | 47 | None => return Ok(None), |
48 | Some(it) => it, | 48 | Some(it) => it, |
49 | }; | 49 | }; |
@@ -99,18 +99,17 @@ fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> { | |||
99 | 99 | ||
100 | pub(crate) fn scope_completion( | 100 | pub(crate) fn scope_completion( |
101 | db: &db::RootDatabase, | 101 | db: &db::RootDatabase, |
102 | file_id: FileId, | 102 | position: FilePosition, |
103 | offset: TextUnit, | ||
104 | ) -> Option<Vec<CompletionItem>> { | 103 | ) -> Option<Vec<CompletionItem>> { |
105 | let original_file = db.file_syntax(file_id); | 104 | let original_file = db.file_syntax(position.file_id); |
106 | // Insert a fake ident to get a valid parse tree | 105 | // Insert a fake ident to get a valid parse tree |
107 | let file = { | 106 | let file = { |
108 | let edit = AtomEdit::insert(offset, "intellijRulezz".to_string()); | 107 | let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string()); |
109 | original_file.reparse(&edit) | 108 | original_file.reparse(&edit) |
110 | }; | 109 | }; |
111 | let mut has_completions = false; | 110 | let mut has_completions = false; |
112 | let mut res = Vec::new(); | 111 | let mut res = Vec::new(); |
113 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) { | 112 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { |
114 | has_completions = true; | 113 | has_completions = true; |
115 | complete_name_ref(&file, name_ref, &mut res); | 114 | complete_name_ref(&file, name_ref, &mut res); |
116 | // special case, `trait T { fn foo(i_am_a_name_ref) {} }` | 115 | // special case, `trait T { fn foo(i_am_a_name_ref) {} }` |
@@ -129,7 +128,7 @@ pub(crate) fn scope_completion( | |||
129 | _ => (), | 128 | _ => (), |
130 | } | 129 | } |
131 | } | 130 | } |
132 | if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) { | 131 | if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { |
133 | if is_node::<ast::Param>(name.syntax()) { | 132 | if is_node::<ast::Param>(name.syntax()) { |
134 | has_completions = true; | 133 | has_completions = true; |
135 | param_completions(name.syntax(), &mut res); | 134 | param_completions(name.syntax(), &mut res); |
@@ -383,7 +382,7 @@ mod tests { | |||
383 | 382 | ||
384 | fn check_scope_completion(code: &str, expected_completions: &str) { | 383 | fn check_scope_completion(code: &str, expected_completions: &str) { |
385 | let (analysis, position) = single_file_with_position(code); | 384 | let (analysis, position) = single_file_with_position(code); |
386 | let completions = scope_completion(&analysis.imp.db, position.file_id, position.offset) | 385 | let completions = scope_completion(&analysis.imp.db, position) |
387 | .unwrap() | 386 | .unwrap() |
388 | .into_iter() | 387 | .into_iter() |
389 | .filter(|c| c.snippet.is_none()) | 388 | .filter(|c| c.snippet.is_none()) |
@@ -393,7 +392,7 @@ mod tests { | |||
393 | 392 | ||
394 | fn check_snippet_completion(code: &str, expected_completions: &str) { | 393 | fn check_snippet_completion(code: &str, expected_completions: &str) { |
395 | let (analysis, position) = single_file_with_position(code); | 394 | let (analysis, position) = single_file_with_position(code); |
396 | let completions = scope_completion(&analysis.imp.db, position.file_id, position.offset) | 395 | let completions = scope_completion(&analysis.imp.db, position) |
397 | .unwrap() | 396 | .unwrap() |
398 | .into_iter() | 397 | .into_iter() |
399 | .filter(|c| c.snippet.is_some()) | 398 | .filter(|c| c.snippet.is_some()) |
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 86d9cecd2..f2482559f 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -220,16 +220,13 @@ impl AnalysisImpl { | |||
220 | let source_root = self.db.file_source_root(file_id); | 220 | let source_root = self.db.file_source_root(file_id); |
221 | self.db.module_tree(source_root) | 221 | self.db.module_tree(source_root) |
222 | } | 222 | } |
223 | pub fn parent_module( | 223 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
224 | &self, | 224 | let module_tree = self.module_tree(position.file_id)?; |
225 | file_id: FileId, | 225 | let file = self.db.file_syntax(position.file_id); |
226 | offset: TextUnit, | 226 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) |
227 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 227 | { |
228 | let module_tree = self.module_tree(file_id)?; | 228 | Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m), |
229 | let file = self.db.file_syntax(file_id); | 229 | _ => ModuleSource::File(position.file_id), |
230 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), offset) { | ||
231 | Some(m) if !m.has_semi() => ModuleSource::new_inline(file_id, m), | ||
232 | _ => ModuleSource::File(file_id), | ||
233 | }; | 230 | }; |
234 | 231 | ||
235 | let res = module_tree | 232 | let res = module_tree |
@@ -269,18 +266,14 @@ impl AnalysisImpl { | |||
269 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | 266 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { |
270 | self.db.crate_graph().crate_roots[&crate_id] | 267 | self.db.crate_graph().crate_roots[&crate_id] |
271 | } | 268 | } |
272 | pub fn completions( | 269 | pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { |
273 | &self, | ||
274 | file_id: FileId, | ||
275 | offset: TextUnit, | ||
276 | ) -> Cancelable<Option<Vec<CompletionItem>>> { | ||
277 | let mut res = Vec::new(); | 270 | let mut res = Vec::new(); |
278 | let mut has_completions = false; | 271 | let mut has_completions = false; |
279 | if let Some(scope_based) = scope_completion(&self.db, file_id, offset) { | 272 | if let Some(scope_based) = scope_completion(&self.db, position) { |
280 | res.extend(scope_based); | 273 | res.extend(scope_based); |
281 | has_completions = true; | 274 | has_completions = true; |
282 | } | 275 | } |
283 | if let Some(scope_based) = resolve_based_completion(&self.db, file_id, offset)? { | 276 | if let Some(scope_based) = resolve_based_completion(&self.db, position)? { |
284 | res.extend(scope_based); | 277 | res.extend(scope_based); |
285 | has_completions = true; | 278 | has_completions = true; |
286 | } | 279 | } |
@@ -289,18 +282,19 @@ impl AnalysisImpl { | |||
289 | } | 282 | } |
290 | pub fn approximately_resolve_symbol( | 283 | pub fn approximately_resolve_symbol( |
291 | &self, | 284 | &self, |
292 | file_id: FileId, | 285 | position: FilePosition, |
293 | offset: TextUnit, | ||
294 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 286 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
295 | let module_tree = self.module_tree(file_id)?; | 287 | let module_tree = self.module_tree(position.file_id)?; |
296 | let file = self.db.file_syntax(file_id); | 288 | let file = self.db.file_syntax(position.file_id); |
297 | let syntax = file.syntax(); | 289 | let syntax = file.syntax(); |
298 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) { | 290 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { |
299 | // First try to resolve the symbol locally | 291 | // First try to resolve the symbol locally |
300 | return if let Some((name, range)) = resolve_local_name(&self.db, file_id, name_ref) { | 292 | return if let Some((name, range)) = |
293 | resolve_local_name(&self.db, position.file_id, name_ref) | ||
294 | { | ||
301 | let mut vec = vec![]; | 295 | let mut vec = vec![]; |
302 | vec.push(( | 296 | vec.push(( |
303 | file_id, | 297 | position.file_id, |
304 | FileSymbol { | 298 | FileSymbol { |
305 | name, | 299 | name, |
306 | node_range: range, | 300 | node_range: range, |
@@ -313,10 +307,10 @@ impl AnalysisImpl { | |||
313 | self.index_resolve(name_ref) | 307 | self.index_resolve(name_ref) |
314 | }; | 308 | }; |
315 | } | 309 | } |
316 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) { | 310 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { |
317 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | 311 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { |
318 | if module.has_semi() { | 312 | if module.has_semi() { |
319 | let file_ids = self.resolve_module(&*module_tree, file_id, module); | 313 | let file_ids = self.resolve_module(&*module_tree, position.file_id, module); |
320 | 314 | ||
321 | let res = file_ids | 315 | let res = file_ids |
322 | .into_iter() | 316 | .into_iter() |
@@ -341,16 +335,17 @@ impl AnalysisImpl { | |||
341 | Ok(vec![]) | 335 | Ok(vec![]) |
342 | } | 336 | } |
343 | 337 | ||
344 | pub fn find_all_refs(&self, file_id: FileId, offset: TextUnit) -> Vec<(FileId, TextRange)> { | 338 | pub fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> { |
345 | let file = self.db.file_syntax(file_id); | 339 | let file = self.db.file_syntax(position.file_id); |
346 | let syntax = file.syntax(); | 340 | let syntax = file.syntax(); |
347 | 341 | ||
348 | // Find the binding associated with the offset | 342 | // Find the binding associated with the offset |
349 | let maybe_binding = find_node_at_offset::<ast::BindPat>(syntax, offset).or_else(|| { | 343 | let maybe_binding = |
350 | let name_ref = find_node_at_offset::<ast::NameRef>(syntax, offset)?; | 344 | find_node_at_offset::<ast::BindPat>(syntax, position.offset).or_else(|| { |
351 | let resolved = resolve_local_name(&self.db, file_id, name_ref)?; | 345 | let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; |
352 | find_node_at_offset::<ast::BindPat>(syntax, resolved.1.end()) | 346 | let resolved = resolve_local_name(&self.db, position.file_id, name_ref)?; |
353 | }); | 347 | find_node_at_offset::<ast::BindPat>(syntax, resolved.1.end()) |
348 | }); | ||
354 | 349 | ||
355 | let binding = match maybe_binding { | 350 | let binding = match maybe_binding { |
356 | None => return Vec::new(), | 351 | None => return Vec::new(), |
@@ -359,11 +354,11 @@ impl AnalysisImpl { | |||
359 | 354 | ||
360 | let decl = DeclarationDescriptor::new(binding); | 355 | let decl = DeclarationDescriptor::new(binding); |
361 | 356 | ||
362 | let mut ret = vec![(file_id, decl.range)]; | 357 | let mut ret = vec![(position.file_id, decl.range)]; |
363 | ret.extend( | 358 | ret.extend( |
364 | decl.find_all_refs() | 359 | decl.find_all_refs() |
365 | .into_iter() | 360 | .into_iter() |
366 | .map(|ref_desc| (file_id, ref_desc.range)), | 361 | .map(|ref_desc| (position.file_id, ref_desc.range)), |
367 | ); | 362 | ); |
368 | 363 | ||
369 | ret | 364 | ret |
@@ -457,14 +452,13 @@ impl AnalysisImpl { | |||
457 | 452 | ||
458 | pub fn resolve_callable( | 453 | pub fn resolve_callable( |
459 | &self, | 454 | &self, |
460 | file_id: FileId, | 455 | position: FilePosition, |
461 | offset: TextUnit, | ||
462 | ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { | 456 | ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { |
463 | let file = self.db.file_syntax(file_id); | 457 | let file = self.db.file_syntax(position.file_id); |
464 | let syntax = file.syntax(); | 458 | let syntax = file.syntax(); |
465 | 459 | ||
466 | // Find the calling expression and it's NameRef | 460 | // Find the calling expression and it's NameRef |
467 | let calling_node = match FnCallNode::with_node(syntax, offset) { | 461 | let calling_node = match FnCallNode::with_node(syntax, position.offset) { |
468 | Some(node) => node, | 462 | Some(node) => node, |
469 | None => return Ok(None), | 463 | None => return Ok(None), |
470 | }; | 464 | }; |
@@ -499,7 +493,7 @@ impl AnalysisImpl { | |||
499 | if let Some(ref arg_list) = calling_node.arg_list() { | 493 | if let Some(ref arg_list) = calling_node.arg_list() { |
500 | let start = arg_list.syntax().range().start(); | 494 | let start = arg_list.syntax().range().start(); |
501 | 495 | ||
502 | let range_search = TextRange::from_to(start, offset); | 496 | let range_search = TextRange::from_to(start, position.offset); |
503 | let mut commas: usize = arg_list | 497 | let mut commas: usize = arg_list |
504 | .syntax() | 498 | .syntax() |
505 | .text() | 499 | .text() |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 3bbe61917..0ea9ebee7 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -119,7 +119,7 @@ impl AnalysisHost { | |||
119 | } | 119 | } |
120 | } | 120 | } |
121 | 121 | ||
122 | #[derive(Debug)] | 122 | #[derive(Clone, Copy, Debug)] |
123 | pub struct FilePosition { | 123 | pub struct FilePosition { |
124 | pub file_id: FileId, | 124 | pub file_id: FileId, |
125 | pub offset: TextUnit, | 125 | pub offset: TextUnit, |
@@ -224,18 +224,18 @@ impl Analysis { | |||
224 | let file = self.imp.file_syntax(file_id); | 224 | let file = self.imp.file_syntax(file_id); |
225 | SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range)) | 225 | SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range)) |
226 | } | 226 | } |
227 | pub fn on_enter(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> { | 227 | pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { |
228 | let file = self.imp.file_syntax(file_id); | 228 | let file = self.imp.file_syntax(position.file_id); |
229 | let edit = ra_editor::on_enter(&file, offset)?; | 229 | let edit = ra_editor::on_enter(&file, position.offset)?; |
230 | let res = SourceChange::from_local_edit(file_id, "on enter", edit); | 230 | let res = SourceChange::from_local_edit(position.file_id, "on enter", edit); |
231 | Some(res) | 231 | Some(res) |
232 | } | 232 | } |
233 | pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> { | 233 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { |
234 | let file = self.imp.file_syntax(file_id); | 234 | let file = self.imp.file_syntax(position.file_id); |
235 | Some(SourceChange::from_local_edit( | 235 | Some(SourceChange::from_local_edit( |
236 | file_id, | 236 | position.file_id, |
237 | "add semicolon", | 237 | "add semicolon", |
238 | ra_editor::on_eq_typed(&file, offset)?, | 238 | ra_editor::on_eq_typed(&file, position.offset)?, |
239 | )) | 239 | )) |
240 | } | 240 | } |
241 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { | 241 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { |
@@ -251,24 +251,15 @@ impl Analysis { | |||
251 | } | 251 | } |
252 | pub fn approximately_resolve_symbol( | 252 | pub fn approximately_resolve_symbol( |
253 | &self, | 253 | &self, |
254 | file_id: FileId, | 254 | position: FilePosition, |
255 | offset: TextUnit, | ||
256 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 255 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
257 | self.imp.approximately_resolve_symbol(file_id, offset) | 256 | self.imp.approximately_resolve_symbol(position) |
258 | } | 257 | } |
259 | pub fn find_all_refs( | 258 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { |
260 | &self, | 259 | Ok(self.imp.find_all_refs(position)) |
261 | file_id: FileId, | ||
262 | offset: TextUnit, | ||
263 | ) -> Cancelable<Vec<(FileId, TextRange)>> { | ||
264 | Ok(self.imp.find_all_refs(file_id, offset)) | ||
265 | } | 260 | } |
266 | pub fn parent_module( | 261 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
267 | &self, | 262 | self.imp.parent_module(position) |
268 | file_id: FileId, | ||
269 | offset: TextUnit, | ||
270 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | ||
271 | self.imp.parent_module(file_id, offset) | ||
272 | } | 263 | } |
273 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 264 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
274 | self.imp.crate_for(file_id) | 265 | self.imp.crate_for(file_id) |
@@ -284,12 +275,8 @@ impl Analysis { | |||
284 | let file = self.imp.file_syntax(file_id); | 275 | let file = self.imp.file_syntax(file_id); |
285 | Ok(ra_editor::highlight(&file)) | 276 | Ok(ra_editor::highlight(&file)) |
286 | } | 277 | } |
287 | pub fn completions( | 278 | pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { |
288 | &self, | 279 | self.imp.completions(position) |
289 | file_id: FileId, | ||
290 | offset: TextUnit, | ||
291 | ) -> Cancelable<Option<Vec<CompletionItem>>> { | ||
292 | self.imp.completions(file_id, offset) | ||
293 | } | 280 | } |
294 | pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> { | 281 | pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> { |
295 | Ok(self.imp.assists(file_id, range)) | 282 | Ok(self.imp.assists(file_id, range)) |
@@ -299,10 +286,9 @@ impl Analysis { | |||
299 | } | 286 | } |
300 | pub fn resolve_callable( | 287 | pub fn resolve_callable( |
301 | &self, | 288 | &self, |
302 | file_id: FileId, | 289 | position: FilePosition, |
303 | offset: TextUnit, | ||
304 | ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { | 290 | ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { |
305 | self.imp.resolve_callable(file_id, offset) | 291 | self.imp.resolve_callable(position) |
306 | } | 292 | } |
307 | } | 293 | } |
308 | 294 | ||
diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs index a7134a0e6..8e8f969f4 100644 --- a/crates/ra_analysis/src/mock_analysis.rs +++ b/crates/ra_analysis/src/mock_analysis.rs | |||
@@ -1,16 +1,9 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::TextUnit; | ||
4 | use relative_path::{RelativePath, RelativePathBuf}; | 3 | use relative_path::{RelativePath, RelativePathBuf}; |
5 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; | 4 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; |
6 | 5 | ||
7 | use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver}; | 6 | use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver, FilePosition}; |
8 | |||
9 | #[derive(Debug)] | ||
10 | pub struct FilePosition { | ||
11 | pub file_id: FileId, | ||
12 | pub offset: TextUnit, | ||
13 | } | ||
14 | 7 | ||
15 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis | 8 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis |
16 | /// from a set of in-memory files. | 9 | /// from a set of in-memory files. |
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index 7f7bb8e6b..c605d34f0 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -15,10 +15,7 @@ use ra_analysis::{ | |||
15 | 15 | ||
16 | fn get_signature(text: &str) -> (FnDescriptor, Option<usize>) { | 16 | fn get_signature(text: &str) -> (FnDescriptor, Option<usize>) { |
17 | let (analysis, position) = single_file_with_position(text); | 17 | let (analysis, position) = single_file_with_position(text); |
18 | analysis | 18 | analysis.resolve_callable(position).unwrap().unwrap() |
19 | .resolve_callable(position.file_id, position.offset) | ||
20 | .unwrap() | ||
21 | .unwrap() | ||
22 | } | 19 | } |
23 | 20 | ||
24 | #[test] | 21 | #[test] |
@@ -32,9 +29,7 @@ fn test_resolve_module() { | |||
32 | ", | 29 | ", |
33 | ); | 30 | ); |
34 | 31 | ||
35 | let symbols = analysis | 32 | let symbols = analysis.approximately_resolve_symbol(pos).unwrap(); |
36 | .approximately_resolve_symbol(pos.file_id, pos.offset) | ||
37 | .unwrap(); | ||
38 | assert_eq_dbg( | 33 | assert_eq_dbg( |
39 | r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#, | 34 | r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#, |
40 | &symbols, | 35 | &symbols, |
@@ -49,9 +44,7 @@ fn test_resolve_module() { | |||
49 | ", | 44 | ", |
50 | ); | 45 | ); |
51 | 46 | ||
52 | let symbols = analysis | 47 | let symbols = analysis.approximately_resolve_symbol(pos).unwrap(); |
53 | .approximately_resolve_symbol(pos.file_id, pos.offset) | ||
54 | .unwrap(); | ||
55 | assert_eq_dbg( | 48 | assert_eq_dbg( |
56 | r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#, | 49 | r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#, |
57 | &symbols, | 50 | &symbols, |
@@ -92,7 +85,7 @@ fn test_resolve_parent_module() { | |||
92 | <|>// empty | 85 | <|>// empty |
93 | ", | 86 | ", |
94 | ); | 87 | ); |
95 | let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap(); | 88 | let symbols = analysis.parent_module(pos).unwrap(); |
96 | assert_eq_dbg( | 89 | assert_eq_dbg( |
97 | r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#, | 90 | r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#, |
98 | &symbols, | 91 | &symbols, |
@@ -111,7 +104,7 @@ fn test_resolve_parent_module_for_inline() { | |||
111 | } | 104 | } |
112 | ", | 105 | ", |
113 | ); | 106 | ); |
114 | let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap(); | 107 | let symbols = analysis.parent_module(pos).unwrap(); |
115 | assert_eq_dbg( | 108 | assert_eq_dbg( |
116 | r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#, | 109 | r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#, |
117 | &symbols, | 110 | &symbols, |
@@ -397,9 +390,7 @@ By default this method stops actor's `Context`."# | |||
397 | 390 | ||
398 | fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> { | 391 | fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> { |
399 | let (analysis, position) = single_file_with_position(text); | 392 | let (analysis, position) = single_file_with_position(text); |
400 | analysis | 393 | analysis.find_all_refs(position).unwrap() |
401 | .find_all_refs(position.file_id, position.offset) | ||
402 | .unwrap() | ||
403 | } | 394 | } |
404 | 395 | ||
405 | #[test] | 396 | #[test] |
@@ -454,10 +445,7 @@ fn test_complete_crate_path() { | |||
454 | use crate::Sp<|> | 445 | use crate::Sp<|> |
455 | ", | 446 | ", |
456 | ); | 447 | ); |
457 | let completions = analysis | 448 | let completions = analysis.completions(position).unwrap().unwrap(); |
458 | .completions(position.file_id, position.offset) | ||
459 | .unwrap() | ||
460 | .unwrap(); | ||
461 | assert_eq_dbg( | 449 | assert_eq_dbg( |
462 | r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, | 450 | r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, |
463 | CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, | 451 | CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 84ffac2da..fa04f4b00 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -2,7 +2,7 @@ use languageserver_types::{ | |||
2 | Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, | 2 | Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, |
3 | TextDocumentItem, TextDocumentPositionParams, TextEdit, Url, VersionedTextDocumentIdentifier, | 3 | TextDocumentItem, TextDocumentPositionParams, TextEdit, Url, VersionedTextDocumentIdentifier, |
4 | }; | 4 | }; |
5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit}; | 5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition}; |
6 | use ra_editor::{AtomEdit, Edit, LineCol, LineIndex}; | 6 | use ra_editor::{AtomEdit, Edit, LineCol, LineIndex}; |
7 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 7 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
8 | 8 | ||
@@ -165,6 +165,17 @@ impl<'a> TryConvWith for &'a TextDocumentIdentifier { | |||
165 | } | 165 | } |
166 | } | 166 | } |
167 | 167 | ||
168 | impl<'a> TryConvWith for &'a TextDocumentPositionParams { | ||
169 | type Ctx = ServerWorld; | ||
170 | type Output = FilePosition; | ||
171 | fn try_conv_with(self, world: &ServerWorld) -> Result<FilePosition> { | ||
172 | let file_id = self.text_document.try_conv_with(world)?; | ||
173 | let line_index = world.analysis().file_line_index(file_id); | ||
174 | let offset = self.position.conv_with(&line_index); | ||
175 | Ok(FilePosition { file_id, offset }) | ||
176 | } | ||
177 | } | ||
178 | |||
168 | impl<T: TryConvWith> TryConvWith for Vec<T> { | 179 | impl<T: TryConvWith> TryConvWith for Vec<T> { |
169 | type Ctx = <T as TryConvWith>::Ctx; | 180 | type Ctx = <T as TryConvWith>::Ctx; |
170 | type Output = Vec<<T as TryConvWith>::Output>; | 181 | type Output = Vec<<T as TryConvWith>::Output>; |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 2219a0036..5314a333e 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -6,9 +6,9 @@ use languageserver_types::{ | |||
6 | DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, | 6 | DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, |
7 | FoldingRangeParams, InsertTextFormat, Location, MarkupContent, MarkupKind, Position, | 7 | FoldingRangeParams, InsertTextFormat, Location, MarkupContent, MarkupKind, Position, |
8 | PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, | 8 | PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, |
9 | WorkspaceEdit, | 9 | WorkspaceEdit, ParameterInformation, SignatureInformation, |
10 | }; | 10 | }; |
11 | use ra_analysis::{FileId, FoldKind, Query, RunnableKind}; | 11 | use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition}; |
12 | use ra_syntax::text_utils::contains_offset_nonstrict; | 12 | use ra_syntax::text_utils::contains_offset_nonstrict; |
13 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
14 | use serde_json::to_value; | 14 | use serde_json::to_value; |
@@ -83,10 +83,8 @@ pub fn handle_on_enter( | |||
83 | world: ServerWorld, | 83 | world: ServerWorld, |
84 | params: req::TextDocumentPositionParams, | 84 | params: req::TextDocumentPositionParams, |
85 | ) -> Result<Option<req::SourceChange>> { | 85 | ) -> Result<Option<req::SourceChange>> { |
86 | let file_id = params.text_document.try_conv_with(&world)?; | 86 | let position = params.try_conv_with(&world)?; |
87 | let line_index = world.analysis().file_line_index(file_id); | 87 | match world.analysis().on_enter(position) { |
88 | let offset = params.position.conv_with(&line_index); | ||
89 | match world.analysis().on_enter(file_id, offset) { | ||
90 | None => Ok(None), | 88 | None => Ok(None), |
91 | Some(edit) => Ok(Some(edit.try_conv_with(&world)?)), | 89 | Some(edit) => Ok(Some(edit.try_conv_with(&world)?)), |
92 | } | 90 | } |
@@ -102,8 +100,11 @@ pub fn handle_on_type_formatting( | |||
102 | 100 | ||
103 | let file_id = params.text_document.try_conv_with(&world)?; | 101 | let file_id = params.text_document.try_conv_with(&world)?; |
104 | let line_index = world.analysis().file_line_index(file_id); | 102 | let line_index = world.analysis().file_line_index(file_id); |
105 | let offset = params.position.conv_with(&line_index); | 103 | let position = FilePosition { |
106 | let edits = match world.analysis().on_eq_typed(file_id, offset) { | 104 | file_id, |
105 | offset: params.position.conv_with(&line_index), | ||
106 | }; | ||
107 | let edits = match world.analysis().on_eq_typed(position) { | ||
107 | None => return Ok(None), | 108 | None => return Ok(None), |
108 | Some(mut action) => action.source_file_edits.pop().unwrap().edits, | 109 | Some(mut action) => action.source_file_edits.pop().unwrap().edits, |
109 | }; | 110 | }; |
@@ -201,14 +202,9 @@ pub fn handle_goto_definition( | |||
201 | world: ServerWorld, | 202 | world: ServerWorld, |
202 | params: req::TextDocumentPositionParams, | 203 | params: req::TextDocumentPositionParams, |
203 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 204 | ) -> Result<Option<req::GotoDefinitionResponse>> { |
204 | let file_id = params.text_document.try_conv_with(&world)?; | 205 | let position = params.try_conv_with(&world)?; |
205 | let line_index = world.analysis().file_line_index(file_id); | ||
206 | let offset = params.position.conv_with(&line_index); | ||
207 | let mut res = Vec::new(); | 206 | let mut res = Vec::new(); |
208 | for (file_id, symbol) in world | 207 | for (file_id, symbol) in world.analysis().approximately_resolve_symbol(position)? { |
209 | .analysis() | ||
210 | .approximately_resolve_symbol(file_id, offset)? | ||
211 | { | ||
212 | let line_index = world.analysis().file_line_index(file_id); | 208 | let line_index = world.analysis().file_line_index(file_id); |
213 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; | 209 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; |
214 | res.push(location) | 210 | res.push(location) |
@@ -220,11 +216,9 @@ pub fn handle_parent_module( | |||
220 | world: ServerWorld, | 216 | world: ServerWorld, |
221 | params: req::TextDocumentPositionParams, | 217 | params: req::TextDocumentPositionParams, |
222 | ) -> Result<Vec<Location>> { | 218 | ) -> Result<Vec<Location>> { |
223 | let file_id = params.text_document.try_conv_with(&world)?; | 219 | let position = params.try_conv_with(&world)?; |
224 | let line_index = world.analysis().file_line_index(file_id); | ||
225 | let offset = params.position.conv_with(&line_index); | ||
226 | let mut res = Vec::new(); | 220 | let mut res = Vec::new(); |
227 | for (file_id, symbol) in world.analysis().parent_module(file_id, offset)? { | 221 | for (file_id, symbol) in world.analysis().parent_module(position)? { |
228 | let line_index = world.analysis().file_line_index(file_id); | 222 | let line_index = world.analysis().file_line_index(file_id); |
229 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; | 223 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; |
230 | res.push(location); | 224 | res.push(location); |
@@ -381,10 +375,13 @@ pub fn handle_completion( | |||
381 | world: ServerWorld, | 375 | world: ServerWorld, |
382 | params: req::CompletionParams, | 376 | params: req::CompletionParams, |
383 | ) -> Result<Option<req::CompletionResponse>> { | 377 | ) -> Result<Option<req::CompletionResponse>> { |
384 | let file_id = params.text_document.try_conv_with(&world)?; | 378 | let position = { |
385 | let line_index = world.analysis().file_line_index(file_id); | 379 | let file_id = params.text_document.try_conv_with(&world)?; |
386 | let offset = params.position.conv_with(&line_index); | 380 | let line_index = world.analysis().file_line_index(file_id); |
387 | let items = match world.analysis().completions(file_id, offset)? { | 381 | let offset = params.position.conv_with(&line_index); |
382 | FilePosition { file_id, offset } | ||
383 | }; | ||
384 | let items = match world.analysis().completions(position)? { | ||
388 | None => return Ok(None), | 385 | None => return Ok(None), |
389 | Some(items) => items, | 386 | Some(items) => items, |
390 | }; | 387 | }; |
@@ -444,13 +441,9 @@ pub fn handle_signature_help( | |||
444 | world: ServerWorld, | 441 | world: ServerWorld, |
445 | params: req::TextDocumentPositionParams, | 442 | params: req::TextDocumentPositionParams, |
446 | ) -> Result<Option<req::SignatureHelp>> { | 443 | ) -> Result<Option<req::SignatureHelp>> { |
447 | use languageserver_types::{ParameterInformation, SignatureInformation}; | 444 | let position = params.try_conv_with(&world)?; |
448 | 445 | ||
449 | let file_id = params.text_document.try_conv_with(&world)?; | 446 | if let Some((descriptor, active_param)) = world.analysis().resolve_callable(position)? { |
450 | let line_index = world.analysis().file_line_index(file_id); | ||
451 | let offset = params.position.conv_with(&line_index); | ||
452 | |||
453 | if let Some((descriptor, active_param)) = world.analysis().resolve_callable(file_id, offset)? { | ||
454 | let parameters: Vec<ParameterInformation> = descriptor | 447 | let parameters: Vec<ParameterInformation> = descriptor |
455 | .params | 448 | .params |
456 | .iter() | 449 | .iter() |
@@ -489,18 +482,17 @@ pub fn handle_prepare_rename( | |||
489 | world: ServerWorld, | 482 | world: ServerWorld, |
490 | params: req::TextDocumentPositionParams, | 483 | params: req::TextDocumentPositionParams, |
491 | ) -> Result<Option<PrepareRenameResponse>> { | 484 | ) -> Result<Option<PrepareRenameResponse>> { |
492 | let file_id = params.text_document.try_conv_with(&world)?; | 485 | let position = params.try_conv_with(&world)?; |
493 | let line_index = world.analysis().file_line_index(file_id); | ||
494 | let offset = params.position.conv_with(&line_index); | ||
495 | 486 | ||
496 | // We support renaming references like handle_rename does. | 487 | // We support renaming references like handle_rename does. |
497 | // In the future we may want to reject the renaming of things like keywords here too. | 488 | // In the future we may want to reject the renaming of things like keywords here too. |
498 | let refs = world.analysis().find_all_refs(file_id, offset)?; | 489 | let refs = world.analysis().find_all_refs(position)?; |
499 | if refs.is_empty() { | 490 | let r = match refs.first() { |
500 | return Ok(None); | 491 | Some(r) => r, |
501 | } | 492 | None => return Ok(None), |
502 | 493 | }; | |
503 | let r = refs.first().unwrap(); | 494 | let file_id = params.text_document.try_conv_with(&world)?; |
495 | let line_index = world.analysis().file_line_index(file_id); | ||
504 | let loc = to_location(r.0, r.1, &world, &line_index)?; | 496 | let loc = to_location(r.0, r.1, &world, &line_index)?; |
505 | 497 | ||
506 | Ok(Some(PrepareRenameResponse::Range(loc.range))) | 498 | Ok(Some(PrepareRenameResponse::Range(loc.range))) |
@@ -519,7 +511,9 @@ pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option< | |||
519 | .into()); | 511 | .into()); |
520 | } | 512 | } |
521 | 513 | ||
522 | let refs = world.analysis().find_all_refs(file_id, offset)?; | 514 | let refs = world |
515 | .analysis() | ||
516 | .find_all_refs(FilePosition { file_id, offset })?; | ||
523 | if refs.is_empty() { | 517 | if refs.is_empty() { |
524 | return Ok(None); | 518 | return Ok(None); |
525 | } | 519 | } |
@@ -550,7 +544,9 @@ pub fn handle_references( | |||
550 | let line_index = world.analysis().file_line_index(file_id); | 544 | let line_index = world.analysis().file_line_index(file_id); |
551 | let offset = params.position.conv_with(&line_index); | 545 | let offset = params.position.conv_with(&line_index); |
552 | 546 | ||
553 | let refs = world.analysis().find_all_refs(file_id, offset)?; | 547 | let refs = world |
548 | .analysis() | ||
549 | .find_all_refs(FilePosition { file_id, offset })?; | ||
554 | 550 | ||
555 | Ok(Some( | 551 | Ok(Some( |
556 | refs.into_iter() | 552 | refs.into_iter() |