diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/assists/add_new.rs | 379 | ||||
-rw-r--r-- | crates/ra_assists/src/doc_tests/generated.rs | 22 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 204 | ||||
-rw-r--r-- | crates/ra_prof/Cargo.toml | 38 |
5 files changed, 515 insertions, 130 deletions
diff --git a/crates/ra_assists/src/assists/add_new.rs b/crates/ra_assists/src/assists/add_new.rs new file mode 100644 index 000000000..a8839cfba --- /dev/null +++ b/crates/ra_assists/src/assists/add_new.rs | |||
@@ -0,0 +1,379 @@ | |||
1 | use format_buf::format; | ||
2 | use hir::{db::HirDatabase, FromSource}; | ||
3 | use join_to_string::join; | ||
4 | use ra_syntax::{ | ||
5 | ast::{ | ||
6 | self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, | ||
7 | }, | ||
8 | TextUnit, T, | ||
9 | }; | ||
10 | use std::fmt::Write; | ||
11 | |||
12 | use crate::{Assist, AssistCtx, AssistId}; | ||
13 | |||
14 | // Assist: add_new | ||
15 | // | ||
16 | // Adds a new inherent impl for a type. | ||
17 | // | ||
18 | // ``` | ||
19 | // struct Ctx<T: Clone> { | ||
20 | // data: T,<|> | ||
21 | // } | ||
22 | // ``` | ||
23 | // -> | ||
24 | // ``` | ||
25 | // struct Ctx<T: Clone> { | ||
26 | // data: T, | ||
27 | // } | ||
28 | // | ||
29 | // impl<T: Clone> Ctx<T> { | ||
30 | // fn new(data: T) -> Self { Self { data } } | ||
31 | // } | ||
32 | // | ||
33 | // ``` | ||
34 | pub(crate) fn add_new(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
35 | let strukt = ctx.find_node_at_offset::<ast::StructDef>()?; | ||
36 | |||
37 | // We want to only apply this to non-union structs with named fields | ||
38 | let field_list = match (strukt.kind(), strukt.is_union()) { | ||
39 | (StructKind::Named(named), false) => named, | ||
40 | _ => return None, | ||
41 | }; | ||
42 | |||
43 | // Return early if we've found an existing new fn | ||
44 | let impl_block = find_struct_impl(&ctx, &strukt)?; | ||
45 | |||
46 | ctx.add_assist(AssistId("add_new"), "add new fn", |edit| { | ||
47 | edit.target(strukt.syntax().text_range()); | ||
48 | |||
49 | let mut buf = String::with_capacity(512); | ||
50 | |||
51 | if impl_block.is_some() { | ||
52 | buf.push('\n'); | ||
53 | } | ||
54 | |||
55 | let vis = strukt.visibility().map(|v| format!("{} ", v.syntax())); | ||
56 | let vis = vis.as_ref().map(String::as_str).unwrap_or(""); | ||
57 | write!(&mut buf, " {}fn new(", vis).unwrap(); | ||
58 | |||
59 | join(field_list.fields().map(|f| { | ||
60 | format!( | ||
61 | "{}: {}", | ||
62 | f.name().unwrap().syntax().text(), | ||
63 | f.ascribed_type().unwrap().syntax().text() | ||
64 | ) | ||
65 | })) | ||
66 | .separator(", ") | ||
67 | .to_buf(&mut buf); | ||
68 | |||
69 | buf.push_str(") -> Self { Self {"); | ||
70 | |||
71 | join(field_list.fields().map(|f| f.name().unwrap().syntax().text())) | ||
72 | .separator(", ") | ||
73 | .surround_with(" ", " ") | ||
74 | .to_buf(&mut buf); | ||
75 | |||
76 | buf.push_str("} }"); | ||
77 | |||
78 | let (start_offset, end_offset) = if let Some(impl_block) = impl_block { | ||
79 | buf.push('\n'); | ||
80 | let start = impl_block | ||
81 | .syntax() | ||
82 | .descendants_with_tokens() | ||
83 | .find(|t| t.kind() == T!['{']) | ||
84 | .unwrap() | ||
85 | .text_range() | ||
86 | .end(); | ||
87 | |||
88 | (start, TextUnit::from_usize(1)) | ||
89 | } else { | ||
90 | buf = generate_impl_text(&strukt, &buf); | ||
91 | let start = strukt.syntax().text_range().end(); | ||
92 | |||
93 | (start, TextUnit::from_usize(3)) | ||
94 | }; | ||
95 | |||
96 | edit.set_cursor(start_offset + TextUnit::of_str(&buf) - end_offset); | ||
97 | edit.insert(start_offset, buf); | ||
98 | }) | ||
99 | } | ||
100 | |||
101 | // Generates the surrounding `impl Type { <code> }` including type and lifetime | ||
102 | // parameters | ||
103 | fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String { | ||
104 | let type_params = strukt.type_param_list(); | ||
105 | let mut buf = String::with_capacity(code.len()); | ||
106 | buf.push_str("\n\nimpl"); | ||
107 | if let Some(type_params) = &type_params { | ||
108 | format!(buf, "{}", type_params.syntax()); | ||
109 | } | ||
110 | buf.push_str(" "); | ||
111 | buf.push_str(strukt.name().unwrap().text().as_str()); | ||
112 | if let Some(type_params) = type_params { | ||
113 | let lifetime_params = type_params | ||
114 | .lifetime_params() | ||
115 | .filter_map(|it| it.lifetime_token()) | ||
116 | .map(|it| it.text().clone()); | ||
117 | let type_params = | ||
118 | type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); | ||
119 | join(lifetime_params.chain(type_params)).surround_with("<", ">").to_buf(&mut buf); | ||
120 | } | ||
121 | |||
122 | format!(&mut buf, " {{\n{}\n}}\n", code); | ||
123 | |||
124 | buf | ||
125 | } | ||
126 | |||
127 | // Uses a syntax-driven approach to find any impl blocks for the struct that | ||
128 | // exist within the module/file | ||
129 | // | ||
130 | // Returns `None` if we've found an existing `new` fn | ||
131 | // | ||
132 | // FIXME: change the new fn checking to a more semantic approach when that's more | ||
133 | // viable (e.g. we process proc macros, etc) | ||
134 | fn find_struct_impl( | ||
135 | ctx: &AssistCtx<impl HirDatabase>, | ||
136 | strukt: &ast::StructDef, | ||
137 | ) -> Option<Option<ast::ImplBlock>> { | ||
138 | let db = ctx.db; | ||
139 | let module = strukt.syntax().ancestors().find(|node| { | ||
140 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | ||
141 | })?; | ||
142 | |||
143 | let struct_ty = { | ||
144 | let src = hir::Source { file_id: ctx.frange.file_id.into(), ast: strukt.clone() }; | ||
145 | hir::Struct::from_source(db, src).unwrap().ty(db) | ||
146 | }; | ||
147 | |||
148 | let mut found_new_fn = false; | ||
149 | |||
150 | let block = module.descendants().filter_map(ast::ImplBlock::cast).find(|impl_blk| { | ||
151 | if found_new_fn { | ||
152 | return false; | ||
153 | } | ||
154 | |||
155 | let src = hir::Source { file_id: ctx.frange.file_id.into(), ast: impl_blk.clone() }; | ||
156 | let blk = hir::ImplBlock::from_source(db, src).unwrap(); | ||
157 | |||
158 | let same_ty = blk.target_ty(db) == struct_ty; | ||
159 | let not_trait_impl = blk.target_trait(db).is_none(); | ||
160 | |||
161 | found_new_fn = has_new_fn(impl_blk); | ||
162 | |||
163 | same_ty && not_trait_impl | ||
164 | }); | ||
165 | |||
166 | if found_new_fn { | ||
167 | None | ||
168 | } else { | ||
169 | Some(block) | ||
170 | } | ||
171 | } | ||
172 | |||
173 | fn has_new_fn(imp: &ast::ImplBlock) -> bool { | ||
174 | if let Some(il) = imp.item_list() { | ||
175 | for item in il.impl_items() { | ||
176 | if let ast::ImplItem::FnDef(f) = item { | ||
177 | if f.name().unwrap().text().eq_ignore_ascii_case("new") { | ||
178 | return true; | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | |||
184 | false | ||
185 | } | ||
186 | |||
187 | #[cfg(test)] | ||
188 | mod tests { | ||
189 | use super::*; | ||
190 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
191 | |||
192 | #[test] | ||
193 | #[rustfmt::skip] | ||
194 | fn test_add_new() { | ||
195 | // Check output of generation | ||
196 | check_assist( | ||
197 | add_new, | ||
198 | "struct Foo {<|>}", | ||
199 | "struct Foo {} | ||
200 | |||
201 | impl Foo { | ||
202 | fn new() -> Self { Self { } }<|> | ||
203 | } | ||
204 | ", | ||
205 | ); | ||
206 | check_assist( | ||
207 | add_new, | ||
208 | "struct Foo<T: Clone> {<|>}", | ||
209 | "struct Foo<T: Clone> {} | ||
210 | |||
211 | impl<T: Clone> Foo<T> { | ||
212 | fn new() -> Self { Self { } }<|> | ||
213 | } | ||
214 | ", | ||
215 | ); | ||
216 | check_assist( | ||
217 | add_new, | ||
218 | "struct Foo<'a, T: Foo<'a>> {<|>}", | ||
219 | "struct Foo<'a, T: Foo<'a>> {} | ||
220 | |||
221 | impl<'a, T: Foo<'a>> Foo<'a, T> { | ||
222 | fn new() -> Self { Self { } }<|> | ||
223 | } | ||
224 | ", | ||
225 | ); | ||
226 | check_assist( | ||
227 | add_new, | ||
228 | "struct Foo { baz: String <|>}", | ||
229 | "struct Foo { baz: String } | ||
230 | |||
231 | impl Foo { | ||
232 | fn new(baz: String) -> Self { Self { baz } }<|> | ||
233 | } | ||
234 | ", | ||
235 | ); | ||
236 | check_assist( | ||
237 | add_new, | ||
238 | "struct Foo { baz: String, qux: Vec<i32> <|>}", | ||
239 | "struct Foo { baz: String, qux: Vec<i32> } | ||
240 | |||
241 | impl Foo { | ||
242 | fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|> | ||
243 | } | ||
244 | ", | ||
245 | ); | ||
246 | |||
247 | // Check that visibility modifiers don't get brought in for fields | ||
248 | check_assist( | ||
249 | add_new, | ||
250 | "struct Foo { pub baz: String, pub qux: Vec<i32> <|>}", | ||
251 | "struct Foo { pub baz: String, pub qux: Vec<i32> } | ||
252 | |||
253 | impl Foo { | ||
254 | fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|> | ||
255 | } | ||
256 | ", | ||
257 | ); | ||
258 | |||
259 | // Check that it reuses existing impls | ||
260 | check_assist( | ||
261 | add_new, | ||
262 | "struct Foo {<|>} | ||
263 | |||
264 | impl Foo {} | ||
265 | ", | ||
266 | "struct Foo {} | ||
267 | |||
268 | impl Foo { | ||
269 | fn new() -> Self { Self { } }<|> | ||
270 | } | ||
271 | ", | ||
272 | ); | ||
273 | check_assist( | ||
274 | add_new, | ||
275 | "struct Foo {<|>} | ||
276 | |||
277 | impl Foo { | ||
278 | fn qux(&self) {} | ||
279 | } | ||
280 | ", | ||
281 | "struct Foo {} | ||
282 | |||
283 | impl Foo { | ||
284 | fn new() -> Self { Self { } }<|> | ||
285 | |||
286 | fn qux(&self) {} | ||
287 | } | ||
288 | ", | ||
289 | ); | ||
290 | |||
291 | check_assist( | ||
292 | add_new, | ||
293 | "struct Foo {<|>} | ||
294 | |||
295 | impl Foo { | ||
296 | fn qux(&self) {} | ||
297 | fn baz() -> i32 { | ||
298 | 5 | ||
299 | } | ||
300 | } | ||
301 | ", | ||
302 | "struct Foo {} | ||
303 | |||
304 | impl Foo { | ||
305 | fn new() -> Self { Self { } }<|> | ||
306 | |||
307 | fn qux(&self) {} | ||
308 | fn baz() -> i32 { | ||
309 | 5 | ||
310 | } | ||
311 | } | ||
312 | ", | ||
313 | ); | ||
314 | |||
315 | // Check visibility of new fn based on struct | ||
316 | check_assist( | ||
317 | add_new, | ||
318 | "pub struct Foo {<|>}", | ||
319 | "pub struct Foo {} | ||
320 | |||
321 | impl Foo { | ||
322 | pub fn new() -> Self { Self { } }<|> | ||
323 | } | ||
324 | ", | ||
325 | ); | ||
326 | check_assist( | ||
327 | add_new, | ||
328 | "pub(crate) struct Foo {<|>}", | ||
329 | "pub(crate) struct Foo {} | ||
330 | |||
331 | impl Foo { | ||
332 | pub(crate) fn new() -> Self { Self { } }<|> | ||
333 | } | ||
334 | ", | ||
335 | ); | ||
336 | } | ||
337 | |||
338 | #[test] | ||
339 | fn add_new_not_applicable_if_fn_exists() { | ||
340 | check_assist_not_applicable( | ||
341 | add_new, | ||
342 | " | ||
343 | struct Foo {<|>} | ||
344 | |||
345 | impl Foo { | ||
346 | fn new() -> Self { | ||
347 | Self | ||
348 | } | ||
349 | }", | ||
350 | ); | ||
351 | |||
352 | check_assist_not_applicable( | ||
353 | add_new, | ||
354 | " | ||
355 | struct Foo {<|>} | ||
356 | |||
357 | impl Foo { | ||
358 | fn New() -> Self { | ||
359 | Self | ||
360 | } | ||
361 | }", | ||
362 | ); | ||
363 | } | ||
364 | |||
365 | #[test] | ||
366 | fn add_new_target() { | ||
367 | check_assist_target( | ||
368 | add_new, | ||
369 | " | ||
370 | struct SomeThingIrrelevant; | ||
371 | /// Has a lifetime parameter | ||
372 | struct Foo<'a, T: Foo<'a>> {<|>} | ||
373 | struct EvenMoreIrrelevant; | ||
374 | ", | ||
375 | "/// Has a lifetime parameter | ||
376 | struct Foo<'a, T: Foo<'a>> {}", | ||
377 | ); | ||
378 | } | ||
379 | } | ||
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 1bee76f59..176761efb 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs | |||
@@ -157,6 +157,28 @@ fn process(map: HashMap<String, String>) {} | |||
157 | } | 157 | } |
158 | 158 | ||
159 | #[test] | 159 | #[test] |
160 | fn doctest_add_new() { | ||
161 | check( | ||
162 | "add_new", | ||
163 | r#####" | ||
164 | struct Ctx<T: Clone> { | ||
165 | data: T,<|> | ||
166 | } | ||
167 | "#####, | ||
168 | r#####" | ||
169 | struct Ctx<T: Clone> { | ||
170 | data: T, | ||
171 | } | ||
172 | |||
173 | impl<T: Clone> Ctx<T> { | ||
174 | fn new(data: T) -> Self { Self { data } } | ||
175 | } | ||
176 | |||
177 | "#####, | ||
178 | ) | ||
179 | } | ||
180 | |||
181 | #[test] | ||
160 | fn doctest_apply_demorgan() { | 182 | fn doctest_apply_demorgan() { |
161 | check( | 183 | check( |
162 | "apply_demorgan", | 184 | "apply_demorgan", |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 39c1c283f..f2f0dacbf 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -95,6 +95,7 @@ mod assists { | |||
95 | mod add_derive; | 95 | mod add_derive; |
96 | mod add_explicit_type; | 96 | mod add_explicit_type; |
97 | mod add_impl; | 97 | mod add_impl; |
98 | mod add_new; | ||
98 | mod apply_demorgan; | 99 | mod apply_demorgan; |
99 | mod flip_comma; | 100 | mod flip_comma; |
100 | mod flip_binexpr; | 101 | mod flip_binexpr; |
@@ -119,6 +120,7 @@ mod assists { | |||
119 | add_derive::add_derive, | 120 | add_derive::add_derive, |
120 | add_explicit_type::add_explicit_type, | 121 | add_explicit_type::add_explicit_type, |
121 | add_impl::add_impl, | 122 | add_impl::add_impl, |
123 | add_new::add_new, | ||
122 | apply_demorgan::apply_demorgan, | 124 | apply_demorgan::apply_demorgan, |
123 | change_visibility::change_visibility, | 125 | change_visibility::change_visibility, |
124 | fill_match_arms::fill_match_arms, | 126 | fill_match_arms::fill_match_arms, |
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; | |||
5 | use ra_syntax::{ | 5 | use 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 | ||
11 | use crate::{ | 11 | use 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 | ||
95 | fn 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 | |||
95 | pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { | 164 | pub(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 | ||
243 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { | 225 | pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { |
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml index bb241258c..751bcdeb8 100644 --- a/crates/ra_prof/Cargo.toml +++ b/crates/ra_prof/Cargo.toml | |||
@@ -1,19 +1,19 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | 2 | edition = "2018" |
3 | name = "ra_prof" | 3 | name = "ra_prof" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | publish = false | 6 | publish = false |
7 | 7 | ||
8 | [dependencies] | 8 | [dependencies] |
9 | once_cell = "1.0.1" | 9 | once_cell = "1.0.1" |
10 | itertools = "0.8.0" | 10 | itertools = "0.8.0" |
11 | backtrace = "0.3.28" | 11 | backtrace = "0.3.28" |
12 | 12 | ||
13 | [target.'cfg(not(target_env = "msvc"))'.dependencies] | 13 | [target.'cfg(not(target_env = "msvc"))'.dependencies] |
14 | jemallocator = { version = "0.3.2", optional = true } | 14 | jemallocator = { version = "0.3.2", optional = true } |
15 | jemalloc-ctl = { version = "0.3.2", optional = true } | 15 | jemalloc-ctl = { version = "0.3.2", optional = true } |
16 | 16 | ||
17 | [features] | 17 | [features] |
18 | jemalloc = [ "jemallocator", "jemalloc-ctl" ] | 18 | jemalloc = [ "jemallocator", "jemalloc-ctl" ] |
19 | cpu_profiler = [] | 19 | cpu_profiler = [] |