aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs54
-rw-r--r--crates/ra_ide_api/src/hover.rs2
-rw-r--r--crates/ra_ide_api/src/imp.rs15
-rw-r--r--crates/ra_ide_api/src/lib.rs3
-rw-r--r--crates/ra_ide_api/src/navigation_target.rs35
-rw-r--r--crates/ra_ide_api/src/parent_module.rs52
-rw-r--r--crates/ra_ide_api/tests/test/main.rs40
-rw-r--r--crates/ra_lsp_server/src/conv.rs5
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)]
87mod tests { 87mod 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
16use crate::{ 16use 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
100impl db::RootDatabase { 100impl 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;
31mod hover; 31mod hover;
32mod call_info; 32mod call_info;
33mod syntax_highlighting; 33mod syntax_highlighting;
34mod parent_module;
34 35
35use std::{fmt, sync::Arc}; 36use 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 @@
1use ra_db::{Cancelable, FilePosition};
2
3use 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.
7pub(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)]
20mod 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;
4use test_utils::{assert_eq_dbg, assert_eq_text}; 4use test_utils::{assert_eq_dbg, assert_eq_text};
5 5
6use ra_ide_api::{ 6use 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]
38fn 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]
55fn 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]
74fn test_resolve_crate_root() { 38fn 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()