aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/hover.rs204
1 files changed, 93 insertions, 111 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index 086e6dec3..07d511fb3 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase;
5use ra_syntax::{ 5use ra_syntax::{
6 algo::{ancestors_at_offset, find_covering_element, find_node_at_offset}, 6 algo::{ancestors_at_offset, find_covering_element, find_node_at_offset},
7 ast::{self, DocCommentsOwner}, 7 ast::{self, DocCommentsOwner},
8 match_ast, AstNode, 8 AstNode,
9}; 9};
10 10
11use crate::{ 11use crate::{
@@ -14,7 +14,7 @@ use crate::{
14 description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, 14 description_from_symbol, docs_from_symbol, macro_label, rust_code_markup,
15 rust_code_markup_with_doc, ShortLabel, 15 rust_code_markup_with_doc, ShortLabel,
16 }, 16 },
17 references::{classify_name_ref, NameKind::*}, 17 references::{classify_name, classify_name_ref, NameKind, NameKind::*},
18 FilePosition, FileRange, RangeInfo, 18 FilePosition, FileRange, RangeInfo,
19}; 19};
20 20
@@ -92,65 +92,88 @@ fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> {
92 } 92 }
93} 93}
94 94
95fn hover_text_from_name_kind(
96 db: &RootDatabase,
97 name_kind: NameKind,
98 no_fallback: &mut bool,
99) -> Option<String> {
100 return match name_kind {
101 Macro(it) => {
102 let src = it.source(db);
103 hover_text(src.ast.doc_comment_text(), Some(macro_label(&src.ast)))
104 }
105 Field(it) => {
106 let src = it.source(db);
107 match src.ast {
108 hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()),
109 _ => None,
110 }
111 }
112 AssocItem(it) => match it {
113 hir::AssocItem::Function(it) => from_def_source(db, it),
114 hir::AssocItem::Const(it) => from_def_source(db, it),
115 hir::AssocItem::TypeAlias(it) => from_def_source(db, it),
116 },
117 Def(it) => match it {
118 hir::ModuleDef::Module(it) => match it.definition_source(db).ast {
119 hir::ModuleSource::Module(it) => {
120 hover_text(it.doc_comment_text(), it.short_label())
121 }
122 _ => None,
123 },
124 hir::ModuleDef::Function(it) => from_def_source(db, it),
125 hir::ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it),
126 hir::ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it),
127 hir::ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it),
128 hir::ModuleDef::EnumVariant(it) => from_def_source(db, it),
129 hir::ModuleDef::Const(it) => from_def_source(db, it),
130 hir::ModuleDef::Static(it) => from_def_source(db, it),
131 hir::ModuleDef::Trait(it) => from_def_source(db, it),
132 hir::ModuleDef::TypeAlias(it) => from_def_source(db, it),
133 hir::ModuleDef::BuiltinType(it) => Some(it.to_string()),
134 },
135 SelfType(ty) => match ty.as_adt() {
136 Some((adt_def, _)) => match adt_def {
137 hir::Adt::Struct(it) => from_def_source(db, it),
138 hir::Adt::Union(it) => from_def_source(db, it),
139 hir::Adt::Enum(it) => from_def_source(db, it),
140 },
141 _ => None,
142 },
143 Local(_) => {
144 // Hover for these shows type names
145 *no_fallback = true;
146 None
147 }
148 GenericParam(_) => {
149 // FIXME: Hover for generic param
150 None
151 }
152 };
153
154 fn from_def_source<A, D>(db: &RootDatabase, def: D) -> Option<String>
155 where
156 D: HasSource<Ast = A>,
157 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
158 {
159 let src = def.source(db);
160 hover_text(src.ast.doc_comment_text(), src.ast.short_label())
161 }
162}
163
95pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { 164pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
96 let parse = db.parse(position.file_id); 165 let parse = db.parse(position.file_id);
97 let file = parse.tree(); 166 let file = parse.tree();
167
98 let mut res = HoverResult::new(); 168 let mut res = HoverResult::new();
99 169
100 let mut range = None; 170 let mut range = if let Some(name_ref) =
101 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { 171 find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)
172 {
102 let mut no_fallback = false; 173 let mut no_fallback = false;
103 let name_kind = classify_name_ref(db, position.file_id, &name_ref).map(|d| d.kind); 174 if let Some(name_kind) = classify_name_ref(db, position.file_id, &name_ref).map(|d| d.kind)
104 match name_kind { 175 {
105 Some(Macro(it)) => { 176 res.extend(hover_text_from_name_kind(db, name_kind, &mut no_fallback))
106 let src = it.source(db);
107 res.extend(hover_text(src.ast.doc_comment_text(), Some(macro_label(&src.ast))));
108 }
109 Some(Field(it)) => {
110 let src = it.source(db);
111 if let hir::FieldSource::Named(it) = src.ast {
112 res.extend(hover_text(it.doc_comment_text(), it.short_label()));
113 }
114 }
115 Some(AssocItem(it)) => res.extend(match it {
116 hir::AssocItem::Function(it) => from_def_source(db, it),
117 hir::AssocItem::Const(it) => from_def_source(db, it),
118 hir::AssocItem::TypeAlias(it) => from_def_source(db, it),
119 }),
120 Some(Def(it)) => match it {
121 hir::ModuleDef::Module(it) => {
122 if let hir::ModuleSource::Module(it) = it.definition_source(db).ast {
123 res.extend(hover_text(it.doc_comment_text(), it.short_label()))
124 }
125 }
126 hir::ModuleDef::Function(it) => res.extend(from_def_source(db, it)),
127 hir::ModuleDef::Adt(Adt::Struct(it)) => res.extend(from_def_source(db, it)),
128 hir::ModuleDef::Adt(Adt::Union(it)) => res.extend(from_def_source(db, it)),
129 hir::ModuleDef::Adt(Adt::Enum(it)) => res.extend(from_def_source(db, it)),
130 hir::ModuleDef::EnumVariant(it) => res.extend(from_def_source(db, it)),
131 hir::ModuleDef::Const(it) => res.extend(from_def_source(db, it)),
132 hir::ModuleDef::Static(it) => res.extend(from_def_source(db, it)),
133 hir::ModuleDef::Trait(it) => res.extend(from_def_source(db, it)),
134 hir::ModuleDef::TypeAlias(it) => res.extend(from_def_source(db, it)),
135 hir::ModuleDef::BuiltinType(it) => res.extend(Some(it.to_string())),
136 },
137 Some(SelfType(ty)) => {
138 if let Some((adt_def, _)) = ty.as_adt() {
139 res.extend(match adt_def {
140 hir::Adt::Struct(it) => from_def_source(db, it),
141 hir::Adt::Union(it) => from_def_source(db, it),
142 hir::Adt::Enum(it) => from_def_source(db, it),
143 })
144 }
145 }
146 Some(Local(_)) => {
147 // Hover for these shows type names
148 no_fallback = true;
149 }
150 Some(GenericParam(_)) => {
151 // FIXME: Hover for generic param
152 }
153 None => {}
154 } 177 }
155 178
156 if res.is_empty() && !no_fallback { 179 if res.is_empty() && !no_fallback {
@@ -164,55 +187,24 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
164 } 187 }
165 188
166 if !res.is_empty() { 189 if !res.is_empty() {
167 range = Some(name_ref.syntax().text_range()) 190 Some(name_ref.syntax().text_range())
191 } else {
192 None
168 } 193 }
169 } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { 194 } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
170 if let Some(parent) = name.syntax().parent() { 195 if let Some(name_kind) = classify_name(db, position.file_id, &name).map(|d| d.kind) {
171 let text = match_ast! { 196 let mut _b: bool = true;
172 match parent { 197 res.extend(hover_text_from_name_kind(db, name_kind, &mut _b));
173 ast::StructDef(it) => {
174 hover_text(it.doc_comment_text(), it.short_label())
175 },
176 ast::EnumDef(it) => {
177 hover_text(it.doc_comment_text(), it.short_label())
178 },
179 ast::EnumVariant(it) => {
180 hover_text(it.doc_comment_text(), it.short_label())
181 },
182 ast::FnDef(it) => {
183 hover_text(it.doc_comment_text(), it.short_label())
184 },
185 ast::TypeAliasDef(it) => {
186 hover_text(it.doc_comment_text(), it.short_label())
187 },
188 ast::ConstDef(it) => {
189 hover_text(it.doc_comment_text(), it.short_label())
190 },
191 ast::StaticDef(it) => {
192 hover_text(it.doc_comment_text(), it.short_label())
193 },
194 ast::TraitDef(it) => {
195 hover_text(it.doc_comment_text(), it.short_label())
196 },
197 ast::RecordFieldDef(it) => {
198 hover_text(it.doc_comment_text(), it.short_label())
199 },
200 ast::Module(it) => {
201 hover_text(it.doc_comment_text(), it.short_label())
202 },
203 ast::MacroCall(it) => {
204 hover_text(it.doc_comment_text(), None)
205 },
206 _ => None,
207 }
208 };
209 res.extend(text);
210 } 198 }
211 199
212 if !res.is_empty() && range.is_none() { 200 if !res.is_empty() {
213 range = Some(name.syntax().text_range()); 201 Some(name.syntax().text_range())
202 } else {
203 None
214 } 204 }
215 } 205 } else {
206 None
207 };
216 208
217 if range.is_none() { 209 if range.is_none() {
218 let node = ancestors_at_offset(file.syntax(), position.offset).find(|n| { 210 let node = ancestors_at_offset(file.syntax(), position.offset).find(|n| {
@@ -221,23 +213,13 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
221 let frange = FileRange { file_id: position.file_id, range: node.text_range() }; 213 let frange = FileRange { file_id: position.file_id, range: node.text_range() };
222 res.extend(type_of(db, frange).map(rust_code_markup)); 214 res.extend(type_of(db, frange).map(rust_code_markup));
223 range = Some(node.text_range()); 215 range = Some(node.text_range());
224 } 216 };
225 217
226 let range = range?; 218 let range = range?;
227 if res.is_empty() { 219 if res.is_empty() {
228 return None; 220 return None;
229 } 221 }
230 let res = RangeInfo::new(range, res); 222 Some(RangeInfo::new(range, res))
231 return Some(res);
232
233 fn from_def_source<A, D>(db: &RootDatabase, def: D) -> Option<String>
234 where
235 D: HasSource<Ast = A>,
236 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
237 {
238 let src = def.source(db);
239 hover_text(src.ast.doc_comment_text(), src.ast.short_label())
240 }
241} 223}
242 224
243pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { 225pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {