diff options
-rw-r--r-- | crates/ra_ide_api/src/display.rs | 27 | ||||
-rw-r--r-- | crates/ra_ide_api/src/display/navigation_target.rs | 82 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 110 |
3 files changed, 110 insertions, 109 deletions
diff --git a/crates/ra_ide_api/src/display.rs b/crates/ra_ide_api/src/display.rs index f1717b008..b68b3a719 100644 --- a/crates/ra_ide_api/src/display.rs +++ b/crates/ra_ide_api/src/display.rs | |||
@@ -5,6 +5,7 @@ mod function_signature; | |||
5 | mod navigation_target; | 5 | mod navigation_target; |
6 | mod structure; | 6 | mod structure; |
7 | 7 | ||
8 | use crate::db::RootDatabase; | ||
8 | use ra_syntax::{ast::{self, AstNode, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}}; | 9 | use ra_syntax::{ast::{self, AstNode, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}}; |
9 | 10 | ||
10 | pub use navigation_target::NavigationTarget; | 11 | pub use navigation_target::NavigationTarget; |
@@ -53,3 +54,29 @@ pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> { | |||
53 | } | 54 | } |
54 | res | 55 | res |
55 | } | 56 | } |
57 | |||
58 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | ||
59 | rust_code_markup_with_doc::<_, &str>(val, None) | ||
60 | } | ||
61 | |||
62 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | ||
63 | where | ||
64 | CODE: AsRef<str>, | ||
65 | DOC: AsRef<str>, | ||
66 | { | ||
67 | if let Some(doc) = doc { | ||
68 | format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref()) | ||
69 | } else { | ||
70 | format!("```rust\n{}\n```", val.as_ref()) | ||
71 | } | ||
72 | } | ||
73 | |||
74 | // FIXME: this should not really use navigation target. Rather, approximately | ||
75 | // resolved symbol should return a `DefId`. | ||
76 | pub(crate) fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> { | ||
77 | match (nav.description(db), nav.docs(db)) { | ||
78 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), | ||
79 | (None, Some(docs)) => Some(docs), | ||
80 | _ => None, | ||
81 | } | ||
82 | } | ||
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index f6d7f3192..3c518faf5 100644 --- a/crates/ra_ide_api/src/display/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs | |||
@@ -1,7 +1,9 @@ | |||
1 | use ra_db::FileId; | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast, | 3 | SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, TreeArc, |
4 | SyntaxKind::{self, NAME}, | 4 | SyntaxKind::{self, NAME}, |
5 | ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, | ||
6 | algo::visit::{visitor, Visitor}, | ||
5 | }; | 7 | }; |
6 | use hir::{ModuleSource, FieldSource, Name, ImplItem}; | 8 | use hir::{ModuleSource, FieldSource, Name, ImplItem}; |
7 | 9 | ||
@@ -248,4 +250,80 @@ impl NavigationTarget { | |||
248 | container_name: None, | 250 | container_name: None, |
249 | } | 251 | } |
250 | } | 252 | } |
253 | |||
254 | pub(crate) fn node(&self, db: &RootDatabase) -> Option<TreeArc<SyntaxNode>> { | ||
255 | let source_file = db.parse(self.file_id()); | ||
256 | let source_file = source_file.syntax(); | ||
257 | let node = source_file | ||
258 | .descendants() | ||
259 | .find(|node| node.kind() == self.kind() && node.range() == self.full_range())? | ||
260 | .to_owned(); | ||
261 | Some(node) | ||
262 | } | ||
263 | |||
264 | pub(crate) fn docs(&self, db: &RootDatabase) -> Option<String> { | ||
265 | let node = self.node(db)?; | ||
266 | fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> { | ||
267 | node.doc_comment_text() | ||
268 | } | ||
269 | |||
270 | visitor() | ||
271 | .visit(doc_comments::<ast::FnDef>) | ||
272 | .visit(doc_comments::<ast::StructDef>) | ||
273 | .visit(doc_comments::<ast::EnumDef>) | ||
274 | .visit(doc_comments::<ast::TraitDef>) | ||
275 | .visit(doc_comments::<ast::Module>) | ||
276 | .visit(doc_comments::<ast::TypeAliasDef>) | ||
277 | .visit(doc_comments::<ast::ConstDef>) | ||
278 | .visit(doc_comments::<ast::StaticDef>) | ||
279 | .visit(doc_comments::<ast::NamedFieldDef>) | ||
280 | .visit(doc_comments::<ast::EnumVariant>) | ||
281 | .accept(&node)? | ||
282 | } | ||
283 | |||
284 | /// Get a description of this node. | ||
285 | /// | ||
286 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
287 | pub(crate) fn description(&self, db: &RootDatabase) -> Option<String> { | ||
288 | // FIXME: After type inference is done, add type information to improve the output | ||
289 | let node = self.node(db)?; | ||
290 | |||
291 | fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String> | ||
292 | where | ||
293 | T: NameOwner + VisibilityOwner + TypeAscriptionOwner, | ||
294 | { | ||
295 | let mut string = visit_node(node, prefix)?; | ||
296 | |||
297 | if let Some(type_ref) = node.ascribed_type() { | ||
298 | string.push_str(": "); | ||
299 | type_ref.syntax().text().push_to(&mut string); | ||
300 | } | ||
301 | |||
302 | Some(string) | ||
303 | } | ||
304 | |||
305 | fn visit_node<T>(node: &T, label: &str) -> Option<String> | ||
306 | where | ||
307 | T: NameOwner + VisibilityOwner, | ||
308 | { | ||
309 | let mut string = | ||
310 | node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default(); | ||
311 | string.push_str(label); | ||
312 | string.push_str(node.name()?.text().as_str()); | ||
313 | Some(string) | ||
314 | } | ||
315 | |||
316 | visitor() | ||
317 | .visit(|node: &ast::FnDef| Some(crate::display::function_label(node))) | ||
318 | .visit(|node: &ast::StructDef| visit_node(node, "struct ")) | ||
319 | .visit(|node: &ast::EnumDef| visit_node(node, "enum ")) | ||
320 | .visit(|node: &ast::TraitDef| visit_node(node, "trait ")) | ||
321 | .visit(|node: &ast::Module| visit_node(node, "mod ")) | ||
322 | .visit(|node: &ast::TypeAliasDef| visit_node(node, "type ")) | ||
323 | .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const ")) | ||
324 | .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static ")) | ||
325 | .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, "")) | ||
326 | .visit(|node: &ast::EnumVariant| Some(node.name()?.text().to_string())) | ||
327 | .accept(&node)? | ||
328 | } | ||
251 | } | 329 | } |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 7d2c57f82..3a8c93b99 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | use ra_db::SourceDatabase; | 1 | use ra_db::SourceDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, | 3 | AstNode, ast, |
4 | algo::{find_covering_element, find_node_at_offset, find_token_at_offset, visit::{visitor, Visitor}}, | 4 | algo::{find_covering_element, find_node_at_offset, find_token_at_offset}, |
5 | }; | 5 | }; |
6 | use hir::HirDisplay; | 6 | use hir::HirDisplay; |
7 | 7 | ||
8 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, NavigationTarget}; | 8 | use crate::{db::RootDatabase, RangeInfo, FilePosition, FileRange, display::{rust_code_markup, doc_text_for}}; |
9 | 9 | ||
10 | /// Contains the results when hovering over an item | 10 | /// Contains the results when hovering over an item |
11 | #[derive(Debug, Clone)] | 11 | #[derive(Debug, Clone)] |
@@ -145,110 +145,6 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | |||
145 | } | 145 | } |
146 | } | 146 | } |
147 | 147 | ||
148 | fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | ||
149 | rust_code_markup_with_doc::<_, &str>(val, None) | ||
150 | } | ||
151 | |||
152 | fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | ||
153 | where | ||
154 | CODE: AsRef<str>, | ||
155 | DOC: AsRef<str>, | ||
156 | { | ||
157 | if let Some(doc) = doc { | ||
158 | format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref()) | ||
159 | } else { | ||
160 | format!("```rust\n{}\n```", val.as_ref()) | ||
161 | } | ||
162 | } | ||
163 | |||
164 | // FIXME: this should not really use navigation target. Rather, approximately | ||
165 | // resolved symbol should return a `DefId`. | ||
166 | fn doc_text_for(db: &RootDatabase, nav: NavigationTarget) -> Option<String> { | ||
167 | match (nav.description(db), nav.docs(db)) { | ||
168 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), | ||
169 | (None, Some(docs)) => Some(docs), | ||
170 | _ => None, | ||
171 | } | ||
172 | } | ||
173 | |||
174 | impl NavigationTarget { | ||
175 | fn node(&self, db: &RootDatabase) -> Option<TreeArc<SyntaxNode>> { | ||
176 | let source_file = db.parse(self.file_id()); | ||
177 | let source_file = source_file.syntax(); | ||
178 | let node = source_file | ||
179 | .descendants() | ||
180 | .find(|node| node.kind() == self.kind() && node.range() == self.full_range())? | ||
181 | .to_owned(); | ||
182 | Some(node) | ||
183 | } | ||
184 | |||
185 | fn docs(&self, db: &RootDatabase) -> Option<String> { | ||
186 | let node = self.node(db)?; | ||
187 | fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> { | ||
188 | node.doc_comment_text() | ||
189 | } | ||
190 | |||
191 | visitor() | ||
192 | .visit(doc_comments::<ast::FnDef>) | ||
193 | .visit(doc_comments::<ast::StructDef>) | ||
194 | .visit(doc_comments::<ast::EnumDef>) | ||
195 | .visit(doc_comments::<ast::TraitDef>) | ||
196 | .visit(doc_comments::<ast::Module>) | ||
197 | .visit(doc_comments::<ast::TypeAliasDef>) | ||
198 | .visit(doc_comments::<ast::ConstDef>) | ||
199 | .visit(doc_comments::<ast::StaticDef>) | ||
200 | .visit(doc_comments::<ast::NamedFieldDef>) | ||
201 | .visit(doc_comments::<ast::EnumVariant>) | ||
202 | .accept(&node)? | ||
203 | } | ||
204 | |||
205 | /// Get a description of this node. | ||
206 | /// | ||
207 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
208 | fn description(&self, db: &RootDatabase) -> Option<String> { | ||
209 | // FIXME: After type inference is done, add type information to improve the output | ||
210 | let node = self.node(db)?; | ||
211 | |||
212 | fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String> | ||
213 | where | ||
214 | T: NameOwner + VisibilityOwner + TypeAscriptionOwner, | ||
215 | { | ||
216 | let mut string = visit_node(node, prefix)?; | ||
217 | |||
218 | if let Some(type_ref) = node.ascribed_type() { | ||
219 | string.push_str(": "); | ||
220 | type_ref.syntax().text().push_to(&mut string); | ||
221 | } | ||
222 | |||
223 | Some(string) | ||
224 | } | ||
225 | |||
226 | fn visit_node<T>(node: &T, label: &str) -> Option<String> | ||
227 | where | ||
228 | T: NameOwner + VisibilityOwner, | ||
229 | { | ||
230 | let mut string = | ||
231 | node.visibility().map(|v| format!("{} ", v.syntax().text())).unwrap_or_default(); | ||
232 | string.push_str(label); | ||
233 | string.push_str(node.name()?.text().as_str()); | ||
234 | Some(string) | ||
235 | } | ||
236 | |||
237 | visitor() | ||
238 | .visit(|node: &ast::FnDef| Some(crate::display::function_label(node))) | ||
239 | .visit(|node: &ast::StructDef| visit_node(node, "struct ")) | ||
240 | .visit(|node: &ast::EnumDef| visit_node(node, "enum ")) | ||
241 | .visit(|node: &ast::TraitDef| visit_node(node, "trait ")) | ||
242 | .visit(|node: &ast::Module| visit_node(node, "mod ")) | ||
243 | .visit(|node: &ast::TypeAliasDef| visit_node(node, "type ")) | ||
244 | .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const ")) | ||
245 | .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static ")) | ||
246 | .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, "")) | ||
247 | .visit(|node: &ast::EnumVariant| Some(node.name()?.text().to_string())) | ||
248 | .accept(&node)? | ||
249 | } | ||
250 | } | ||
251 | |||
252 | #[cfg(test)] | 148 | #[cfg(test)] |
253 | mod tests { | 149 | mod tests { |
254 | use ra_syntax::TextRange; | 150 | use ra_syntax::TextRange; |