diff options
Diffstat (limited to 'crates/ra_ide_api/src/hover.rs')
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 180 |
1 files changed, 162 insertions, 18 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index cb676eb12..fbabeb194 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -1,14 +1,15 @@ | |||
1 | use ra_db::SourceDatabase; | 1 | use ra_db::SourceDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AstNode, ast, | 3 | AstNode, ast::{self, DocCommentsOwner}, |
4 | algo::{find_covering_element, find_node_at_offset, ancestors_at_offset}, | 4 | algo::{find_covering_element, find_node_at_offset, ancestors_at_offset, visit::{visitor, Visitor}}, |
5 | }; | 5 | }; |
6 | use hir::HirDisplay; | 6 | use hir::HirDisplay; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | db::RootDatabase, | 9 | db::RootDatabase, |
10 | RangeInfo, FilePosition, FileRange, | 10 | RangeInfo, FilePosition, FileRange, |
11 | display::{rust_code_markup, doc_text_for}, | 11 | display::{rust_code_markup, rust_code_markup_with_doc, ShortLabel, docs_from_symbol, description_from_symbol}, |
12 | name_ref_kind::{NameRefKind::*, classify_name_ref}, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | /// Contains the results when hovering over an item | 15 | /// Contains the results when hovering over an item |
@@ -77,34 +78,177 @@ impl HoverResult { | |||
77 | } | 78 | } |
78 | } | 79 | } |
79 | 80 | ||
81 | fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> { | ||
82 | match (desc, docs) { | ||
83 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), | ||
84 | (None, Some(docs)) => Some(docs.to_string()), | ||
85 | _ => None, | ||
86 | } | ||
87 | } | ||
88 | |||
80 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | 89 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { |
81 | let file = db.parse(position.file_id).tree; | 90 | let file = db.parse(position.file_id).tree; |
82 | let mut res = HoverResult::new(); | 91 | let mut res = HoverResult::new(); |
83 | 92 | ||
84 | let mut range = None; | 93 | let mut range = None; |
85 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { | 94 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { |
86 | use crate::goto_definition::{ReferenceResult::*, reference_definition}; | 95 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); |
87 | let ref_result = reference_definition(db, position.file_id, name_ref); | 96 | |
88 | match ref_result { | 97 | match classify_name_ref(db, &analyzer, name_ref) { |
89 | Exact(nav) => res.extend(doc_text_for(nav)), | 98 | Some(Method(it)) => { |
90 | Approximate(navs) => { | 99 | let it = it.source(db).1; |
91 | // We are no longer exact | 100 | res.extend(hover_text(it.doc_comment_text(), it.short_label())); |
92 | res.exact = false; | 101 | } |
93 | 102 | Some(Macro(it)) => { | |
94 | for nav in navs { | 103 | let it = it.source(db).1; |
95 | res.extend(doc_text_for(nav)) | 104 | res.extend(hover_text(it.doc_comment_text(), None)); |
105 | } | ||
106 | Some(FieldAccess(it)) => { | ||
107 | let it = it.source(db).1; | ||
108 | if let hir::FieldSource::Named(it) = it { | ||
109 | res.extend(hover_text(it.doc_comment_text(), it.short_label())); | ||
110 | } | ||
111 | } | ||
112 | Some(AssocItem(it)) => match it { | ||
113 | hir::ImplItem::Method(it) => { | ||
114 | let it = it.source(db).1; | ||
115 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
116 | } | ||
117 | hir::ImplItem::Const(it) => { | ||
118 | let it = it.source(db).1; | ||
119 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
120 | } | ||
121 | hir::ImplItem::TypeAlias(it) => { | ||
122 | let it = it.source(db).1; | ||
123 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
124 | } | ||
125 | }, | ||
126 | Some(Def(it)) => { | ||
127 | match it { | ||
128 | hir::ModuleDef::Module(it) => { | ||
129 | let it = it.definition_source(db).1; | ||
130 | if let hir::ModuleSource::Module(it) = it { | ||
131 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
132 | } | ||
133 | } | ||
134 | hir::ModuleDef::Function(it) => { | ||
135 | let it = it.source(db).1; | ||
136 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
137 | } | ||
138 | hir::ModuleDef::Struct(it) => { | ||
139 | let it = it.source(db).1; | ||
140 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
141 | } | ||
142 | hir::ModuleDef::Union(it) => { | ||
143 | let it = it.source(db).1; | ||
144 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
145 | } | ||
146 | hir::ModuleDef::Enum(it) => { | ||
147 | let it = it.source(db).1; | ||
148 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
149 | } | ||
150 | hir::ModuleDef::EnumVariant(it) => { | ||
151 | let it = it.source(db).1; | ||
152 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
153 | } | ||
154 | hir::ModuleDef::Const(it) => { | ||
155 | let it = it.source(db).1; | ||
156 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
157 | } | ||
158 | hir::ModuleDef::Static(it) => { | ||
159 | let it = it.source(db).1; | ||
160 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
161 | } | ||
162 | hir::ModuleDef::Trait(it) => { | ||
163 | let it = it.source(db).1; | ||
164 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
165 | } | ||
166 | hir::ModuleDef::TypeAlias(it) => { | ||
167 | let it = it.source(db).1; | ||
168 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
169 | } | ||
170 | hir::ModuleDef::BuiltinType(_) => { | ||
171 | // FIXME: hover for builtin Type ? | ||
172 | } | ||
96 | } | 173 | } |
97 | } | 174 | } |
175 | Some(SelfType(ty)) => { | ||
176 | if let Some((adt_def, _)) = ty.as_adt() { | ||
177 | match adt_def { | ||
178 | hir::AdtDef::Struct(it) => { | ||
179 | let it = it.source(db).1; | ||
180 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
181 | } | ||
182 | hir::AdtDef::Union(it) => { | ||
183 | let it = it.source(db).1; | ||
184 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
185 | } | ||
186 | hir::AdtDef::Enum(it) => { | ||
187 | let it = it.source(db).1; | ||
188 | res.extend(hover_text(it.doc_comment_text(), it.short_label())) | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | Some(Pat(_)) => { | ||
194 | res.extend(None); | ||
195 | } | ||
196 | Some(SelfParam(_)) => { | ||
197 | res.extend(None); | ||
198 | } | ||
199 | Some(GenericParam(_)) => { | ||
200 | // FIXME: Hover for generic param | ||
201 | } | ||
202 | None => {} | ||
98 | } | 203 | } |
204 | |||
205 | if res.is_empty() { | ||
206 | // Fallback index based approach: | ||
207 | let symbols = crate::symbol_index::index_resolve(db, name_ref); | ||
208 | for sym in symbols { | ||
209 | let docs = docs_from_symbol(db, &sym); | ||
210 | let desc = description_from_symbol(db, &sym); | ||
211 | res.extend(hover_text(docs, desc)); | ||
212 | } | ||
213 | } | ||
214 | |||
99 | if !res.is_empty() { | 215 | if !res.is_empty() { |
100 | range = Some(name_ref.syntax().range()) | 216 | range = Some(name_ref.syntax().range()) |
101 | } | 217 | } |
102 | } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { | 218 | } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { |
103 | let navs = crate::goto_definition::name_definition(db, position.file_id, name); | 219 | if let Some(parent) = name.syntax().parent() { |
104 | 220 | let text = visitor() | |
105 | if let Some(navs) = navs { | 221 | .visit(|node: &ast::StructDef| { |
106 | for nav in navs { | 222 | hover_text(node.doc_comment_text(), node.short_label()) |
107 | res.extend(doc_text_for(nav)) | 223 | }) |
224 | .visit(|node: &ast::EnumDef| { | ||
225 | hover_text(node.doc_comment_text(), node.short_label()) | ||
226 | }) | ||
227 | .visit(|node: &ast::EnumVariant| { | ||
228 | hover_text(node.doc_comment_text(), node.short_label()) | ||
229 | }) | ||
230 | .visit(|node: &ast::FnDef| hover_text(node.doc_comment_text(), node.short_label())) | ||
231 | .visit(|node: &ast::TypeAliasDef| { | ||
232 | hover_text(node.doc_comment_text(), node.short_label()) | ||
233 | }) | ||
234 | .visit(|node: &ast::ConstDef| { | ||
235 | hover_text(node.doc_comment_text(), node.short_label()) | ||
236 | }) | ||
237 | .visit(|node: &ast::StaticDef| { | ||
238 | hover_text(node.doc_comment_text(), node.short_label()) | ||
239 | }) | ||
240 | .visit(|node: &ast::TraitDef| { | ||
241 | hover_text(node.doc_comment_text(), node.short_label()) | ||
242 | }) | ||
243 | .visit(|node: &ast::NamedFieldDef| { | ||
244 | hover_text(node.doc_comment_text(), node.short_label()) | ||
245 | }) | ||
246 | .visit(|node: &ast::Module| hover_text(node.doc_comment_text(), node.short_label())) | ||
247 | .visit(|node: &ast::MacroCall| hover_text(node.doc_comment_text(), None)) | ||
248 | .accept(parent); | ||
249 | |||
250 | if let Some(text) = text { | ||
251 | res.extend(text); | ||
108 | } | 252 | } |
109 | } | 253 | } |
110 | 254 | ||