aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide_api/src/lib.rs4
-rw-r--r--crates/ra_ide_api/src/references.rs154
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs14
-rw-r--r--crates/ra_parser/src/grammar/items.rs2
-rw-r--r--crates/ra_parser/src/grammar/items/use_item.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt2
-rw-r--r--crates/ra_syntax/test_data/parser/err/0035_use_recover.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0035_use_recover.txt54
8 files changed, 157 insertions, 83 deletions
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 514dcaf96..e90fbd428 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -466,7 +466,7 @@ impl Analysis {
466 &self, 466 &self,
467 position: FilePosition, 467 position: FilePosition,
468 ) -> Cancelable<Option<ReferenceSearchResult>> { 468 ) -> Cancelable<Option<ReferenceSearchResult>> {
469 self.with_db(|db| references::find_all_refs(db, position)) 469 self.with_db(|db| references::find_all_refs(db, position).map(|it| it.info))
470 } 470 }
471 471
472 /// Returns a short text describing element at position. 472 /// Returns a short text describing element at position.
@@ -536,7 +536,7 @@ impl Analysis {
536 &self, 536 &self,
537 position: FilePosition, 537 position: FilePosition,
538 new_name: &str, 538 new_name: &str,
539 ) -> Cancelable<Option<SourceChange>> { 539 ) -> Cancelable<Option<RangeInfo<SourceChange>>> {
540 self.with_db(|db| references::rename(db, position, new_name)) 540 self.with_db(|db| references::rename(db, position, new_name))
541 } 541 }
542 542
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index d8a00067f..5f1f0efc3 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -4,7 +4,7 @@ use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode}
4use relative_path::{RelativePath, RelativePathBuf}; 4use relative_path::{RelativePath, RelativePathBuf};
5 5
6use crate::{ 6use crate::{
7 db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, 7 db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo,
8 SourceChange, SourceFileEdit, TextRange, 8 SourceChange, SourceFileEdit, TextRange,
9}; 9};
10 10
@@ -48,9 +48,9 @@ impl IntoIterator for ReferenceSearchResult {
48pub(crate) fn find_all_refs( 48pub(crate) fn find_all_refs(
49 db: &RootDatabase, 49 db: &RootDatabase,
50 position: FilePosition, 50 position: FilePosition,
51) -> Option<ReferenceSearchResult> { 51) -> Option<RangeInfo<ReferenceSearchResult>> {
52 let parse = db.parse(position.file_id); 52 let parse = db.parse(position.file_id);
53 let (binding, analyzer) = find_binding(db, &parse.tree(), position)?; 53 let RangeInfo { range, info: (binding, analyzer) } = find_binding(db, &parse.tree(), position)?;
54 let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding); 54 let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding);
55 55
56 let references = analyzer 56 let references = analyzer
@@ -59,24 +59,26 @@ pub(crate) fn find_all_refs(
59 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) 59 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
60 .collect::<Vec<_>>(); 60 .collect::<Vec<_>>();
61 61
62 return Some(ReferenceSearchResult { declaration, references }); 62 return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }));
63 63
64 fn find_binding<'a>( 64 fn find_binding<'a>(
65 db: &RootDatabase, 65 db: &RootDatabase,
66 source_file: &SourceFile, 66 source_file: &SourceFile,
67 position: FilePosition, 67 position: FilePosition,
68 ) -> Option<(ast::BindPat, hir::SourceAnalyzer)> { 68 ) -> Option<RangeInfo<(ast::BindPat, hir::SourceAnalyzer)>> {
69 let syntax = source_file.syntax(); 69 let syntax = source_file.syntax();
70 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { 70 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
71 let range = binding.syntax().text_range();
71 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); 72 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None);
72 return Some((binding, analyzer)); 73 return Some(RangeInfo::new(range, (binding, analyzer)));
73 }; 74 };
74 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; 75 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
76 let range = name_ref.syntax().text_range();
75 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); 77 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
76 let resolved = analyzer.resolve_local_name(&name_ref)?; 78 let resolved = analyzer.resolve_local_name(&name_ref)?;
77 if let Either::A(ptr) = resolved.ptr() { 79 if let Either::A(ptr) = resolved.ptr() {
78 if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) { 80 if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) {
79 return Some((binding, analyzer)); 81 return Some(RangeInfo::new(range, (binding, analyzer)));
80 } 82 }
81 } 83 }
82 None 84 None
@@ -87,12 +89,14 @@ pub(crate) fn rename(
87 db: &RootDatabase, 89 db: &RootDatabase,
88 position: FilePosition, 90 position: FilePosition,
89 new_name: &str, 91 new_name: &str,
90) -> Option<SourceChange> { 92) -> Option<RangeInfo<SourceChange>> {
91 let parse = db.parse(position.file_id); 93 let parse = db.parse(position.file_id);
92 if let Some((ast_name, ast_module)) = 94 if let Some((ast_name, ast_module)) =
93 find_name_and_module_at_offset(parse.tree().syntax(), position) 95 find_name_and_module_at_offset(parse.tree().syntax(), position)
94 { 96 {
97 let range = ast_name.syntax().text_range();
95 rename_mod(db, &ast_name, &ast_module, position, new_name) 98 rename_mod(db, &ast_name, &ast_module, position, new_name)
99 .map(|info| RangeInfo::new(range, info))
96 } else { 100 } else {
97 rename_reference(db, position, new_name) 101 rename_reference(db, position, new_name)
98 } 102 }
@@ -107,7 +111,7 @@ fn find_name_and_module_at_offset(
107 Some((ast_name, ast_module)) 111 Some((ast_name, ast_module))
108} 112}
109 113
110fn source_edit_from_fileid_range( 114fn source_edit_from_file_id_range(
111 file_id: FileId, 115 file_id: FileId,
112 range: TextRange, 116 range: TextRange,
113 new_name: &str, 117 new_name: &str,
@@ -179,19 +183,19 @@ fn rename_reference(
179 db: &RootDatabase, 183 db: &RootDatabase,
180 position: FilePosition, 184 position: FilePosition,
181 new_name: &str, 185 new_name: &str,
182) -> Option<SourceChange> { 186) -> Option<RangeInfo<SourceChange>> {
183 let refs = find_all_refs(db, position)?; 187 let RangeInfo { range, info: refs } = find_all_refs(db, position)?;
184 188
185 let edit = refs 189 let edit = refs
186 .into_iter() 190 .into_iter()
187 .map(|range| source_edit_from_fileid_range(range.file_id, range.range, new_name)) 191 .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name))
188 .collect::<Vec<_>>(); 192 .collect::<Vec<_>>();
189 193
190 if edit.is_empty() { 194 if edit.is_empty() {
191 return None; 195 return None;
192 } 196 }
193 197
194 Some(SourceChange::source_file_edits("rename", edit)) 198 Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit)))
195} 199}
196 200
197#[cfg(test)] 201#[cfg(test)]
@@ -342,38 +346,43 @@ mod tests {
342 let new_name = "foo2"; 346 let new_name = "foo2";
343 let source_change = analysis.rename(position, new_name).unwrap(); 347 let source_change = analysis.rename(position, new_name).unwrap();
344 assert_debug_snapshot!(&source_change, 348 assert_debug_snapshot!(&source_change,
345@r#"Some( 349@r###"
346 SourceChange { 350 Some(
347 label: "rename", 351 RangeInfo {
348 source_file_edits: [ 352 range: [4; 7),
349 SourceFileEdit { 353 info: SourceChange {
350 file_id: FileId( 354 label: "rename",
351 2, 355 source_file_edits: [
352 ), 356 SourceFileEdit {
353 edit: TextEdit { 357 file_id: FileId(
354 atoms: [ 358 2,
355 AtomTextEdit { 359 ),
356 delete: [4; 7), 360 edit: TextEdit {
357 insert: "foo2", 361 atoms: [
362 AtomTextEdit {
363 delete: [4; 7),
364 insert: "foo2",
365 },
366 ],
367 },
368 },
369 ],
370 file_system_edits: [
371 MoveFile {
372 src: FileId(
373 3,
374 ),
375 dst_source_root: SourceRootId(
376 0,
377 ),
378 dst_path: "bar/foo2.rs",
358 }, 379 },
359 ], 380 ],
381 cursor_position: None,
360 }, 382 },
361 }, 383 },
362 ], 384 )
363 file_system_edits: [ 385 "###);
364 MoveFile {
365 src: FileId(
366 3,
367 ),
368 dst_source_root: SourceRootId(
369 0,
370 ),
371 dst_path: "bar/foo2.rs",
372 },
373 ],
374 cursor_position: None,
375 },
376)"#);
377 } 386 }
378 387
379 #[test] 388 #[test]
@@ -389,38 +398,43 @@ mod tests {
389 let new_name = "foo2"; 398 let new_name = "foo2";
390 let source_change = analysis.rename(position, new_name).unwrap(); 399 let source_change = analysis.rename(position, new_name).unwrap();
391 assert_debug_snapshot!(&source_change, 400 assert_debug_snapshot!(&source_change,
392 @r###"Some( 401 @r###"
393 SourceChange { 402 Some(
394 label: "rename", 403 RangeInfo {
395 source_file_edits: [ 404 range: [4; 7),
396 SourceFileEdit { 405 info: SourceChange {
397 file_id: FileId( 406 label: "rename",
398 1, 407 source_file_edits: [
399 ), 408 SourceFileEdit {
400 edit: TextEdit { 409 file_id: FileId(
401 atoms: [ 410 1,
402 AtomTextEdit { 411 ),
403 delete: [4; 7), 412 edit: TextEdit {
404 insert: "foo2", 413 atoms: [
414 AtomTextEdit {
415 delete: [4; 7),
416 insert: "foo2",
417 },
418 ],
419 },
420 },
421 ],
422 file_system_edits: [
423 MoveFile {
424 src: FileId(
425 2,
426 ),
427 dst_source_root: SourceRootId(
428 0,
429 ),
430 dst_path: "foo2/mod.rs",
405 }, 431 },
406 ], 432 ],
433 cursor_position: None,
407 }, 434 },
408 }, 435 },
409 ], 436 )
410 file_system_edits: [ 437 "###
411 MoveFile {
412 src: FileId(
413 2,
414 ),
415 dst_source_root: SourceRootId(
416 0,
417 ),
418 dst_path: "foo2/mod.rs",
419 },
420 ],
421 cursor_position: None,
422 },
423)"###
424 ); 438 );
425 } 439 }
426 440
@@ -430,7 +444,7 @@ mod tests {
430 let mut text_edit_builder = ra_text_edit::TextEditBuilder::default(); 444 let mut text_edit_builder = ra_text_edit::TextEditBuilder::default();
431 let mut file_id: Option<FileId> = None; 445 let mut file_id: Option<FileId> = None;
432 if let Some(change) = source_change { 446 if let Some(change) = source_change {
433 for edit in change.source_file_edits { 447 for edit in change.info.source_file_edits {
434 file_id = Some(edit.file_id); 448 file_id = Some(edit.file_id);
435 for atom in edit.edit.as_atoms() { 449 for atom in edit.edit.as_atoms() {
436 text_edit_builder.replace(atom.delete, atom.insert.clone()); 450 text_edit_builder.replace(atom.delete, atom.insert.clone());
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index eb805a6d3..948d543ea 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -460,18 +460,16 @@ pub fn handle_prepare_rename(
460 460
461 // We support renaming references like handle_rename does. 461 // We support renaming references like handle_rename does.
462 // In the future we may want to reject the renaming of things like keywords here too. 462 // In the future we may want to reject the renaming of things like keywords here too.
463 let refs = match world.analysis().find_all_refs(position)? { 463 let optional_change = world.analysis().rename(position, "dummy")?;
464 let range = match optional_change {
464 None => return Ok(None), 465 None => return Ok(None),
465 Some(refs) => refs, 466 Some(it) => it.range,
466 }; 467 };
467 468
468 // Refs should always have a declaration
469 let r = refs.declaration();
470 let file_id = params.text_document.try_conv_with(&world)?; 469 let file_id = params.text_document.try_conv_with(&world)?;
471 let line_index = world.analysis().file_line_index(file_id)?; 470 let line_index = world.analysis().file_line_index(file_id)?;
472 let loc = to_location(r.file_id(), r.range(), &world, &line_index)?; 471 let range = range.conv_with(&line_index);
473 472 Ok(Some(PrepareRenameResponse::Range(range)))
474 Ok(Some(PrepareRenameResponse::Range(loc.range)))
475} 473}
476 474
477pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 475pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
@@ -488,7 +486,7 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
488 let optional_change = world.analysis().rename(position, &*params.new_name)?; 486 let optional_change = world.analysis().rename(position, &*params.new_name)?;
489 let change = match optional_change { 487 let change = match optional_change {
490 None => return Ok(None), 488 None => return Ok(None),
491 Some(it) => it, 489 Some(it) => it.info,
492 }; 490 };
493 491
494 let source_change_req = change.try_conv_with(&world)?; 492 let source_change_req = change.try_conv_with(&world)?;
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs
index 6d426206e..b4327b78f 100644
--- a/crates/ra_parser/src/grammar/items.rs
+++ b/crates/ra_parser/src/grammar/items.rs
@@ -31,7 +31,7 @@ pub(super) enum ItemFlavor {
31 31
32pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![ 32pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![
33 FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW, 33 FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW,
34 CRATE_KW 34 CRATE_KW, USE_KW
35]; 35];
36 36
37pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemFlavor) { 37pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemFlavor) {
diff --git a/crates/ra_parser/src/grammar/items/use_item.rs b/crates/ra_parser/src/grammar/items/use_item.rs
index c0c7d0ec6..83a65e226 100644
--- a/crates/ra_parser/src/grammar/items/use_item.rs
+++ b/crates/ra_parser/src/grammar/items/use_item.rs
@@ -101,7 +101,10 @@ fn use_tree(p: &mut Parser) {
101 } 101 }
102 _ => { 102 _ => {
103 m.abandon(p); 103 m.abandon(p);
104 p.err_and_bump("expected one of `*`, `::`, `{`, `self`, `super` or an indentifier"); 104 p.err_recover(
105 "expected one of `*`, `::`, `{`, `self`, `super` or an identifier",
106 ITEM_RECOVERY_SET,
107 );
105 return; 108 return;
106 } 109 }
107 } 110 }
diff --git a/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt b/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
index 84867026f..bdb5fa6c5 100644
--- a/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
+++ b/crates/ra_syntax/test_data/parser/err/0002_duplicate_shebang.txt
@@ -28,7 +28,7 @@ SOURCE_FILE@[0; 42)
28 WHITESPACE@[41; 42) "\n" 28 WHITESPACE@[41; 42) "\n"
29error 23: expected `[` 29error 23: expected `[`
30error 23: expected an item 30error 23: expected an item
31error 27: expected one of `*`, `::`, `{`, `self`, `super` or an indentifier 31error 27: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
32error 28: expected SEMI 32error 28: expected SEMI
33error 31: expected EXCL 33error 31: expected EXCL
34error 31: expected `{`, `[`, `(` 34error 31: expected `{`, `[`, `(`
diff --git a/crates/ra_syntax/test_data/parser/err/0035_use_recover.rs b/crates/ra_syntax/test_data/parser/err/0035_use_recover.rs
new file mode 100644
index 000000000..4a2668126
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0035_use_recover.rs
@@ -0,0 +1,5 @@
1use foo::bar;
2use
3use crate::baz;
4use
5fn f() {}
diff --git a/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt b/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt
new file mode 100644
index 000000000..636840828
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0035_use_recover.txt
@@ -0,0 +1,54 @@
1SOURCE_FILE@[0; 48)
2 USE_ITEM@[0; 13)
3 USE_KW@[0; 3) "use"
4 WHITESPACE@[3; 4) " "
5 USE_TREE@[4; 12)
6 PATH@[4; 12)
7 PATH@[4; 7)
8 PATH_SEGMENT@[4; 7)
9 NAME_REF@[4; 7)
10 IDENT@[4; 7) "foo"
11 COLONCOLON@[7; 9) "::"
12 PATH_SEGMENT@[9; 12)
13 NAME_REF@[9; 12)
14 IDENT@[9; 12) "bar"
15 SEMI@[12; 13) ";"
16 WHITESPACE@[13; 14) "\n"
17 USE_ITEM@[14; 17)
18 USE_KW@[14; 17) "use"
19 WHITESPACE@[17; 18) "\n"
20 USE_ITEM@[18; 33)
21 USE_KW@[18; 21) "use"
22 WHITESPACE@[21; 22) " "
23 USE_TREE@[22; 32)
24 PATH@[22; 32)
25 PATH@[22; 27)
26 PATH_SEGMENT@[22; 27)
27 CRATE_KW@[22; 27) "crate"
28 COLONCOLON@[27; 29) "::"
29 PATH_SEGMENT@[29; 32)
30 NAME_REF@[29; 32)
31 IDENT@[29; 32) "baz"
32 SEMI@[32; 33) ";"
33 WHITESPACE@[33; 34) "\n"
34 USE_ITEM@[34; 37)
35 USE_KW@[34; 37) "use"
36 WHITESPACE@[37; 38) "\n"
37 FN_DEF@[38; 47)
38 FN_KW@[38; 40) "fn"
39 WHITESPACE@[40; 41) " "
40 NAME@[41; 42)
41 IDENT@[41; 42) "f"
42 PARAM_LIST@[42; 44)
43 L_PAREN@[42; 43) "("
44 R_PAREN@[43; 44) ")"
45 WHITESPACE@[44; 45) " "
46 BLOCK_EXPR@[45; 47)
47 BLOCK@[45; 47)
48 L_CURLY@[45; 46) "{"
49 R_CURLY@[46; 47) "}"
50 WHITESPACE@[47; 48) "\n"
51error 17: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
52error 17: expected SEMI
53error 37: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
54error 37: expected SEMI