diff options
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 54 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide_api/src/imp.rs | 15 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide_api/src/navigation_target.rs | 35 | ||||
-rw-r--r-- | crates/ra_ide_api/src/parent_module.rs | 52 | ||||
-rw-r--r-- | crates/ra_ide_api/tests/test/main.rs | 40 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 5 |
8 files changed, 108 insertions, 98 deletions
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 3fb29950b..8d2ff561a 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -85,31 +85,32 @@ fn name_definition( | |||
85 | 85 | ||
86 | #[cfg(test)] | 86 | #[cfg(test)] |
87 | mod tests { | 87 | mod tests { |
88 | use test_utils::assert_eq_dbg; | ||
89 | use crate::mock_analysis::analysis_and_position; | 88 | use crate::mock_analysis::analysis_and_position; |
90 | 89 | ||
90 | fn check_goto(fixuture: &str, expected: &str) { | ||
91 | let (analysis, pos) = analysis_and_position(fixuture); | ||
92 | |||
93 | let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; | ||
94 | assert_eq!(navs.len(), 1); | ||
95 | let nav = navs.pop().unwrap(); | ||
96 | nav.assert_match(expected); | ||
97 | } | ||
98 | |||
91 | #[test] | 99 | #[test] |
92 | fn goto_definition_works_in_items() { | 100 | fn goto_definition_works_in_items() { |
93 | let (analysis, pos) = analysis_and_position( | 101 | check_goto( |
94 | " | 102 | " |
95 | //- /lib.rs | 103 | //- /lib.rs |
96 | struct Foo; | 104 | struct Foo; |
97 | enum E { X(Foo<|>) } | 105 | enum E { X(Foo<|>) } |
98 | ", | 106 | ", |
99 | ); | 107 | "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", |
100 | |||
101 | let symbols = analysis.goto_definition(pos).unwrap().unwrap(); | ||
102 | assert_eq_dbg( | ||
103 | r#"[NavigationTarget { file_id: FileId(1), name: "Foo", | ||
104 | kind: STRUCT_DEF, range: [0; 11), | ||
105 | ptr: Some(LocalSyntaxPtr { range: [0; 11), kind: STRUCT_DEF }) }]"#, | ||
106 | &symbols, | ||
107 | ); | 108 | ); |
108 | } | 109 | } |
109 | 110 | ||
110 | #[test] | 111 | #[test] |
111 | fn goto_definition_resolves_correct_name() { | 112 | fn goto_definition_resolves_correct_name() { |
112 | let (analysis, pos) = analysis_and_position( | 113 | check_goto( |
113 | " | 114 | " |
114 | //- /lib.rs | 115 | //- /lib.rs |
115 | use a::Foo; | 116 | use a::Foo; |
@@ -121,47 +122,30 @@ mod tests { | |||
121 | //- /b.rs | 122 | //- /b.rs |
122 | struct Foo; | 123 | struct Foo; |
123 | ", | 124 | ", |
124 | ); | 125 | "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)", |
125 | |||
126 | let symbols = analysis.goto_definition(pos).unwrap().unwrap(); | ||
127 | assert_eq_dbg( | ||
128 | r#"[NavigationTarget { file_id: FileId(2), name: "Foo", | ||
129 | kind: STRUCT_DEF, range: [0; 11), | ||
130 | ptr: Some(LocalSyntaxPtr { range: [0; 11), kind: STRUCT_DEF }) }]"#, | ||
131 | &symbols, | ||
132 | ); | 126 | ); |
133 | } | 127 | } |
134 | 128 | ||
135 | #[test] | 129 | #[test] |
136 | fn goto_definition_works_for_module_declaration() { | 130 | fn goto_definition_works_for_module_declaration() { |
137 | let (analysis, pos) = analysis_and_position( | 131 | check_goto( |
138 | " | 132 | " |
139 | //- /lib.rs | 133 | //- /lib.rs |
140 | mod <|>foo; | 134 | mod <|>foo; |
141 | //- /foo.rs | 135 | //- /foo.rs |
142 | // empty | 136 | // empty |
143 | ", | 137 | ", |
144 | ); | 138 | "foo SOURCE_FILE FileId(2) [0; 10)", |
145 | |||
146 | let symbols = analysis.goto_definition(pos).unwrap().unwrap(); | ||
147 | assert_eq_dbg( | ||
148 | r#"[NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]"#, | ||
149 | &symbols, | ||
150 | ); | 139 | ); |
151 | 140 | ||
152 | let (analysis, pos) = analysis_and_position( | 141 | check_goto( |
153 | " | 142 | " |
154 | //- /lib.rs | 143 | //- /lib.rs |
155 | mod <|>foo; | 144 | mod <|>foo; |
156 | //- /foo/mod.rs | 145 | //- /foo/mod.rs |
157 | // empty | 146 | // empty |
158 | ", | 147 | ", |
159 | ); | 148 | "foo SOURCE_FILE FileId(2) [0; 10)", |
160 | |||
161 | let symbols = analysis.goto_definition(pos).unwrap().unwrap(); | ||
162 | assert_eq_dbg( | ||
163 | r#"[NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]"#, | ||
164 | &symbols, | ||
165 | ); | 149 | ); |
166 | } | 150 | } |
167 | } | 151 | } |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 9b06a0e58..f544ffa6d 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -92,7 +92,7 @@ impl NavigationTarget { | |||
92 | let source_file = source_file.syntax(); | 92 | let source_file = source_file.syntax(); |
93 | let node = source_file | 93 | let node = source_file |
94 | .descendants() | 94 | .descendants() |
95 | .find(|node| node.kind() == self.kind() && node.range() == self.range())? | 95 | .find(|node| node.kind() == self.kind() && node.range() == self.full_range())? |
96 | .to_owned(); | 96 | .to_owned(); |
97 | Some(node) | 97 | Some(node) |
98 | } | 98 | } |
diff --git a/crates/ra_ide_api/src/imp.rs b/crates/ra_ide_api/src/imp.rs index 12bfe1761..ba4aa0fd5 100644 --- a/crates/ra_ide_api/src/imp.rs +++ b/crates/ra_ide_api/src/imp.rs | |||
@@ -15,7 +15,7 @@ use ra_syntax::{ | |||
15 | 15 | ||
16 | use crate::{ | 16 | use crate::{ |
17 | AnalysisChange, | 17 | AnalysisChange, |
18 | Cancelable, NavigationTarget, | 18 | Cancelable, |
19 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, | 19 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, |
20 | Query, RootChange, SourceChange, SourceFileEdit, | 20 | Query, RootChange, SourceChange, SourceFileEdit, |
21 | symbol_index::{LibrarySymbolsQuery, FileSymbol}, | 21 | symbol_index::{LibrarySymbolsQuery, FileSymbol}, |
@@ -98,19 +98,6 @@ impl db::RootDatabase { | |||
98 | } | 98 | } |
99 | 99 | ||
100 | impl db::RootDatabase { | 100 | impl db::RootDatabase { |
101 | /// This returns `Vec` because a module may be included from several places. We | ||
102 | /// don't handle this case yet though, so the Vec has length at most one. | ||
103 | pub(crate) fn parent_module( | ||
104 | &self, | ||
105 | position: FilePosition, | ||
106 | ) -> Cancelable<Vec<NavigationTarget>> { | ||
107 | let module = match source_binder::module_from_position(self, position)? { | ||
108 | None => return Ok(Vec::new()), | ||
109 | Some(it) => it, | ||
110 | }; | ||
111 | let nav = NavigationTarget::from_module(self, module)?; | ||
112 | Ok(vec![nav]) | ||
113 | } | ||
114 | /// Returns `Vec` for the same reason as `parent_module` | 101 | /// Returns `Vec` for the same reason as `parent_module` |
115 | pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 102 | pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
116 | let module = match source_binder::module_from_file_id(self, file_id)? { | 103 | let module = match source_binder::module_from_file_id(self, file_id)? { |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 2b02dab2a..6155d903a 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -31,6 +31,7 @@ mod extend_selection; | |||
31 | mod hover; | 31 | mod hover; |
32 | mod call_info; | 32 | mod call_info; |
33 | mod syntax_highlighting; | 33 | mod syntax_highlighting; |
34 | mod parent_module; | ||
34 | 35 | ||
35 | use std::{fmt, sync::Arc}; | 36 | use std::{fmt, sync::Arc}; |
36 | 37 | ||
@@ -414,7 +415,7 @@ impl Analysis { | |||
414 | 415 | ||
415 | /// Returns a `mod name;` declaration which created the current module. | 416 | /// Returns a `mod name;` declaration which created the current module. |
416 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { | 417 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { |
417 | self.with_db(|db| db.parent_module(position))? | 418 | self.with_db(|db| parent_module::parent_module(db, position))? |
418 | } | 419 | } |
419 | 420 | ||
420 | /// Returns crates this file belongs too. | 421 | /// Returns crates this file belongs too. |
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index 943e62eb8..8b29c3a97 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs | |||
@@ -17,7 +17,7 @@ pub struct NavigationTarget { | |||
17 | file_id: FileId, | 17 | file_id: FileId, |
18 | name: SmolStr, | 18 | name: SmolStr, |
19 | kind: SyntaxKind, | 19 | kind: SyntaxKind, |
20 | range: TextRange, | 20 | full_range: TextRange, |
21 | focus_range: Option<TextRange>, | 21 | focus_range: Option<TextRange>, |
22 | // Should be DefId ideally | 22 | // Should be DefId ideally |
23 | ptr: Option<LocalSyntaxPtr>, | 23 | ptr: Option<LocalSyntaxPtr>, |
@@ -36,11 +36,11 @@ impl NavigationTarget { | |||
36 | self.file_id | 36 | self.file_id |
37 | } | 37 | } |
38 | 38 | ||
39 | pub fn range(&self) -> TextRange { | 39 | pub fn full_range(&self) -> TextRange { |
40 | self.range | 40 | self.full_range |
41 | } | 41 | } |
42 | 42 | ||
43 | /// A "most interesting" range withing the `range`. | 43 | /// A "most interesting" range withing the `range_full`. |
44 | /// | 44 | /// |
45 | /// Typically, `range` is the whole syntax node, including doc comments, and | 45 | /// Typically, `range` is the whole syntax node, including doc comments, and |
46 | /// `focus_range` is the range of the identifier. | 46 | /// `focus_range` is the range of the identifier. |
@@ -53,7 +53,7 @@ impl NavigationTarget { | |||
53 | file_id: symbol.file_id, | 53 | file_id: symbol.file_id, |
54 | name: symbol.name.clone(), | 54 | name: symbol.name.clone(), |
55 | kind: symbol.ptr.kind(), | 55 | kind: symbol.ptr.kind(), |
56 | range: symbol.ptr.range(), | 56 | full_range: symbol.ptr.range(), |
57 | focus_range: None, | 57 | focus_range: None, |
58 | ptr: Some(symbol.ptr.clone()), | 58 | ptr: Some(symbol.ptr.clone()), |
59 | } | 59 | } |
@@ -66,7 +66,7 @@ impl NavigationTarget { | |||
66 | NavigationTarget { | 66 | NavigationTarget { |
67 | file_id, | 67 | file_id, |
68 | name: entry.name().to_string().into(), | 68 | name: entry.name().to_string().into(), |
69 | range: entry.ptr().range(), | 69 | full_range: entry.ptr().range(), |
70 | focus_range: None, | 70 | focus_range: None, |
71 | kind: NAME, | 71 | kind: NAME, |
72 | ptr: None, | 72 | ptr: None, |
@@ -118,6 +118,27 @@ impl NavigationTarget { | |||
118 | Ok(Some(res)) | 118 | Ok(Some(res)) |
119 | } | 119 | } |
120 | 120 | ||
121 | #[cfg(test)] | ||
122 | pub(crate) fn assert_match(&self, expected: &str) { | ||
123 | let actual = self.debug_render(); | ||
124 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | ||
125 | } | ||
126 | |||
127 | #[cfg(test)] | ||
128 | pub(crate) fn debug_render(&self) -> String { | ||
129 | let mut buf = format!( | ||
130 | "{} {:?} {:?} {:?}", | ||
131 | self.name(), | ||
132 | self.kind(), | ||
133 | self.file_id(), | ||
134 | self.full_range() | ||
135 | ); | ||
136 | if let Some(focus_range) = self.focus_range() { | ||
137 | buf.push_str(&format!(" {:?}", focus_range)) | ||
138 | } | ||
139 | buf | ||
140 | } | ||
141 | |||
121 | fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { | 142 | fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { |
122 | let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); | 143 | let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); |
123 | let focus_range = node.name().map(|it| it.syntax().range()); | 144 | let focus_range = node.name().map(|it| it.syntax().range()); |
@@ -134,7 +155,7 @@ impl NavigationTarget { | |||
134 | file_id, | 155 | file_id, |
135 | name, | 156 | name, |
136 | kind: node.kind(), | 157 | kind: node.kind(), |
137 | range: node.range(), | 158 | full_range: node.range(), |
138 | focus_range, | 159 | focus_range, |
139 | ptr: Some(LocalSyntaxPtr::new(node)), | 160 | ptr: Some(LocalSyntaxPtr::new(node)), |
140 | } | 161 | } |
diff --git a/crates/ra_ide_api/src/parent_module.rs b/crates/ra_ide_api/src/parent_module.rs new file mode 100644 index 000000000..d345839a3 --- /dev/null +++ b/crates/ra_ide_api/src/parent_module.rs | |||
@@ -0,0 +1,52 @@ | |||
1 | use ra_db::{Cancelable, FilePosition}; | ||
2 | |||
3 | use crate::{NavigationTarget, db::RootDatabase}; | ||
4 | |||
5 | /// This returns `Vec` because a module may be included from several places. We | ||
6 | /// don't handle this case yet though, so the Vec has length at most one. | ||
7 | pub(crate) fn parent_module( | ||
8 | db: &RootDatabase, | ||
9 | position: FilePosition, | ||
10 | ) -> Cancelable<Vec<NavigationTarget>> { | ||
11 | let module = match hir::source_binder::module_from_position(db, position)? { | ||
12 | None => return Ok(Vec::new()), | ||
13 | Some(it) => it, | ||
14 | }; | ||
15 | let nav = NavigationTarget::from_module(db, module)?; | ||
16 | Ok(vec![nav]) | ||
17 | } | ||
18 | |||
19 | #[cfg(test)] | ||
20 | mod tests { | ||
21 | use crate::mock_analysis::analysis_and_position; | ||
22 | |||
23 | #[test] | ||
24 | fn test_resolve_parent_module() { | ||
25 | let (analysis, pos) = analysis_and_position( | ||
26 | " | ||
27 | //- /lib.rs | ||
28 | mod foo; | ||
29 | //- /foo.rs | ||
30 | <|>// empty | ||
31 | ", | ||
32 | ); | ||
33 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | ||
34 | nav.assert_match("foo SOURCE_FILE FileId(2) [0; 10)"); | ||
35 | } | ||
36 | |||
37 | #[test] | ||
38 | fn test_resolve_parent_module_for_inline() { | ||
39 | let (analysis, pos) = analysis_and_position( | ||
40 | " | ||
41 | //- /lib.rs | ||
42 | mod foo { | ||
43 | mod bar { | ||
44 | mod baz { <|> } | ||
45 | } | ||
46 | } | ||
47 | ", | ||
48 | ); | ||
49 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | ||
50 | nav.assert_match("baz MODULE FileId(1) [32; 44)"); | ||
51 | } | ||
52 | } | ||
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs index d1dc07e5b..7dc1dba73 100644 --- a/crates/ra_ide_api/tests/test/main.rs +++ b/crates/ra_ide_api/tests/test/main.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::TextRange; | |||
4 | use test_utils::{assert_eq_dbg, assert_eq_text}; | 4 | use test_utils::{assert_eq_dbg, assert_eq_text}; |
5 | 5 | ||
6 | use ra_ide_api::{ | 6 | use ra_ide_api::{ |
7 | mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis}, | 7 | mock_analysis::{single_file, single_file_with_position, MockAnalysis}, |
8 | AnalysisChange, CrateGraph, FileId, Query | 8 | AnalysisChange, CrateGraph, FileId, Query |
9 | }; | 9 | }; |
10 | 10 | ||
@@ -35,42 +35,6 @@ fn test_unresolved_module_diagnostic_no_diag_for_inline_mode() { | |||
35 | } | 35 | } |
36 | 36 | ||
37 | #[test] | 37 | #[test] |
38 | fn test_resolve_parent_module() { | ||
39 | let (analysis, pos) = analysis_and_position( | ||
40 | " | ||
41 | //- /lib.rs | ||
42 | mod foo; | ||
43 | //- /foo.rs | ||
44 | <|>// empty | ||
45 | ", | ||
46 | ); | ||
47 | let symbols = analysis.parent_module(pos).unwrap(); | ||
48 | assert_eq_dbg( | ||
49 | r#"[NavigationTarget { file_id: FileId(1), name: "foo", kind: MODULE, range: [4; 7), ptr: None }]"#, | ||
50 | &symbols, | ||
51 | ); | ||
52 | } | ||
53 | |||
54 | #[test] | ||
55 | fn test_resolve_parent_module_for_inline() { | ||
56 | let (analysis, pos) = analysis_and_position( | ||
57 | " | ||
58 | //- /lib.rs | ||
59 | mod foo { | ||
60 | mod bar { | ||
61 | mod baz { <|> } | ||
62 | } | ||
63 | } | ||
64 | ", | ||
65 | ); | ||
66 | let symbols = analysis.parent_module(pos).unwrap(); | ||
67 | assert_eq_dbg( | ||
68 | r#"[NavigationTarget { file_id: FileId(1), name: "baz", kind: MODULE, range: [36; 39), ptr: None }]"#, | ||
69 | &symbols, | ||
70 | ); | ||
71 | } | ||
72 | |||
73 | #[test] | ||
74 | fn test_resolve_crate_root() { | 38 | fn test_resolve_crate_root() { |
75 | let mock = MockAnalysis::with_files( | 39 | let mock = MockAnalysis::with_files( |
76 | " | 40 | " |
@@ -245,5 +209,5 @@ pub trait HirDatabase: SyntaxDatabase {} | |||
245 | let mut symbols = analysis.symbol_search(Query::new("Hir".into())).unwrap(); | 209 | let mut symbols = analysis.symbol_search(Query::new("Hir".into())).unwrap(); |
246 | let s = symbols.pop().unwrap(); | 210 | let s = symbols.pop().unwrap(); |
247 | assert_eq!(s.name(), "HirDatabase"); | 211 | assert_eq!(s.name(), "HirDatabase"); |
248 | assert_eq!(s.range(), TextRange::from_to(33.into(), 44.into())); | 212 | assert_eq!(s.full_range(), TextRange::from_to(33.into(), 44.into())); |
249 | } | 213 | } |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index aad698da1..76fa98cbe 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -345,7 +345,8 @@ impl TryConvWith for &NavigationTarget { | |||
345 | type Output = Location; | 345 | type Output = Location; |
346 | fn try_conv_with(self, world: &ServerWorld) -> Result<Location> { | 346 | fn try_conv_with(self, world: &ServerWorld) -> Result<Location> { |
347 | let line_index = world.analysis().file_line_index(self.file_id()); | 347 | let line_index = world.analysis().file_line_index(self.file_id()); |
348 | to_location(self.file_id(), self.range(), &world, &line_index) | 348 | let range = self.focus_range().unwrap_or(self.full_range()); |
349 | to_location(self.file_id(), range, &world, &line_index) | ||
349 | } | 350 | } |
350 | } | 351 | } |
351 | 352 | ||
@@ -361,7 +362,7 @@ pub fn to_location_link( | |||
361 | let res = LocationLink { | 362 | let res = LocationLink { |
362 | origin_selection_range: Some(target.range.conv_with(line_index)), | 363 | origin_selection_range: Some(target.range.conv_with(line_index)), |
363 | target_uri: url.to_string(), | 364 | target_uri: url.to_string(), |
364 | target_range: target.info.range().conv_with(&tgt_line_index), | 365 | target_range: target.info.full_range().conv_with(&tgt_line_index), |
365 | target_selection_range: target | 366 | target_selection_range: target |
366 | .info | 367 | .info |
367 | .focus_range() | 368 | .focus_range() |