diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-09 18:55:44 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-09 18:55:44 +0100 |
commit | 2fc2d4373b2c4e96bebf320a84270eee3afe34aa (patch) | |
tree | 5bdb33ae377b4004f0b28ec5e2edff71b41e8c4e /crates/ra_ide_api/src/hover.rs | |
parent | 5f700179fc7ed16d2848a6dbc7cf23da3b8df6c7 (diff) | |
parent | 45a2b9252401cc580dfa2e0e761313cc8334d47c (diff) |
Merge #1110
1110: Introduce display module and implement new FunctionSignature for CallInfo's r=matklad a=vipentti
This introduces a new module `display` in `ra_ide_api` that contains UI-related things, in addition this refactors CallInfo's function signatures into a new `FunctionSignature` type, which implements `Display` and can be converted into `lsp_types::SignatureInformation` in the `conv` layer.
Currently only `CallInfo` uses the `FunctionSignature` directly, but `function_label` now uses the same signature and returns it as a string, using the `Display` implementation.
This also fixes #960
I think this similar structure could be applied to other UI-displayable items, so instead of the `ra_ide_api` returning `Strings` we could return some intermediate structures that can be converted into a UI-displayable `String` easily, but that could also provide some additional information.
Co-authored-by: Ville Penttinen <[email protected]>
Diffstat (limited to 'crates/ra_ide_api/src/hover.rs')
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 110 |
1 files changed, 3 insertions, 107 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index bfa7cd67a..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(crate::completion::function_label) | ||
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; |