diff options
Diffstat (limited to 'crates/ra_ide')
27 files changed, 1183 insertions, 213 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 4ca0fdf4f..8bdc43b1a 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -65,7 +65,7 @@ pub(crate) fn completions( | |||
65 | let ctx = CompletionContext::new(db, position, config)?; | 65 | let ctx = CompletionContext::new(db, position, config)?; |
66 | 66 | ||
67 | let mut acc = Completions::default(); | 67 | let mut acc = Completions::default(); |
68 | 68 | complete_attribute::complete_attribute(&mut acc, &ctx); | |
69 | complete_fn_param::complete_fn_param(&mut acc, &ctx); | 69 | complete_fn_param::complete_fn_param(&mut acc, &ctx); |
70 | complete_keyword::complete_expr_keyword(&mut acc, &ctx); | 70 | complete_keyword::complete_expr_keyword(&mut acc, &ctx); |
71 | complete_keyword::complete_use_tree_keyword(&mut acc, &ctx); | 71 | complete_keyword::complete_use_tree_keyword(&mut acc, &ctx); |
@@ -79,7 +79,6 @@ pub(crate) fn completions( | |||
79 | complete_postfix::complete_postfix(&mut acc, &ctx); | 79 | complete_postfix::complete_postfix(&mut acc, &ctx); |
80 | complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); | 80 | complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); |
81 | complete_trait_impl::complete_trait_impl(&mut acc, &ctx); | 81 | complete_trait_impl::complete_trait_impl(&mut acc, &ctx); |
82 | complete_attribute::complete_attribute(&mut acc, &ctx); | ||
83 | 82 | ||
84 | Some(acc) | 83 | Some(acc) |
85 | } | 84 | } |
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs index 8bf952798..f17266221 100644 --- a/crates/ra_ide/src/completion/complete_attribute.rs +++ b/crates/ra_ide/src/completion/complete_attribute.rs | |||
@@ -3,25 +3,29 @@ | |||
3 | //! This module uses a bit of static metadata to provide completions | 3 | //! This module uses a bit of static metadata to provide completions |
4 | //! for built-in attributes. | 4 | //! for built-in attributes. |
5 | 5 | ||
6 | use super::completion_context::CompletionContext; | 6 | use ra_syntax::{ast, AstNode, SyntaxKind}; |
7 | use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; | 7 | use rustc_hash::FxHashSet; |
8 | use ra_syntax::{ | 8 | |
9 | ast::{Attr, AttrKind}, | 9 | use crate::completion::{ |
10 | AstNode, | 10 | completion_context::CompletionContext, |
11 | completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}, | ||
11 | }; | 12 | }; |
12 | 13 | ||
13 | pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { | 14 | pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
14 | if !ctx.is_attribute { | 15 | let attribute = ctx.attribute_under_caret.as_ref()?; |
15 | return; | ||
16 | } | ||
17 | 16 | ||
18 | let is_inner = ctx | 17 | match (attribute.path(), attribute.input()) { |
19 | .original_token | 18 | (Some(path), Some(ast::AttrInput::TokenTree(token_tree))) |
20 | .ancestors() | 19 | if path.to_string() == "derive" => |
21 | .find_map(Attr::cast) | 20 | { |
22 | .map(|attr| attr.kind() == AttrKind::Inner) | 21 | complete_derive(acc, ctx, token_tree) |
23 | .unwrap_or(false); | 22 | } |
23 | _ => complete_attribute_start(acc, ctx, attribute), | ||
24 | } | ||
25 | Some(()) | ||
26 | } | ||
24 | 27 | ||
28 | fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { | ||
25 | for attr_completion in ATTRIBUTES { | 29 | for attr_completion in ATTRIBUTES { |
26 | let mut item = CompletionItem::new( | 30 | let mut item = CompletionItem::new( |
27 | CompletionKind::Attribute, | 31 | CompletionKind::Attribute, |
@@ -37,7 +41,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) | |||
37 | _ => {} | 41 | _ => {} |
38 | } | 42 | } |
39 | 43 | ||
40 | if is_inner || !attr_completion.should_be_inner { | 44 | if attribute.kind() == ast::AttrKind::Inner || !attr_completion.should_be_inner { |
41 | acc.add(item); | 45 | acc.add(item); |
42 | } | 46 | } |
43 | } | 47 | } |
@@ -126,6 +130,106 @@ const ATTRIBUTES: &[AttrCompletion] = &[ | |||
126 | }, | 130 | }, |
127 | ]; | 131 | ]; |
128 | 132 | ||
133 | fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { | ||
134 | if let Ok(existing_derives) = parse_derive_input(derive_input) { | ||
135 | for derive_completion in DEFAULT_DERIVE_COMPLETIONS | ||
136 | .into_iter() | ||
137 | .filter(|completion| !existing_derives.contains(completion.label)) | ||
138 | { | ||
139 | let mut label = derive_completion.label.to_owned(); | ||
140 | for dependency in derive_completion | ||
141 | .dependencies | ||
142 | .into_iter() | ||
143 | .filter(|&&dependency| !existing_derives.contains(dependency)) | ||
144 | { | ||
145 | label.push_str(", "); | ||
146 | label.push_str(dependency); | ||
147 | } | ||
148 | acc.add( | ||
149 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label) | ||
150 | .kind(CompletionItemKind::Attribute), | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { | ||
155 | acc.add( | ||
156 | CompletionItem::new( | ||
157 | CompletionKind::Attribute, | ||
158 | ctx.source_range(), | ||
159 | custom_derive_name, | ||
160 | ) | ||
161 | .kind(CompletionItemKind::Attribute), | ||
162 | ); | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { | ||
168 | match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { | ||
169 | (Some(left_paren), Some(right_paren)) | ||
170 | if left_paren.kind() == SyntaxKind::L_PAREN | ||
171 | && right_paren.kind() == SyntaxKind::R_PAREN => | ||
172 | { | ||
173 | let mut input_derives = FxHashSet::default(); | ||
174 | let mut current_derive = String::new(); | ||
175 | for token in derive_input | ||
176 | .syntax() | ||
177 | .children_with_tokens() | ||
178 | .filter_map(|token| token.into_token()) | ||
179 | .skip_while(|token| token != &left_paren) | ||
180 | .skip(1) | ||
181 | .take_while(|token| token != &right_paren) | ||
182 | { | ||
183 | if SyntaxKind::COMMA == token.kind() { | ||
184 | if !current_derive.is_empty() { | ||
185 | input_derives.insert(current_derive); | ||
186 | current_derive = String::new(); | ||
187 | } | ||
188 | } else { | ||
189 | current_derive.push_str(token.to_string().trim()); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | if !current_derive.is_empty() { | ||
194 | input_derives.insert(current_derive); | ||
195 | } | ||
196 | Ok(input_derives) | ||
197 | } | ||
198 | _ => Err(()), | ||
199 | } | ||
200 | } | ||
201 | |||
202 | fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { | ||
203 | let mut result = FxHashSet::default(); | ||
204 | ctx.scope().process_all_names(&mut |name, scope_def| { | ||
205 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | ||
206 | if mac.is_derive_macro() { | ||
207 | result.insert(name.to_string()); | ||
208 | } | ||
209 | } | ||
210 | }); | ||
211 | result | ||
212 | } | ||
213 | |||
214 | struct DeriveCompletion { | ||
215 | label: &'static str, | ||
216 | dependencies: &'static [&'static str], | ||
217 | } | ||
218 | |||
219 | /// Standard Rust derives and the information about their dependencies | ||
220 | /// (the dependencies are needed so that the main derive don't break the compilation when added) | ||
221 | const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ | ||
222 | DeriveCompletion { label: "Clone", dependencies: &[] }, | ||
223 | DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, | ||
224 | DeriveCompletion { label: "Debug", dependencies: &[] }, | ||
225 | DeriveCompletion { label: "Default", dependencies: &[] }, | ||
226 | DeriveCompletion { label: "Hash", dependencies: &[] }, | ||
227 | DeriveCompletion { label: "PartialEq", dependencies: &[] }, | ||
228 | DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] }, | ||
229 | DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] }, | ||
230 | DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, | ||
231 | ]; | ||
232 | |||
129 | #[cfg(test)] | 233 | #[cfg(test)] |
130 | mod tests { | 234 | mod tests { |
131 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | 235 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; |
@@ -136,6 +240,170 @@ mod tests { | |||
136 | } | 240 | } |
137 | 241 | ||
138 | #[test] | 242 | #[test] |
243 | fn empty_derive_completion() { | ||
244 | assert_debug_snapshot!( | ||
245 | do_attr_completion( | ||
246 | r" | ||
247 | #[derive(<|>)] | ||
248 | struct Test {} | ||
249 | ", | ||
250 | ), | ||
251 | @r###" | ||
252 | [ | ||
253 | CompletionItem { | ||
254 | label: "Clone", | ||
255 | source_range: 30..30, | ||
256 | delete: 30..30, | ||
257 | insert: "Clone", | ||
258 | kind: Attribute, | ||
259 | }, | ||
260 | CompletionItem { | ||
261 | label: "Copy, Clone", | ||
262 | source_range: 30..30, | ||
263 | delete: 30..30, | ||
264 | insert: "Copy, Clone", | ||
265 | kind: Attribute, | ||
266 | }, | ||
267 | CompletionItem { | ||
268 | label: "Debug", | ||
269 | source_range: 30..30, | ||
270 | delete: 30..30, | ||
271 | insert: "Debug", | ||
272 | kind: Attribute, | ||
273 | }, | ||
274 | CompletionItem { | ||
275 | label: "Default", | ||
276 | source_range: 30..30, | ||
277 | delete: 30..30, | ||
278 | insert: "Default", | ||
279 | kind: Attribute, | ||
280 | }, | ||
281 | CompletionItem { | ||
282 | label: "Eq, PartialEq", | ||
283 | source_range: 30..30, | ||
284 | delete: 30..30, | ||
285 | insert: "Eq, PartialEq", | ||
286 | kind: Attribute, | ||
287 | }, | ||
288 | CompletionItem { | ||
289 | label: "Hash", | ||
290 | source_range: 30..30, | ||
291 | delete: 30..30, | ||
292 | insert: "Hash", | ||
293 | kind: Attribute, | ||
294 | }, | ||
295 | CompletionItem { | ||
296 | label: "Ord, PartialOrd, Eq, PartialEq", | ||
297 | source_range: 30..30, | ||
298 | delete: 30..30, | ||
299 | insert: "Ord, PartialOrd, Eq, PartialEq", | ||
300 | kind: Attribute, | ||
301 | }, | ||
302 | CompletionItem { | ||
303 | label: "PartialEq", | ||
304 | source_range: 30..30, | ||
305 | delete: 30..30, | ||
306 | insert: "PartialEq", | ||
307 | kind: Attribute, | ||
308 | }, | ||
309 | CompletionItem { | ||
310 | label: "PartialOrd, PartialEq", | ||
311 | source_range: 30..30, | ||
312 | delete: 30..30, | ||
313 | insert: "PartialOrd, PartialEq", | ||
314 | kind: Attribute, | ||
315 | }, | ||
316 | ] | ||
317 | "### | ||
318 | ); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
322 | fn no_completion_for_incorrect_derive() { | ||
323 | assert_debug_snapshot!( | ||
324 | do_attr_completion( | ||
325 | r" | ||
326 | #[derive{<|>)] | ||
327 | struct Test {} | ||
328 | ", | ||
329 | ), | ||
330 | @"[]" | ||
331 | ); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn derive_with_input_completion() { | ||
336 | assert_debug_snapshot!( | ||
337 | do_attr_completion( | ||
338 | r" | ||
339 | #[derive(serde::Serialize, PartialEq, <|>)] | ||
340 | struct Test {} | ||
341 | ", | ||
342 | ), | ||
343 | @r###" | ||
344 | [ | ||
345 | CompletionItem { | ||
346 | label: "Clone", | ||
347 | source_range: 59..59, | ||
348 | delete: 59..59, | ||
349 | insert: "Clone", | ||
350 | kind: Attribute, | ||
351 | }, | ||
352 | CompletionItem { | ||
353 | label: "Copy, Clone", | ||
354 | source_range: 59..59, | ||
355 | delete: 59..59, | ||
356 | insert: "Copy, Clone", | ||
357 | kind: Attribute, | ||
358 | }, | ||
359 | CompletionItem { | ||
360 | label: "Debug", | ||
361 | source_range: 59..59, | ||
362 | delete: 59..59, | ||
363 | insert: "Debug", | ||
364 | kind: Attribute, | ||
365 | }, | ||
366 | CompletionItem { | ||
367 | label: "Default", | ||
368 | source_range: 59..59, | ||
369 | delete: 59..59, | ||
370 | insert: "Default", | ||
371 | kind: Attribute, | ||
372 | }, | ||
373 | CompletionItem { | ||
374 | label: "Eq", | ||
375 | source_range: 59..59, | ||
376 | delete: 59..59, | ||
377 | insert: "Eq", | ||
378 | kind: Attribute, | ||
379 | }, | ||
380 | CompletionItem { | ||
381 | label: "Hash", | ||
382 | source_range: 59..59, | ||
383 | delete: 59..59, | ||
384 | insert: "Hash", | ||
385 | kind: Attribute, | ||
386 | }, | ||
387 | CompletionItem { | ||
388 | label: "Ord, PartialOrd, Eq", | ||
389 | source_range: 59..59, | ||
390 | delete: 59..59, | ||
391 | insert: "Ord, PartialOrd, Eq", | ||
392 | kind: Attribute, | ||
393 | }, | ||
394 | CompletionItem { | ||
395 | label: "PartialOrd", | ||
396 | source_range: 59..59, | ||
397 | delete: 59..59, | ||
398 | insert: "PartialOrd", | ||
399 | kind: Attribute, | ||
400 | }, | ||
401 | ] | ||
402 | "### | ||
403 | ); | ||
404 | } | ||
405 | |||
406 | #[test] | ||
139 | fn test_attribute_completion() { | 407 | fn test_attribute_completion() { |
140 | assert_debug_snapshot!( | 408 | assert_debug_snapshot!( |
141 | do_attr_completion( | 409 | do_attr_completion( |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 814354ffa..05f825c6f 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -250,6 +250,44 @@ mod tests { | |||
250 | } | 250 | } |
251 | 251 | ||
252 | #[test] | 252 | #[test] |
253 | fn test_union_field_completion() { | ||
254 | assert_debug_snapshot!( | ||
255 | do_ref_completion( | ||
256 | r" | ||
257 | union Un { | ||
258 | field: u8, | ||
259 | other: u16, | ||
260 | } | ||
261 | |||
262 | fn foo(u: Un) { | ||
263 | u.<|> | ||
264 | } | ||
265 | ", | ||
266 | ), | ||
267 | @r###" | ||
268 | [ | ||
269 | CompletionItem { | ||
270 | label: "field", | ||
271 | source_range: 140..140, | ||
272 | delete: 140..140, | ||
273 | insert: "field", | ||
274 | kind: Field, | ||
275 | detail: "u8", | ||
276 | }, | ||
277 | CompletionItem { | ||
278 | label: "other", | ||
279 | source_range: 140..140, | ||
280 | delete: 140..140, | ||
281 | insert: "other", | ||
282 | kind: Field, | ||
283 | detail: "u16", | ||
284 | }, | ||
285 | ] | ||
286 | "### | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
253 | fn test_method_completion() { | 291 | fn test_method_completion() { |
254 | assert_debug_snapshot!( | 292 | assert_debug_snapshot!( |
255 | do_ref_completion( | 293 | do_ref_completion( |
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index dd10f74e6..7fcd22525 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
5 | use rustc_hash::FxHashSet; | ||
5 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
6 | 7 | ||
7 | use crate::completion::{CompletionContext, Completions}; | 8 | use crate::completion::{CompletionContext, Completions}; |
@@ -9,15 +10,29 @@ use crate::completion::{CompletionContext, Completions}; | |||
9 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | let path = match &ctx.path_prefix { | 11 | let path = match &ctx.path_prefix { |
11 | Some(path) => path.clone(), | 12 | Some(path) => path.clone(), |
12 | _ => return, | 13 | None => return, |
13 | }; | 14 | }; |
14 | let def = match ctx.scope().resolve_hir_path(&path) { | 15 | |
15 | Some(PathResolution::Def(def)) => def, | 16 | if ctx.attribute_under_caret.is_some() { |
16 | _ => return, | 17 | return; |
18 | } | ||
19 | |||
20 | let scope = ctx.scope(); | ||
21 | let context_module = scope.module(); | ||
22 | |||
23 | let res = match scope.resolve_hir_path(&path) { | ||
24 | Some(res) => res, | ||
25 | None => return, | ||
17 | }; | 26 | }; |
18 | let context_module = ctx.scope().module(); | 27 | |
19 | match def { | 28 | // Add associated types on type parameters and `Self`. |
20 | hir::ModuleDef::Module(module) => { | 29 | res.assoc_type_shorthand_candidates(ctx.db, |alias| { |
30 | acc.add_type_alias(ctx, alias); | ||
31 | None::<()> | ||
32 | }); | ||
33 | |||
34 | match res { | ||
35 | PathResolution::Def(hir::ModuleDef::Module(module)) => { | ||
21 | let module_scope = module.scope(ctx.db, context_module); | 36 | let module_scope = module.scope(ctx.db, context_module); |
22 | for (name, def) in module_scope { | 37 | for (name, def) in module_scope { |
23 | if ctx.use_item_syntax.is_some() { | 38 | if ctx.use_item_syntax.is_some() { |
@@ -35,7 +50,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
35 | acc.add_resolution(ctx, name.to_string(), &def); | 50 | acc.add_resolution(ctx, name.to_string(), &def); |
36 | } | 51 | } |
37 | } | 52 | } |
38 | hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { | 53 | PathResolution::Def(def @ hir::ModuleDef::Adt(_)) |
54 | | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => { | ||
39 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { | 55 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { |
40 | for variant in e.variants(ctx.db) { | 56 | for variant in e.variants(ctx.db) { |
41 | acc.add_enum_variant(ctx, variant, None); | 57 | acc.add_enum_variant(ctx, variant, None); |
@@ -46,8 +62,10 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
46 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | 62 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), |
47 | _ => unreachable!(), | 63 | _ => unreachable!(), |
48 | }; | 64 | }; |
49 | // Iterate assoc types separately | 65 | |
50 | // FIXME: complete T::AssocType | 66 | // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. |
67 | // (where AssocType is defined on a trait, not an inherent impl) | ||
68 | |||
51 | let krate = ctx.krate; | 69 | let krate = ctx.krate; |
52 | if let Some(krate) = krate { | 70 | if let Some(krate) = krate { |
53 | let traits_in_scope = ctx.scope().traits_in_scope(); | 71 | let traits_in_scope = ctx.scope().traits_in_scope(); |
@@ -65,7 +83,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
65 | None::<()> | 83 | None::<()> |
66 | }); | 84 | }); |
67 | 85 | ||
68 | ty.iterate_impl_items(ctx.db, krate, |item| { | 86 | // Iterate assoc types separately |
87 | ty.iterate_assoc_items(ctx.db, krate, |item| { | ||
69 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 88 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
70 | return None; | 89 | return None; |
71 | } | 90 | } |
@@ -77,7 +96,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
77 | }); | 96 | }); |
78 | } | 97 | } |
79 | } | 98 | } |
80 | hir::ModuleDef::Trait(t) => { | 99 | PathResolution::Def(hir::ModuleDef::Trait(t)) => { |
100 | // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. | ||
81 | for item in t.items(ctx.db) { | 101 | for item in t.items(ctx.db) { |
82 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 102 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
83 | continue; | 103 | continue; |
@@ -91,8 +111,38 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
91 | } | 111 | } |
92 | } | 112 | } |
93 | } | 113 | } |
114 | PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { | ||
115 | if let Some(krate) = ctx.krate { | ||
116 | let ty = match res { | ||
117 | PathResolution::TypeParam(param) => param.ty(ctx.db), | ||
118 | PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), | ||
119 | _ => return, | ||
120 | }; | ||
121 | |||
122 | let traits_in_scope = ctx.scope().traits_in_scope(); | ||
123 | let mut seen = FxHashSet::default(); | ||
124 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | ||
125 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
126 | return None; | ||
127 | } | ||
128 | |||
129 | // We might iterate candidates of a trait multiple times here, so deduplicate | ||
130 | // them. | ||
131 | if seen.insert(item) { | ||
132 | match item { | ||
133 | hir::AssocItem::Function(func) => { | ||
134 | acc.add_function(ctx, func, None); | ||
135 | } | ||
136 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
137 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
138 | } | ||
139 | } | ||
140 | None::<()> | ||
141 | }); | ||
142 | } | ||
143 | } | ||
94 | _ => {} | 144 | _ => {} |
95 | }; | 145 | } |
96 | } | 146 | } |
97 | 147 | ||
98 | #[cfg(test)] | 148 | #[cfg(test)] |
@@ -844,6 +894,211 @@ mod tests { | |||
844 | } | 894 | } |
845 | 895 | ||
846 | #[test] | 896 | #[test] |
897 | fn completes_ty_param_assoc_ty() { | ||
898 | assert_debug_snapshot!( | ||
899 | do_reference_completion( | ||
900 | " | ||
901 | //- /lib.rs | ||
902 | trait Super { | ||
903 | type Ty; | ||
904 | const CONST: u8; | ||
905 | fn func() {} | ||
906 | fn method(&self) {} | ||
907 | } | ||
908 | |||
909 | trait Sub: Super { | ||
910 | type SubTy; | ||
911 | const C2: (); | ||
912 | fn subfunc() {} | ||
913 | fn submethod(&self) {} | ||
914 | } | ||
915 | |||
916 | fn foo<T: Sub>() { | ||
917 | T::<|> | ||
918 | } | ||
919 | " | ||
920 | ), | ||
921 | @r###" | ||
922 | [ | ||
923 | CompletionItem { | ||
924 | label: "C2", | ||
925 | source_range: 219..219, | ||
926 | delete: 219..219, | ||
927 | insert: "C2", | ||
928 | kind: Const, | ||
929 | detail: "const C2: ();", | ||
930 | }, | ||
931 | CompletionItem { | ||
932 | label: "CONST", | ||
933 | source_range: 219..219, | ||
934 | delete: 219..219, | ||
935 | insert: "CONST", | ||
936 | kind: Const, | ||
937 | detail: "const CONST: u8;", | ||
938 | }, | ||
939 | CompletionItem { | ||
940 | label: "SubTy", | ||
941 | source_range: 219..219, | ||
942 | delete: 219..219, | ||
943 | insert: "SubTy", | ||
944 | kind: TypeAlias, | ||
945 | detail: "type SubTy;", | ||
946 | }, | ||
947 | CompletionItem { | ||
948 | label: "Ty", | ||
949 | source_range: 219..219, | ||
950 | delete: 219..219, | ||
951 | insert: "Ty", | ||
952 | kind: TypeAlias, | ||
953 | detail: "type Ty;", | ||
954 | }, | ||
955 | CompletionItem { | ||
956 | label: "func()", | ||
957 | source_range: 219..219, | ||
958 | delete: 219..219, | ||
959 | insert: "func()$0", | ||
960 | kind: Function, | ||
961 | lookup: "func", | ||
962 | detail: "fn func()", | ||
963 | }, | ||
964 | CompletionItem { | ||
965 | label: "method()", | ||
966 | source_range: 219..219, | ||
967 | delete: 219..219, | ||
968 | insert: "method()$0", | ||
969 | kind: Method, | ||
970 | lookup: "method", | ||
971 | detail: "fn method(&self)", | ||
972 | }, | ||
973 | CompletionItem { | ||
974 | label: "subfunc()", | ||
975 | source_range: 219..219, | ||
976 | delete: 219..219, | ||
977 | insert: "subfunc()$0", | ||
978 | kind: Function, | ||
979 | lookup: "subfunc", | ||
980 | detail: "fn subfunc()", | ||
981 | }, | ||
982 | CompletionItem { | ||
983 | label: "submethod()", | ||
984 | source_range: 219..219, | ||
985 | delete: 219..219, | ||
986 | insert: "submethod()$0", | ||
987 | kind: Method, | ||
988 | lookup: "submethod", | ||
989 | detail: "fn submethod(&self)", | ||
990 | }, | ||
991 | ] | ||
992 | "### | ||
993 | ); | ||
994 | } | ||
995 | |||
996 | #[test] | ||
997 | fn completes_self_param_assoc_ty() { | ||
998 | assert_debug_snapshot!( | ||
999 | do_reference_completion( | ||
1000 | " | ||
1001 | //- /lib.rs | ||
1002 | trait Super { | ||
1003 | type Ty; | ||
1004 | const CONST: u8 = 0; | ||
1005 | fn func() {} | ||
1006 | fn method(&self) {} | ||
1007 | } | ||
1008 | |||
1009 | trait Sub: Super { | ||
1010 | type SubTy; | ||
1011 | const C2: () = (); | ||
1012 | fn subfunc() {} | ||
1013 | fn submethod(&self) {} | ||
1014 | } | ||
1015 | |||
1016 | struct Wrap<T>(T); | ||
1017 | impl<T> Super for Wrap<T> {} | ||
1018 | impl<T> Sub for Wrap<T> { | ||
1019 | fn subfunc() { | ||
1020 | // Should be able to assume `Self: Sub + Super` | ||
1021 | Self::<|> | ||
1022 | } | ||
1023 | } | ||
1024 | " | ||
1025 | ), | ||
1026 | @r###" | ||
1027 | [ | ||
1028 | CompletionItem { | ||
1029 | label: "C2", | ||
1030 | source_range: 365..365, | ||
1031 | delete: 365..365, | ||
1032 | insert: "C2", | ||
1033 | kind: Const, | ||
1034 | detail: "const C2: () = ();", | ||
1035 | }, | ||
1036 | CompletionItem { | ||
1037 | label: "CONST", | ||
1038 | source_range: 365..365, | ||
1039 | delete: 365..365, | ||
1040 | insert: "CONST", | ||
1041 | kind: Const, | ||
1042 | detail: "const CONST: u8 = 0;", | ||
1043 | }, | ||
1044 | CompletionItem { | ||
1045 | label: "SubTy", | ||
1046 | source_range: 365..365, | ||
1047 | delete: 365..365, | ||
1048 | insert: "SubTy", | ||
1049 | kind: TypeAlias, | ||
1050 | detail: "type SubTy;", | ||
1051 | }, | ||
1052 | CompletionItem { | ||
1053 | label: "Ty", | ||
1054 | source_range: 365..365, | ||
1055 | delete: 365..365, | ||
1056 | insert: "Ty", | ||
1057 | kind: TypeAlias, | ||
1058 | detail: "type Ty;", | ||
1059 | }, | ||
1060 | CompletionItem { | ||
1061 | label: "func()", | ||
1062 | source_range: 365..365, | ||
1063 | delete: 365..365, | ||
1064 | insert: "func()$0", | ||
1065 | kind: Function, | ||
1066 | lookup: "func", | ||
1067 | detail: "fn func()", | ||
1068 | }, | ||
1069 | CompletionItem { | ||
1070 | label: "method()", | ||
1071 | source_range: 365..365, | ||
1072 | delete: 365..365, | ||
1073 | insert: "method()$0", | ||
1074 | kind: Method, | ||
1075 | lookup: "method", | ||
1076 | detail: "fn method(&self)", | ||
1077 | }, | ||
1078 | CompletionItem { | ||
1079 | label: "subfunc()", | ||
1080 | source_range: 365..365, | ||
1081 | delete: 365..365, | ||
1082 | insert: "subfunc()$0", | ||
1083 | kind: Function, | ||
1084 | lookup: "subfunc", | ||
1085 | detail: "fn subfunc()", | ||
1086 | }, | ||
1087 | CompletionItem { | ||
1088 | label: "submethod()", | ||
1089 | source_range: 365..365, | ||
1090 | delete: 365..365, | ||
1091 | insert: "submethod()$0", | ||
1092 | kind: Method, | ||
1093 | lookup: "submethod", | ||
1094 | detail: "fn submethod(&self)", | ||
1095 | }, | ||
1096 | ] | ||
1097 | "### | ||
1098 | ); | ||
1099 | } | ||
1100 | |||
1101 | #[test] | ||
847 | fn completes_type_alias() { | 1102 | fn completes_type_alias() { |
848 | assert_debug_snapshot!( | 1103 | assert_debug_snapshot!( |
849 | do_reference_completion( | 1104 | do_reference_completion( |
@@ -1075,4 +1330,18 @@ mod tests { | |||
1075 | "### | 1330 | "### |
1076 | ); | 1331 | ); |
1077 | } | 1332 | } |
1333 | |||
1334 | #[test] | ||
1335 | fn dont_complete_attr() { | ||
1336 | assert_debug_snapshot!( | ||
1337 | do_reference_completion( | ||
1338 | r" | ||
1339 | mod foo { pub struct Foo; } | ||
1340 | #[foo::<|>] | ||
1341 | fn f() {} | ||
1342 | " | ||
1343 | ), | ||
1344 | @r###"[]"### | ||
1345 | ) | ||
1346 | } | ||
1078 | } | 1347 | } |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index ee32d1ff6..039df03e0 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -32,7 +32,7 @@ | |||
32 | //! ``` | 32 | //! ``` |
33 | 33 | ||
34 | use hir::{self, Docs, HasSource}; | 34 | use hir::{self, Docs, HasSource}; |
35 | use ra_assists::utils::get_missing_impl_items; | 35 | use ra_assists::utils::get_missing_assoc_items; |
36 | use ra_syntax::{ | 36 | use ra_syntax::{ |
37 | ast::{self, edit, ImplDef}, | 37 | ast::{self, edit, ImplDef}, |
38 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, | 38 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, |
@@ -50,7 +50,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
50 | if let Some((trigger, impl_def)) = completion_match(ctx) { | 50 | if let Some((trigger, impl_def)) = completion_match(ctx) { |
51 | match trigger.kind() { | 51 | match trigger.kind() { |
52 | SyntaxKind::NAME_REF => { | 52 | SyntaxKind::NAME_REF => { |
53 | get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { | 53 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { |
54 | hir::AssocItem::Function(fn_item) => { | 54 | hir::AssocItem::Function(fn_item) => { |
55 | add_function_impl(&trigger, acc, ctx, &fn_item) | 55 | add_function_impl(&trigger, acc, ctx, &fn_item) |
56 | } | 56 | } |
@@ -64,34 +64,40 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
64 | } | 64 | } |
65 | 65 | ||
66 | SyntaxKind::FN_DEF => { | 66 | SyntaxKind::FN_DEF => { |
67 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 67 | for missing_fn in |
68 | |item| match item { | 68 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
69 | hir::AssocItem::Function(fn_item) => Some(fn_item), | 69 | match item { |
70 | _ => None, | 70 | hir::AssocItem::Function(fn_item) => Some(fn_item), |
71 | }, | 71 | _ => None, |
72 | ) { | 72 | } |
73 | }) | ||
74 | { | ||
73 | add_function_impl(&trigger, acc, ctx, &missing_fn); | 75 | add_function_impl(&trigger, acc, ctx, &missing_fn); |
74 | } | 76 | } |
75 | } | 77 | } |
76 | 78 | ||
77 | SyntaxKind::TYPE_ALIAS_DEF => { | 79 | SyntaxKind::TYPE_ALIAS_DEF => { |
78 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 80 | for missing_fn in |
79 | |item| match item { | 81 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
80 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | 82 | match item { |
81 | _ => None, | 83 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), |
82 | }, | 84 | _ => None, |
83 | ) { | 85 | } |
86 | }) | ||
87 | { | ||
84 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); | 88 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); |
85 | } | 89 | } |
86 | } | 90 | } |
87 | 91 | ||
88 | SyntaxKind::CONST_DEF => { | 92 | SyntaxKind::CONST_DEF => { |
89 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 93 | for missing_fn in |
90 | |item| match item { | 94 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
91 | hir::AssocItem::Const(const_item) => Some(const_item), | 95 | match item { |
92 | _ => None, | 96 | hir::AssocItem::Const(const_item) => Some(const_item), |
93 | }, | 97 | _ => None, |
94 | ) { | 98 | } |
99 | }) | ||
100 | { | ||
95 | add_const_impl(&trigger, acc, ctx, &missing_fn); | 101 | add_const_impl(&trigger, acc, ctx, &missing_fn); |
96 | } | 102 | } |
97 | } | 103 | } |
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index 56cd086c6..bd40af1cb 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs | |||
@@ -4,18 +4,23 @@ use hir::ScopeDef; | |||
4 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
5 | 5 | ||
6 | use crate::completion::{CompletionContext, Completions}; | 6 | use crate::completion::{CompletionContext, Completions}; |
7 | use hir::{Adt, ModuleDef}; | 7 | use hir::{Adt, ModuleDef, Type}; |
8 | use ra_syntax::AstNode; | 8 | use ra_syntax::AstNode; |
9 | 9 | ||
10 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
11 | if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const) | 11 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { |
12 | || ctx.record_lit_syntax.is_some() | 12 | return; |
13 | } | ||
14 | if ctx.record_lit_syntax.is_some() | ||
13 | || ctx.record_pat_syntax.is_some() | 15 | || ctx.record_pat_syntax.is_some() |
16 | || ctx.attribute_under_caret.is_some() | ||
14 | { | 17 | { |
15 | return; | 18 | return; |
16 | } | 19 | } |
17 | 20 | ||
18 | complete_enum_variants(acc, ctx); | 21 | if let Some(ty) = &ctx.expected_type { |
22 | complete_enum_variants(acc, ctx, ty); | ||
23 | } | ||
19 | 24 | ||
20 | if ctx.is_pat_binding_or_const { | 25 | if ctx.is_pat_binding_or_const { |
21 | return; | 26 | return; |
@@ -34,26 +39,24 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
34 | }); | 39 | }); |
35 | } | 40 | } |
36 | 41 | ||
37 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) { | 42 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { |
38 | if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) { | 43 | if let Some(Adt::Enum(enum_data)) = ty.as_adt() { |
39 | if let Some(Adt::Enum(enum_data)) = ty.as_adt() { | 44 | let variants = enum_data.variants(ctx.db); |
40 | let variants = enum_data.variants(ctx.db); | 45 | |
41 | 46 | let module = if let Some(module) = ctx.scope().module() { | |
42 | let module = if let Some(module) = ctx.scope().module() { | 47 | // Compute path from the completion site if available. |
43 | // Compute path from the completion site if available. | 48 | module |
44 | module | 49 | } else { |
45 | } else { | 50 | // Otherwise fall back to the enum's definition site. |
46 | // Otherwise fall back to the enum's definition site. | 51 | enum_data.module(ctx.db) |
47 | enum_data.module(ctx.db) | 52 | }; |
48 | }; | 53 | |
49 | 54 | for variant in variants { | |
50 | for variant in variants { | 55 | if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { |
51 | if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { | 56 | // Variants with trivial paths are already added by the existing completion logic, |
52 | // Variants with trivial paths are already added by the existing completion logic, | 57 | // so we should avoid adding these twice |
53 | // so we should avoid adding these twice | 58 | if path.segments.len() > 1 { |
54 | if path.segments.len() > 1 { | 59 | acc.add_qualified_enum_variant(ctx, variant, path); |
55 | acc.add_enum_variant(ctx, variant, Some(path.to_string())); | ||
56 | } | ||
57 | } | 60 | } |
58 | } | 61 | } |
59 | } | 62 | } |
@@ -1173,6 +1176,7 @@ mod tests { | |||
1173 | delete: 248..250, | 1176 | delete: 248..250, |
1174 | insert: "Foo::Bar", | 1177 | insert: "Foo::Bar", |
1175 | kind: EnumVariant, | 1178 | kind: EnumVariant, |
1179 | lookup: "Bar", | ||
1176 | detail: "()", | 1180 | detail: "()", |
1177 | }, | 1181 | }, |
1178 | CompletionItem { | 1182 | CompletionItem { |
@@ -1181,6 +1185,7 @@ mod tests { | |||
1181 | delete: 248..250, | 1185 | delete: 248..250, |
1182 | insert: "Foo::Baz", | 1186 | insert: "Foo::Baz", |
1183 | kind: EnumVariant, | 1187 | kind: EnumVariant, |
1188 | lookup: "Baz", | ||
1184 | detail: "()", | 1189 | detail: "()", |
1185 | }, | 1190 | }, |
1186 | CompletionItem { | 1191 | CompletionItem { |
@@ -1189,6 +1194,7 @@ mod tests { | |||
1189 | delete: 248..250, | 1194 | delete: 248..250, |
1190 | insert: "Foo::Quux", | 1195 | insert: "Foo::Quux", |
1191 | kind: EnumVariant, | 1196 | kind: EnumVariant, |
1197 | lookup: "Quux", | ||
1192 | detail: "()", | 1198 | detail: "()", |
1193 | }, | 1199 | }, |
1194 | ] | 1200 | ] |
@@ -1231,6 +1237,7 @@ mod tests { | |||
1231 | delete: 219..221, | 1237 | delete: 219..221, |
1232 | insert: "Foo::Bar", | 1238 | insert: "Foo::Bar", |
1233 | kind: EnumVariant, | 1239 | kind: EnumVariant, |
1240 | lookup: "Bar", | ||
1234 | detail: "()", | 1241 | detail: "()", |
1235 | }, | 1242 | }, |
1236 | CompletionItem { | 1243 | CompletionItem { |
@@ -1239,6 +1246,7 @@ mod tests { | |||
1239 | delete: 219..221, | 1246 | delete: 219..221, |
1240 | insert: "Foo::Baz", | 1247 | insert: "Foo::Baz", |
1241 | kind: EnumVariant, | 1248 | kind: EnumVariant, |
1249 | lookup: "Baz", | ||
1242 | detail: "()", | 1250 | detail: "()", |
1243 | }, | 1251 | }, |
1244 | CompletionItem { | 1252 | CompletionItem { |
@@ -1247,6 +1255,7 @@ mod tests { | |||
1247 | delete: 219..221, | 1255 | delete: 219..221, |
1248 | insert: "Foo::Quux", | 1256 | insert: "Foo::Quux", |
1249 | kind: EnumVariant, | 1257 | kind: EnumVariant, |
1258 | lookup: "Quux", | ||
1250 | detail: "()", | 1259 | detail: "()", |
1251 | }, | 1260 | }, |
1252 | ] | 1261 | ] |
@@ -1285,6 +1294,7 @@ mod tests { | |||
1285 | delete: 185..186, | 1294 | delete: 185..186, |
1286 | insert: "Foo::Bar", | 1295 | insert: "Foo::Bar", |
1287 | kind: EnumVariant, | 1296 | kind: EnumVariant, |
1297 | lookup: "Bar", | ||
1288 | detail: "()", | 1298 | detail: "()", |
1289 | }, | 1299 | }, |
1290 | CompletionItem { | 1300 | CompletionItem { |
@@ -1293,6 +1303,7 @@ mod tests { | |||
1293 | delete: 185..186, | 1303 | delete: 185..186, |
1294 | insert: "Foo::Baz", | 1304 | insert: "Foo::Baz", |
1295 | kind: EnumVariant, | 1305 | kind: EnumVariant, |
1306 | lookup: "Baz", | ||
1296 | detail: "()", | 1307 | detail: "()", |
1297 | }, | 1308 | }, |
1298 | CompletionItem { | 1309 | CompletionItem { |
@@ -1301,6 +1312,7 @@ mod tests { | |||
1301 | delete: 185..186, | 1312 | delete: 185..186, |
1302 | insert: "Foo::Quux", | 1313 | insert: "Foo::Quux", |
1303 | kind: EnumVariant, | 1314 | kind: EnumVariant, |
1315 | lookup: "Quux", | ||
1304 | detail: "()", | 1316 | detail: "()", |
1305 | }, | 1317 | }, |
1306 | CompletionItem { | 1318 | CompletionItem { |
@@ -1353,10 +1365,25 @@ mod tests { | |||
1353 | delete: 98..99, | 1365 | delete: 98..99, |
1354 | insert: "m::E::V", | 1366 | insert: "m::E::V", |
1355 | kind: EnumVariant, | 1367 | kind: EnumVariant, |
1368 | lookup: "V", | ||
1356 | detail: "()", | 1369 | detail: "()", |
1357 | }, | 1370 | }, |
1358 | ] | 1371 | ] |
1359 | "### | 1372 | "### |
1360 | ) | 1373 | ) |
1361 | } | 1374 | } |
1375 | |||
1376 | #[test] | ||
1377 | fn dont_complete_attr() { | ||
1378 | assert_debug_snapshot!( | ||
1379 | do_reference_completion( | ||
1380 | r" | ||
1381 | struct Foo; | ||
1382 | #[<|>] | ||
1383 | fn f() {} | ||
1384 | " | ||
1385 | ), | ||
1386 | @r###"[]"### | ||
1387 | ) | ||
1388 | } | ||
1362 | } | 1389 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 5f2797e41..dd87bd119 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase; | |||
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
8 | ast, AstNode, | 8 | ast, match_ast, AstNode, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
@@ -26,6 +26,7 @@ pub(crate) struct CompletionContext<'a> { | |||
26 | /// The token before the cursor, in the macro-expanded file. | 26 | /// The token before the cursor, in the macro-expanded file. |
27 | pub(super) token: SyntaxToken, | 27 | pub(super) token: SyntaxToken, |
28 | pub(super) krate: Option<hir::Crate>, | 28 | pub(super) krate: Option<hir::Crate>, |
29 | pub(super) expected_type: Option<Type>, | ||
29 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 30 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
30 | pub(super) function_syntax: Option<ast::FnDef>, | 31 | pub(super) function_syntax: Option<ast::FnDef>, |
31 | pub(super) use_item_syntax: Option<ast::UseItem>, | 32 | pub(super) use_item_syntax: Option<ast::UseItem>, |
@@ -57,7 +58,7 @@ pub(crate) struct CompletionContext<'a> { | |||
57 | pub(super) is_macro_call: bool, | 58 | pub(super) is_macro_call: bool, |
58 | pub(super) is_path_type: bool, | 59 | pub(super) is_path_type: bool, |
59 | pub(super) has_type_args: bool, | 60 | pub(super) has_type_args: bool, |
60 | pub(super) is_attribute: bool, | 61 | pub(super) attribute_under_caret: Option<ast::Attr>, |
61 | } | 62 | } |
62 | 63 | ||
63 | impl<'a> CompletionContext<'a> { | 64 | impl<'a> CompletionContext<'a> { |
@@ -93,6 +94,7 @@ impl<'a> CompletionContext<'a> { | |||
93 | token, | 94 | token, |
94 | offset: position.offset, | 95 | offset: position.offset, |
95 | krate, | 96 | krate, |
97 | expected_type: None, | ||
96 | name_ref_syntax: None, | 98 | name_ref_syntax: None, |
97 | function_syntax: None, | 99 | function_syntax: None, |
98 | use_item_syntax: None, | 100 | use_item_syntax: None, |
@@ -114,7 +116,7 @@ impl<'a> CompletionContext<'a> { | |||
114 | is_path_type: false, | 116 | is_path_type: false, |
115 | has_type_args: false, | 117 | has_type_args: false, |
116 | dot_receiver_is_ambiguous_float_literal: false, | 118 | dot_receiver_is_ambiguous_float_literal: false, |
117 | is_attribute: false, | 119 | attribute_under_caret: None, |
118 | }; | 120 | }; |
119 | 121 | ||
120 | let mut original_file = original_file.syntax().clone(); | 122 | let mut original_file = original_file.syntax().clone(); |
@@ -175,23 +177,31 @@ impl<'a> CompletionContext<'a> { | |||
175 | self.sema.scope_at_offset(&self.token.parent(), self.offset) | 177 | self.sema.scope_at_offset(&self.token.parent(), self.offset) |
176 | } | 178 | } |
177 | 179 | ||
178 | pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> { | ||
179 | for ancestor in node.ancestors() { | ||
180 | if let Some(pat) = ast::Pat::cast(ancestor.clone()) { | ||
181 | return self.sema.type_of_pat(&pat); | ||
182 | } else if let Some(expr) = ast::Expr::cast(ancestor) { | ||
183 | return self.sema.type_of_expr(&expr); | ||
184 | } | ||
185 | } | ||
186 | None | ||
187 | } | ||
188 | |||
189 | fn fill( | 180 | fn fill( |
190 | &mut self, | 181 | &mut self, |
191 | original_file: &SyntaxNode, | 182 | original_file: &SyntaxNode, |
192 | file_with_fake_ident: SyntaxNode, | 183 | file_with_fake_ident: SyntaxNode, |
193 | offset: TextSize, | 184 | offset: TextSize, |
194 | ) { | 185 | ) { |
186 | // FIXME: this is wrong in at least two cases: | ||
187 | // * when there's no token `foo(<|>)` | ||
188 | // * when there is a token, but it happens to have type of it's own | ||
189 | self.expected_type = self | ||
190 | .token | ||
191 | .ancestors() | ||
192 | .find_map(|node| { | ||
193 | let ty = match_ast! { | ||
194 | match node { | ||
195 | ast::Pat(it) => self.sema.type_of_pat(&it), | ||
196 | ast::Expr(it) => self.sema.type_of_expr(&it), | ||
197 | _ => return None, | ||
198 | } | ||
199 | }; | ||
200 | Some(ty) | ||
201 | }) | ||
202 | .flatten(); | ||
203 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | ||
204 | |||
195 | // First, let's try to complete a reference to some declaration. | 205 | // First, let's try to complete a reference to some declaration. |
196 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { | 206 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { |
197 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | 207 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
@@ -309,7 +319,6 @@ impl<'a> CompletionContext<'a> { | |||
309 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | 319 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) |
310 | .is_some(); | 320 | .is_some(); |
311 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); | 321 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); |
312 | self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some(); | ||
313 | 322 | ||
314 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 323 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); |
315 | self.has_type_args = segment.type_arg_list().is_some(); | 324 | self.has_type_args = segment.type_arg_list().is_some(); |
@@ -335,7 +344,7 @@ impl<'a> CompletionContext<'a> { | |||
335 | stmt.syntax().text_range() == name_ref.syntax().text_range(), | 344 | stmt.syntax().text_range() == name_ref.syntax().text_range(), |
336 | ); | 345 | ); |
337 | } | 346 | } |
338 | if let Some(block) = ast::Block::cast(node) { | 347 | if let Some(block) = ast::BlockExpr::cast(node) { |
339 | return Some( | 348 | return Some( |
340 | block.expr().map(|e| e.syntax().text_range()) | 349 | block.expr().map(|e| e.syntax().text_range()) |
341 | == Some(name_ref.syntax().text_range()), | 350 | == Some(name_ref.syntax().text_range()), |
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 7633cd7fd..2edb130cf 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! This modules takes care of rendering various definitions as completion items. | 1 | //! This modules takes care of rendering various definitions as completion items. |
2 | 2 | ||
3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type}; | 3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; |
4 | use ra_syntax::ast::NameOwner; | 4 | use ra_syntax::ast::NameOwner; |
5 | use stdx::SepBy; | 5 | use stdx::SepBy; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
@@ -246,14 +246,37 @@ impl Completions { | |||
246 | .add_to(self); | 246 | .add_to(self); |
247 | } | 247 | } |
248 | 248 | ||
249 | pub(crate) fn add_qualified_enum_variant( | ||
250 | &mut self, | ||
251 | ctx: &CompletionContext, | ||
252 | variant: hir::EnumVariant, | ||
253 | path: ModPath, | ||
254 | ) { | ||
255 | self.add_enum_variant_impl(ctx, variant, None, Some(path)) | ||
256 | } | ||
257 | |||
249 | pub(crate) fn add_enum_variant( | 258 | pub(crate) fn add_enum_variant( |
250 | &mut self, | 259 | &mut self, |
251 | ctx: &CompletionContext, | 260 | ctx: &CompletionContext, |
252 | variant: hir::EnumVariant, | 261 | variant: hir::EnumVariant, |
253 | local_name: Option<String>, | 262 | local_name: Option<String>, |
254 | ) { | 263 | ) { |
264 | self.add_enum_variant_impl(ctx, variant, local_name, None) | ||
265 | } | ||
266 | |||
267 | fn add_enum_variant_impl( | ||
268 | &mut self, | ||
269 | ctx: &CompletionContext, | ||
270 | variant: hir::EnumVariant, | ||
271 | local_name: Option<String>, | ||
272 | path: Option<ModPath>, | ||
273 | ) { | ||
255 | let is_deprecated = is_deprecated(variant, ctx.db); | 274 | let is_deprecated = is_deprecated(variant, ctx.db); |
256 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); | 275 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); |
276 | let qualified_name = match &path { | ||
277 | Some(it) => it.to_string(), | ||
278 | None => name.to_string(), | ||
279 | }; | ||
257 | let detail_types = variant | 280 | let detail_types = variant |
258 | .fields(ctx.db) | 281 | .fields(ctx.db) |
259 | .into_iter() | 282 | .into_iter() |
@@ -271,16 +294,23 @@ impl Completions { | |||
271 | .surround_with("{ ", " }") | 294 | .surround_with("{ ", " }") |
272 | .to_string(), | 295 | .to_string(), |
273 | }; | 296 | }; |
274 | let mut res = | 297 | let mut res = CompletionItem::new( |
275 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) | 298 | CompletionKind::Reference, |
276 | .kind(CompletionItemKind::EnumVariant) | 299 | ctx.source_range(), |
277 | .set_documentation(variant.docs(ctx.db)) | 300 | qualified_name.clone(), |
278 | .set_deprecated(is_deprecated) | 301 | ) |
279 | .detail(detail); | 302 | .kind(CompletionItemKind::EnumVariant) |
303 | .set_documentation(variant.docs(ctx.db)) | ||
304 | .set_deprecated(is_deprecated) | ||
305 | .detail(detail); | ||
306 | |||
307 | if path.is_some() { | ||
308 | res = res.lookup_by(name); | ||
309 | } | ||
280 | 310 | ||
281 | if variant_kind == StructKind::Tuple { | 311 | if variant_kind == StructKind::Tuple { |
282 | let params = Params::Anonymous(variant.fields(ctx.db).len()); | 312 | let params = Params::Anonymous(variant.fields(ctx.db).len()); |
283 | res = res.add_call_parens(ctx, name, params) | 313 | res = res.add_call_parens(ctx, qualified_name, params) |
284 | } | 314 | } |
285 | 315 | ||
286 | res.add_to(self); | 316 | res.add_to(self); |
@@ -349,6 +379,14 @@ impl Builder { | |||
349 | if ctx.use_item_syntax.is_some() || ctx.is_call { | 379 | if ctx.use_item_syntax.is_some() || ctx.is_call { |
350 | return self; | 380 | return self; |
351 | } | 381 | } |
382 | |||
383 | // Don't add parentheses if the expected type is some function reference. | ||
384 | if let Some(ty) = &ctx.expected_type { | ||
385 | if ty.is_fn() { | ||
386 | return self; | ||
387 | } | ||
388 | } | ||
389 | |||
352 | let cap = match ctx.config.snippet_cap { | 390 | let cap = match ctx.config.snippet_cap { |
353 | Some(it) => it, | 391 | Some(it) => it, |
354 | None => return self, | 392 | None => return self, |
@@ -749,6 +787,54 @@ mod tests { | |||
749 | } | 787 | } |
750 | 788 | ||
751 | #[test] | 789 | #[test] |
790 | fn no_call_parens_if_fn_ptr_needed() { | ||
791 | assert_debug_snapshot!( | ||
792 | do_reference_completion( | ||
793 | r" | ||
794 | fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8) {} | ||
795 | |||
796 | struct ManualVtable { | ||
797 | method: fn(u8, u8, u8, u8, u8), | ||
798 | } | ||
799 | |||
800 | fn main() -> ManualVtable { | ||
801 | ManualVtable { | ||
802 | method: some<|> | ||
803 | } | ||
804 | } | ||
805 | " | ||
806 | ), | ||
807 | @r###" | ||
808 | [ | ||
809 | CompletionItem { | ||
810 | label: "ManualVtable", | ||
811 | source_range: 295..299, | ||
812 | delete: 295..299, | ||
813 | insert: "ManualVtable", | ||
814 | kind: Struct, | ||
815 | }, | ||
816 | CompletionItem { | ||
817 | label: "main", | ||
818 | source_range: 295..299, | ||
819 | delete: 295..299, | ||
820 | insert: "main", | ||
821 | kind: Function, | ||
822 | detail: "fn main() -> ManualVtable", | ||
823 | }, | ||
824 | CompletionItem { | ||
825 | label: "somefn", | ||
826 | source_range: 295..299, | ||
827 | delete: 295..299, | ||
828 | insert: "somefn", | ||
829 | kind: Function, | ||
830 | detail: "fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8)", | ||
831 | }, | ||
832 | ] | ||
833 | "### | ||
834 | ); | ||
835 | } | ||
836 | |||
837 | #[test] | ||
752 | fn arg_snippets_for_method_call() { | 838 | fn arg_snippets_for_method_call() { |
753 | assert_debug_snapshot!( | 839 | assert_debug_snapshot!( |
754 | do_reference_completion( | 840 | do_reference_completion( |
@@ -1179,7 +1265,7 @@ mod tests { | |||
1179 | 1265 | ||
1180 | #[test] | 1266 | #[test] |
1181 | fn test_struct_field_completion_in_record_lit() { | 1267 | fn test_struct_field_completion_in_record_lit() { |
1182 | covers!(test_struct_field_completion_in_func_call); | 1268 | covers!(test_struct_field_completion_in_record_lit); |
1183 | assert_debug_snapshot!( | 1269 | assert_debug_snapshot!( |
1184 | do_reference_completion( | 1270 | do_reference_completion( |
1185 | r" | 1271 | r" |
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b5e2785fe..db3907fe6 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -26,6 +26,8 @@ pub struct FunctionSignature { | |||
26 | pub kind: CallableKind, | 26 | pub kind: CallableKind, |
27 | /// Optional visibility | 27 | /// Optional visibility |
28 | pub visibility: Option<String>, | 28 | pub visibility: Option<String>, |
29 | /// Qualifiers like `async`, `unsafe`, ... | ||
30 | pub qualifier: FunctionQualifier, | ||
29 | /// Name of the function | 31 | /// Name of the function |
30 | pub name: Option<String>, | 32 | pub name: Option<String>, |
31 | /// Documentation for the function | 33 | /// Documentation for the function |
@@ -46,6 +48,16 @@ pub struct FunctionSignature { | |||
46 | pub has_self_param: bool, | 48 | pub has_self_param: bool, |
47 | } | 49 | } |
48 | 50 | ||
51 | #[derive(Debug, Default)] | ||
52 | pub struct FunctionQualifier { | ||
53 | // `async` and `const` are mutually exclusive. Do we need to enforcing it here? | ||
54 | pub is_async: bool, | ||
55 | pub is_const: bool, | ||
56 | pub is_unsafe: bool, | ||
57 | /// The string `extern ".."` | ||
58 | pub extern_abi: Option<String>, | ||
59 | } | ||
60 | |||
49 | impl FunctionSignature { | 61 | impl FunctionSignature { |
50 | pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self { | 62 | pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self { |
51 | self.doc = doc; | 63 | self.doc = doc; |
@@ -83,6 +95,8 @@ impl FunctionSignature { | |||
83 | FunctionSignature { | 95 | FunctionSignature { |
84 | kind: CallableKind::StructConstructor, | 96 | kind: CallableKind::StructConstructor, |
85 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 97 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
98 | // Do we need `const`? | ||
99 | qualifier: Default::default(), | ||
86 | name: node.name().map(|n| n.text().to_string()), | 100 | name: node.name().map(|n| n.text().to_string()), |
87 | ret_type: node.name().map(|n| n.text().to_string()), | 101 | ret_type: node.name().map(|n| n.text().to_string()), |
88 | parameters: params, | 102 | parameters: params, |
@@ -128,6 +142,8 @@ impl FunctionSignature { | |||
128 | FunctionSignature { | 142 | FunctionSignature { |
129 | kind: CallableKind::VariantConstructor, | 143 | kind: CallableKind::VariantConstructor, |
130 | visibility: None, | 144 | visibility: None, |
145 | // Do we need `const`? | ||
146 | qualifier: Default::default(), | ||
131 | name: Some(name), | 147 | name: Some(name), |
132 | ret_type: None, | 148 | ret_type: None, |
133 | parameters: params, | 149 | parameters: params, |
@@ -151,6 +167,7 @@ impl FunctionSignature { | |||
151 | FunctionSignature { | 167 | FunctionSignature { |
152 | kind: CallableKind::Macro, | 168 | kind: CallableKind::Macro, |
153 | visibility: None, | 169 | visibility: None, |
170 | qualifier: Default::default(), | ||
154 | name: node.name().map(|n| n.text().to_string()), | 171 | name: node.name().map(|n| n.text().to_string()), |
155 | ret_type: None, | 172 | ret_type: None, |
156 | parameters: params, | 173 | parameters: params, |
@@ -223,6 +240,12 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
223 | FunctionSignature { | 240 | FunctionSignature { |
224 | kind: CallableKind::Function, | 241 | kind: CallableKind::Function, |
225 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 242 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
243 | qualifier: FunctionQualifier { | ||
244 | is_async: node.async_token().is_some(), | ||
245 | is_const: node.const_token().is_some(), | ||
246 | is_unsafe: node.unsafe_token().is_some(), | ||
247 | extern_abi: node.abi().map(|n| n.to_string()), | ||
248 | }, | ||
226 | name: node.name().map(|n| n.text().to_string()), | 249 | name: node.name().map(|n| n.text().to_string()), |
227 | ret_type: node | 250 | ret_type: node |
228 | .ret_type() | 251 | .ret_type() |
@@ -246,6 +269,23 @@ impl Display for FunctionSignature { | |||
246 | write!(f, "{} ", t)?; | 269 | write!(f, "{} ", t)?; |
247 | } | 270 | } |
248 | 271 | ||
272 | if self.qualifier.is_async { | ||
273 | write!(f, "async ")?; | ||
274 | } | ||
275 | |||
276 | if self.qualifier.is_const { | ||
277 | write!(f, "const ")?; | ||
278 | } | ||
279 | |||
280 | if self.qualifier.is_unsafe { | ||
281 | write!(f, "unsafe ")?; | ||
282 | } | ||
283 | |||
284 | if let Some(extern_abi) = &self.qualifier.extern_abi { | ||
285 | // Keyword `extern` is included in the string. | ||
286 | write!(f, "{} ", extern_abi)?; | ||
287 | } | ||
288 | |||
249 | if let Some(name) = &self.name { | 289 | if let Some(name) = &self.name { |
250 | match self.kind { | 290 | match self.kind { |
251 | CallableKind::Function => write!(f, "fn {}", name)?, | 291 | CallableKind::Function => write!(f, "fn {}", name)?, |
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index 914a8b471..de35c6711 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs | |||
@@ -376,16 +376,20 @@ impl ToNav for hir::Local { | |||
376 | impl ToNav for hir::TypeParam { | 376 | impl ToNav for hir::TypeParam { |
377 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 377 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
378 | let src = self.source(db); | 378 | let src = self.source(db); |
379 | let range = match src.value { | 379 | let full_range = match &src.value { |
380 | Either::Left(it) => it.syntax().text_range(), | 380 | Either::Left(it) => it.syntax().text_range(), |
381 | Either::Right(it) => it.syntax().text_range(), | 381 | Either::Right(it) => it.syntax().text_range(), |
382 | }; | 382 | }; |
383 | let focus_range = match &src.value { | ||
384 | Either::Left(_) => None, | ||
385 | Either::Right(it) => it.name().map(|it| it.syntax().text_range()), | ||
386 | }; | ||
383 | NavigationTarget { | 387 | NavigationTarget { |
384 | file_id: src.file_id.original_file(db), | 388 | file_id: src.file_id.original_file(db), |
385 | name: self.name(db).to_string().into(), | 389 | name: self.name(db).to_string().into(), |
386 | kind: TYPE_PARAM, | 390 | kind: TYPE_PARAM, |
387 | full_range: range, | 391 | full_range, |
388 | focus_range: None, | 392 | focus_range, |
389 | container_name: None, | 393 | container_name: None, |
390 | description: None, | 394 | description: None, |
391 | docs: None, | 395 | docs: None, |
diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs index 4b081bf6c..d37260e96 100644 --- a/crates/ra_ide/src/display/short_label.rs +++ b/crates/ra_ide/src/display/short_label.rs | |||
@@ -33,7 +33,11 @@ impl ShortLabel for ast::EnumDef { | |||
33 | 33 | ||
34 | impl ShortLabel for ast::TraitDef { | 34 | impl ShortLabel for ast::TraitDef { |
35 | fn short_label(&self) -> Option<String> { | 35 | fn short_label(&self) -> Option<String> { |
36 | short_label_from_node(self, "trait ") | 36 | if self.unsafe_token().is_some() { |
37 | short_label_from_node(self, "unsafe trait ") | ||
38 | } else { | ||
39 | short_label_from_node(self, "trait ") | ||
40 | } | ||
37 | } | 41 | } |
38 | } | 42 | } |
39 | 43 | ||
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs index 4379005aa..8657377de 100644 --- a/crates/ra_ide/src/folding_ranges.rs +++ b/crates/ra_ide/src/folding_ranges.rs | |||
@@ -88,7 +88,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
88 | | ITEM_LIST | 88 | | ITEM_LIST |
89 | | EXTERN_ITEM_LIST | 89 | | EXTERN_ITEM_LIST |
90 | | USE_TREE_LIST | 90 | | USE_TREE_LIST |
91 | | BLOCK | 91 | | BLOCK_EXPR |
92 | | MATCH_ARM_LIST | 92 | | MATCH_ARM_LIST |
93 | | ENUM_VARIANT_LIST | 93 | | ENUM_VARIANT_LIST |
94 | | TOKEN_TREE => Some(FoldKind::Block), | 94 | | TOKEN_TREE => Some(FoldKind::Block), |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 1dfca819d..150895abb 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -244,6 +244,39 @@ mod tests { | |||
244 | } | 244 | } |
245 | 245 | ||
246 | #[test] | 246 | #[test] |
247 | fn goto_def_for_use_alias() { | ||
248 | covers!(ra_ide_db::goto_def_for_use_alias); | ||
249 | check_goto( | ||
250 | " | ||
251 | //- /lib.rs | ||
252 | use foo as bar<|>; | ||
253 | |||
254 | |||
255 | //- /foo/lib.rs | ||
256 | #[macro_export] | ||
257 | macro_rules! foo { () => { () } }", | ||
258 | "SOURCE_FILE FileId(2) 0..50", | ||
259 | "#[macro_export]\nmacro_rules! foo { () => { () } }\n", | ||
260 | ); | ||
261 | } | ||
262 | |||
263 | #[test] | ||
264 | fn goto_def_for_use_alias_foo_macro() { | ||
265 | check_goto( | ||
266 | " | ||
267 | //- /lib.rs | ||
268 | use foo::foo as bar<|>; | ||
269 | |||
270 | //- /foo/lib.rs | ||
271 | #[macro_export] | ||
272 | macro_rules! foo { () => { () } } | ||
273 | ", | ||
274 | "foo MACRO_CALL FileId(2) 0..49 29..32", | ||
275 | "#[macro_export]\nmacro_rules! foo { () => { () } }|foo", | ||
276 | ); | ||
277 | } | ||
278 | |||
279 | #[test] | ||
247 | fn goto_def_for_macros_in_use_tree() { | 280 | fn goto_def_for_macros_in_use_tree() { |
248 | check_goto( | 281 | check_goto( |
249 | " | 282 | " |
@@ -754,14 +787,14 @@ mod tests { | |||
754 | #[test] | 787 | #[test] |
755 | fn goto_for_type_param() { | 788 | fn goto_for_type_param() { |
756 | check_goto( | 789 | check_goto( |
757 | " | 790 | r#" |
758 | //- /lib.rs | 791 | //- /lib.rs |
759 | struct Foo<T> { | 792 | struct Foo<T: Clone> { |
760 | t: <|>T, | 793 | t: <|>T, |
761 | } | 794 | } |
762 | ", | 795 | "#, |
763 | "T TYPE_PARAM FileId(1) 11..12", | 796 | "T TYPE_PARAM FileId(1) 11..19 11..12", |
764 | "T", | 797 | "T: Clone|T", |
765 | ); | 798 | ); |
766 | } | 799 | } |
767 | 800 | ||
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 58c799eca..54d318858 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -844,4 +844,40 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
844 | &["fn foo()\n```\n\n<- `\u{3000}` here"], | 844 | &["fn foo()\n```\n\n<- `\u{3000}` here"], |
845 | ); | 845 | ); |
846 | } | 846 | } |
847 | |||
848 | #[test] | ||
849 | fn test_hover_function_show_qualifiers() { | ||
850 | check_hover_result( | ||
851 | " | ||
852 | //- /lib.rs | ||
853 | async fn foo<|>() {} | ||
854 | ", | ||
855 | &["async fn foo()"], | ||
856 | ); | ||
857 | check_hover_result( | ||
858 | " | ||
859 | //- /lib.rs | ||
860 | pub const unsafe fn foo<|>() {} | ||
861 | ", | ||
862 | &["pub const unsafe fn foo()"], | ||
863 | ); | ||
864 | check_hover_result( | ||
865 | r#" | ||
866 | //- /lib.rs | ||
867 | pub(crate) async unsafe extern "C" fn foo<|>() {} | ||
868 | "#, | ||
869 | &[r#"pub(crate) async unsafe extern "C" fn foo()"#], | ||
870 | ); | ||
871 | } | ||
872 | |||
873 | #[test] | ||
874 | fn test_hover_trait_show_qualifiers() { | ||
875 | check_hover_result( | ||
876 | " | ||
877 | //- /lib.rs | ||
878 | unsafe trait foo<|>() {} | ||
879 | ", | ||
880 | &["unsafe trait foo"], | ||
881 | ); | ||
882 | } | ||
847 | } | 883 | } |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 98483df32..b391f903a 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -9,6 +9,7 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{FileId, FunctionSignature}; | 11 | use crate::{FileId, FunctionSignature}; |
12 | use stdx::to_lower_snake_case; | ||
12 | 13 | ||
13 | #[derive(Clone, Debug, PartialEq, Eq)] | 14 | #[derive(Clone, Debug, PartialEq, Eq)] |
14 | pub struct InlayHintsConfig { | 15 | pub struct InlayHintsConfig { |
@@ -144,7 +145,7 @@ fn get_param_name_hints( | |||
144 | .iter() | 145 | .iter() |
145 | .skip(n_params_to_skip) | 146 | .skip(n_params_to_skip) |
146 | .zip(args) | 147 | .zip(args) |
147 | .filter(|(param, arg)| should_show_param_hint(&fn_signature, param, &arg)) | 148 | .filter(|(param, arg)| should_show_param_name_hint(sema, &fn_signature, param, &arg)) |
148 | .map(|(param_name, arg)| InlayHint { | 149 | .map(|(param_name, arg)| InlayHint { |
149 | range: arg.syntax().text_range(), | 150 | range: arg.syntax().text_range(), |
150 | kind: InlayKind::ParameterHint, | 151 | kind: InlayKind::ParameterHint, |
@@ -181,7 +182,7 @@ fn get_bind_pat_hints( | |||
181 | 182 | ||
182 | fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ty: &Type) -> bool { | 183 | fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ty: &Type) -> bool { |
183 | if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { | 184 | if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { |
184 | let pat_text = bind_pat.syntax().to_string(); | 185 | let pat_text = bind_pat.to_string(); |
185 | enum_data | 186 | enum_data |
186 | .variants(db) | 187 | .variants(db) |
187 | .into_iter() | 188 | .into_iter() |
@@ -198,7 +199,7 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ | |||
198 | } | 199 | } |
199 | 200 | ||
200 | if let Some(Adt::Struct(s)) = pat_ty.as_adt() { | 201 | if let Some(Adt::Struct(s)) = pat_ty.as_adt() { |
201 | if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.syntax().to_string() { | 202 | if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() { |
202 | return true; | 203 | return true; |
203 | } | 204 | } |
204 | } | 205 | } |
@@ -230,15 +231,16 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ | |||
230 | false | 231 | false |
231 | } | 232 | } |
232 | 233 | ||
233 | fn should_show_param_hint( | 234 | fn should_show_param_name_hint( |
235 | sema: &Semantics<RootDatabase>, | ||
234 | fn_signature: &FunctionSignature, | 236 | fn_signature: &FunctionSignature, |
235 | param_name: &str, | 237 | param_name: &str, |
236 | argument: &ast::Expr, | 238 | argument: &ast::Expr, |
237 | ) -> bool { | 239 | ) -> bool { |
240 | let param_name = param_name.trim_start_matches('_'); | ||
238 | if param_name.is_empty() | 241 | if param_name.is_empty() |
239 | || is_argument_similar_to_param(argument, param_name) | 242 | || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) |
240 | || Some(param_name.trim_start_matches('_')) | 243 | || is_argument_similar_to_param_name(sema, argument, param_name) |
241 | == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) | ||
242 | { | 244 | { |
243 | return false; | 245 | return false; |
244 | } | 246 | } |
@@ -254,20 +256,42 @@ fn should_show_param_hint( | |||
254 | parameters_len != 1 || !is_obvious_param(param_name) | 256 | parameters_len != 1 || !is_obvious_param(param_name) |
255 | } | 257 | } |
256 | 258 | ||
257 | fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { | 259 | fn is_argument_similar_to_param_name( |
258 | let argument_string = remove_ref(argument.clone()).syntax().to_string(); | 260 | sema: &Semantics<RootDatabase>, |
259 | let param_name = param_name.trim_start_matches('_'); | 261 | argument: &ast::Expr, |
260 | let argument_string = argument_string.trim_start_matches('_'); | 262 | param_name: &str, |
261 | argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name) | 263 | ) -> bool { |
264 | if is_enum_name_similar_to_param_name(sema, argument, param_name) { | ||
265 | return true; | ||
266 | } | ||
267 | match get_string_representation(argument) { | ||
268 | None => false, | ||
269 | Some(repr) => { | ||
270 | let argument_string = repr.trim_start_matches('_'); | ||
271 | argument_string.starts_with(param_name) || argument_string.ends_with(param_name) | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | |||
276 | fn is_enum_name_similar_to_param_name( | ||
277 | sema: &Semantics<RootDatabase>, | ||
278 | argument: &ast::Expr, | ||
279 | param_name: &str, | ||
280 | ) -> bool { | ||
281 | match sema.type_of_expr(argument).and_then(|t| t.as_adt()) { | ||
282 | Some(Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name, | ||
283 | _ => false, | ||
284 | } | ||
262 | } | 285 | } |
263 | 286 | ||
264 | fn remove_ref(expr: ast::Expr) -> ast::Expr { | 287 | fn get_string_representation(expr: &ast::Expr) -> Option<String> { |
265 | if let ast::Expr::RefExpr(ref_expr) = &expr { | 288 | match expr { |
266 | if let Some(inner) = ref_expr.expr() { | 289 | ast::Expr::MethodCallExpr(method_call_expr) => { |
267 | return inner; | 290 | Some(method_call_expr.name_ref()?.to_string()) |
268 | } | 291 | } |
292 | ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?), | ||
293 | _ => Some(expr.to_string()), | ||
269 | } | 294 | } |
270 | expr | ||
271 | } | 295 | } |
272 | 296 | ||
273 | fn is_obvious_param(param_name: &str) -> bool { | 297 | fn is_obvious_param(param_name: &str) -> bool { |
@@ -1073,6 +1097,12 @@ struct TestVarContainer { | |||
1073 | test_var: i32, | 1097 | test_var: i32, |
1074 | } | 1098 | } |
1075 | 1099 | ||
1100 | impl TestVarContainer { | ||
1101 | fn test_var(&self) -> i32 { | ||
1102 | self.test_var | ||
1103 | } | ||
1104 | } | ||
1105 | |||
1076 | struct Test {} | 1106 | struct Test {} |
1077 | 1107 | ||
1078 | impl Test { | 1108 | impl Test { |
@@ -1098,10 +1128,15 @@ struct Param {} | |||
1098 | fn different_order(param: &Param) {} | 1128 | fn different_order(param: &Param) {} |
1099 | fn different_order_mut(param: &mut Param) {} | 1129 | fn different_order_mut(param: &mut Param) {} |
1100 | fn has_underscore(_param: bool) {} | 1130 | fn has_underscore(_param: bool) {} |
1131 | fn enum_matches_param_name(completion_kind: CompletionKind) {} | ||
1101 | 1132 | ||
1102 | fn twiddle(twiddle: bool) {} | 1133 | fn twiddle(twiddle: bool) {} |
1103 | fn doo(_doo: bool) {} | 1134 | fn doo(_doo: bool) {} |
1104 | 1135 | ||
1136 | enum CompletionKind { | ||
1137 | Keyword, | ||
1138 | } | ||
1139 | |||
1105 | fn main() { | 1140 | fn main() { |
1106 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; | 1141 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; |
1107 | let test: Test = Test {}; | 1142 | let test: Test = Test {}; |
@@ -1114,18 +1149,21 @@ fn main() { | |||
1114 | let test_var: i32 = 55; | 1149 | let test_var: i32 = 55; |
1115 | test_processed.no_hints_expected(22, test_var); | 1150 | test_processed.no_hints_expected(22, test_var); |
1116 | test_processed.no_hints_expected(33, container.test_var); | 1151 | test_processed.no_hints_expected(33, container.test_var); |
1152 | test_processed.no_hints_expected(44, container.test_var()); | ||
1117 | test_processed.frob(false); | 1153 | test_processed.frob(false); |
1118 | 1154 | ||
1119 | twiddle(true); | 1155 | twiddle(true); |
1120 | doo(true); | 1156 | doo(true); |
1121 | 1157 | ||
1122 | let param_begin: Param = Param {}; | 1158 | let mut param_begin: Param = Param {}; |
1123 | different_order(¶m_begin); | 1159 | different_order(¶m_begin); |
1124 | different_order(&mut param_begin); | 1160 | different_order(&mut param_begin); |
1125 | 1161 | ||
1126 | let param: bool = true; | 1162 | let param: bool = true; |
1127 | has_underscore(param); | 1163 | has_underscore(param); |
1128 | 1164 | ||
1165 | enum_matches_param_name(CompletionKind::Keyword); | ||
1166 | |||
1129 | let a: f64 = 7.0; | 1167 | let a: f64 = 7.0; |
1130 | let b: f64 = 4.0; | 1168 | let b: f64 = 4.0; |
1131 | let _: f64 = a.div_euclid(b); | 1169 | let _: f64 = a.div_euclid(b); |
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index fde0bfa98..63fd6b3e4 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs | |||
@@ -129,8 +129,10 @@ fn has_comma_after(node: &SyntaxNode) -> bool { | |||
129 | } | 129 | } |
130 | 130 | ||
131 | fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> { | 131 | fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> { |
132 | let block = ast::Block::cast(token.parent())?; | 132 | let block_expr = ast::BlockExpr::cast(token.parent())?; |
133 | let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?; | 133 | if !block_expr.is_standalone() { |
134 | return None; | ||
135 | } | ||
134 | let expr = extract_trivial_expression(&block_expr)?; | 136 | let expr = extract_trivial_expression(&block_expr)?; |
135 | 137 | ||
136 | let block_range = block_expr.syntax().text_range(); | 138 | let block_range = block_expr.syntax().text_range(); |
@@ -662,4 +664,67 @@ fn main() { | |||
662 | ", | 664 | ", |
663 | ) | 665 | ) |
664 | } | 666 | } |
667 | |||
668 | #[test] | ||
669 | fn join_lines_mandatory_blocks_block() { | ||
670 | check_join_lines( | ||
671 | r" | ||
672 | <|>fn foo() { | ||
673 | 92 | ||
674 | } | ||
675 | ", | ||
676 | r" | ||
677 | <|>fn foo() { 92 | ||
678 | } | ||
679 | ", | ||
680 | ); | ||
681 | |||
682 | check_join_lines( | ||
683 | r" | ||
684 | fn foo() { | ||
685 | <|>if true { | ||
686 | 92 | ||
687 | } | ||
688 | } | ||
689 | ", | ||
690 | r" | ||
691 | fn foo() { | ||
692 | <|>if true { 92 | ||
693 | } | ||
694 | } | ||
695 | ", | ||
696 | ); | ||
697 | |||
698 | check_join_lines( | ||
699 | r" | ||
700 | fn foo() { | ||
701 | <|>loop { | ||
702 | 92 | ||
703 | } | ||
704 | } | ||
705 | ", | ||
706 | r" | ||
707 | fn foo() { | ||
708 | <|>loop { 92 | ||
709 | } | ||
710 | } | ||
711 | ", | ||
712 | ); | ||
713 | |||
714 | check_join_lines( | ||
715 | r" | ||
716 | fn foo() { | ||
717 | <|>unsafe { | ||
718 | 92 | ||
719 | } | ||
720 | } | ||
721 | ", | ||
722 | r" | ||
723 | fn foo() { | ||
724 | <|>unsafe { 92 | ||
725 | } | ||
726 | } | ||
727 | ", | ||
728 | ); | ||
729 | } | ||
665 | } | 730 | } |
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index bea30fe2a..51ca4dde3 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs | |||
@@ -11,4 +11,6 @@ test_utils::marks!( | |||
11 | self_fulfilling_completion | 11 | self_fulfilling_completion |
12 | test_struct_field_completion_in_func_call | 12 | test_struct_field_completion_in_func_call |
13 | test_struct_field_completion_in_record_lit | 13 | test_struct_field_completion_in_record_lit |
14 | test_rename_struct_field_for_shorthand | ||
15 | test_rename_local_for_field_shorthand | ||
14 | ); | 16 | ); |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index fd17bc9f2..916edaef2 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -7,14 +7,13 @@ use ra_syntax::{ | |||
7 | algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, | 7 | algo::find_node_at_offset, ast, lex_single_valid_syntax_kind, AstNode, SyntaxKind, SyntaxNode, |
8 | }; | 8 | }; |
9 | use ra_text_edit::TextEdit; | 9 | use ra_text_edit::TextEdit; |
10 | use test_utils::tested_by; | ||
10 | 11 | ||
11 | use crate::{ | 12 | use crate::{ |
12 | FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, SourceChange, | 13 | references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, |
13 | SourceFileEdit, TextRange, | 14 | SourceChange, SourceFileEdit, TextRange, |
14 | }; | 15 | }; |
15 | 16 | ||
16 | use super::find_all_refs; | ||
17 | |||
18 | pub(crate) fn rename( | 17 | pub(crate) fn rename( |
19 | db: &RootDatabase, | 18 | db: &RootDatabase, |
20 | position: FilePosition, | 19 | position: FilePosition, |
@@ -52,11 +51,13 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil | |||
52 | let file_id = reference.file_range.file_id; | 51 | let file_id = reference.file_range.file_id; |
53 | let range = match reference.kind { | 52 | let range = match reference.kind { |
54 | ReferenceKind::FieldShorthandForField => { | 53 | ReferenceKind::FieldShorthandForField => { |
54 | tested_by!(test_rename_struct_field_for_shorthand); | ||
55 | replacement_text.push_str(new_name); | 55 | replacement_text.push_str(new_name); |
56 | replacement_text.push_str(": "); | 56 | replacement_text.push_str(": "); |
57 | TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) | 57 | TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) |
58 | } | 58 | } |
59 | ReferenceKind::FieldShorthandForLocal => { | 59 | ReferenceKind::FieldShorthandForLocal => { |
60 | tested_by!(test_rename_local_for_field_shorthand); | ||
60 | replacement_text.push_str(": "); | 61 | replacement_text.push_str(": "); |
61 | replacement_text.push_str(new_name); | 62 | replacement_text.push_str(new_name); |
62 | TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) | 63 | TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) |
@@ -147,7 +148,7 @@ fn rename_reference( | |||
147 | mod tests { | 148 | mod tests { |
148 | use insta::assert_debug_snapshot; | 149 | use insta::assert_debug_snapshot; |
149 | use ra_text_edit::TextEditBuilder; | 150 | use ra_text_edit::TextEditBuilder; |
150 | use test_utils::assert_eq_text; | 151 | use test_utils::{assert_eq_text, covers}; |
151 | 152 | ||
152 | use crate::{ | 153 | use crate::{ |
153 | mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, | 154 | mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, |
@@ -379,6 +380,7 @@ mod tests { | |||
379 | 380 | ||
380 | #[test] | 381 | #[test] |
381 | fn test_rename_struct_field_for_shorthand() { | 382 | fn test_rename_struct_field_for_shorthand() { |
383 | covers!(test_rename_struct_field_for_shorthand); | ||
382 | test_rename( | 384 | test_rename( |
383 | r#" | 385 | r#" |
384 | struct Foo { | 386 | struct Foo { |
@@ -408,6 +410,7 @@ mod tests { | |||
408 | 410 | ||
409 | #[test] | 411 | #[test] |
410 | fn test_rename_local_for_field_shorthand() { | 412 | fn test_rename_local_for_field_shorthand() { |
413 | covers!(test_rename_local_for_field_shorthand); | ||
411 | test_rename( | 414 | test_rename( |
412 | r#" | 415 | r#" |
413 | struct Foo { | 416 | struct Foo { |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 38637c19c..fa8a9d92c 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -9,6 +9,7 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::FileId; | 11 | use crate::FileId; |
12 | use ast::DocCommentsOwner; | ||
12 | use std::fmt::Display; | 13 | use std::fmt::Display; |
13 | 14 | ||
14 | #[derive(Debug)] | 15 | #[derive(Debug)] |
@@ -37,6 +38,7 @@ pub enum RunnableKind { | |||
37 | Test { test_id: TestId, attr: TestAttr }, | 38 | Test { test_id: TestId, attr: TestAttr }, |
38 | TestMod { path: String }, | 39 | TestMod { path: String }, |
39 | Bench { test_id: TestId }, | 40 | Bench { test_id: TestId }, |
41 | DocTest { test_id: TestId }, | ||
40 | Bin, | 42 | Bin, |
41 | } | 43 | } |
42 | 44 | ||
@@ -81,6 +83,8 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
81 | RunnableKind::Test { test_id, attr } | 83 | RunnableKind::Test { test_id, attr } |
82 | } else if fn_def.has_atom_attr("bench") { | 84 | } else if fn_def.has_atom_attr("bench") { |
83 | RunnableKind::Bench { test_id } | 85 | RunnableKind::Bench { test_id } |
86 | } else if has_doc_test(&fn_def) { | ||
87 | RunnableKind::DocTest { test_id } | ||
84 | } else { | 88 | } else { |
85 | return None; | 89 | return None; |
86 | } | 90 | } |
@@ -117,6 +121,10 @@ fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool { | |||
117 | .any(|attribute_text| attribute_text.contains("test")) | 121 | .any(|attribute_text| attribute_text.contains("test")) |
118 | } | 122 | } |
119 | 123 | ||
124 | fn has_doc_test(fn_def: &ast::FnDef) -> bool { | ||
125 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | ||
126 | } | ||
127 | |||
120 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | 128 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { |
121 | let has_test_function = module | 129 | let has_test_function = module |
122 | .item_list()? | 130 | .item_list()? |
@@ -195,6 +203,41 @@ mod tests { | |||
195 | } | 203 | } |
196 | 204 | ||
197 | #[test] | 205 | #[test] |
206 | fn test_runnables_doc_test() { | ||
207 | let (analysis, pos) = analysis_and_position( | ||
208 | r#" | ||
209 | //- /lib.rs | ||
210 | <|> //empty | ||
211 | fn main() {} | ||
212 | |||
213 | /// ``` | ||
214 | /// let x = 5; | ||
215 | /// ``` | ||
216 | fn foo() {} | ||
217 | "#, | ||
218 | ); | ||
219 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
220 | assert_debug_snapshot!(&runnables, | ||
221 | @r###" | ||
222 | [ | ||
223 | Runnable { | ||
224 | range: 1..21, | ||
225 | kind: Bin, | ||
226 | }, | ||
227 | Runnable { | ||
228 | range: 22..64, | ||
229 | kind: DocTest { | ||
230 | test_id: Path( | ||
231 | "foo", | ||
232 | ), | ||
233 | }, | ||
234 | }, | ||
235 | ] | ||
236 | "### | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
198 | fn test_runnables_module() { | 241 | fn test_runnables_module() { |
199 | let (analysis, pos) = analysis_and_position( | 242 | let (analysis, pos) = analysis_and_position( |
200 | r#" | 243 | r#" |
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html index 6ec13bd80..ea026d7a0 100644 --- a/crates/ra_ide/src/snapshots/highlight_injection.html +++ b/crates/ra_ide/src/snapshots/highlight_injection.html | |||
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
20 | .macro { color: #94BFF3; } | 20 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 21 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 22 | .variable { color: #DCDCCC; } |
23 | .format_specifier { color: #CC696B; } | ||
23 | .mutable { text-decoration: underline; } | 24 | .mutable { text-decoration: underline; } |
24 | 25 | ||
25 | .keyword { color: #F0DFAF; font-weight: bold; } | 26 | .keyword { color: #F0DFAF; font-weight: bold; } |
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index 433f2e0c5..de06daf72 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html | |||
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
20 | .macro { color: #94BFF3; } | 20 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 21 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 22 | .variable { color: #DCDCCC; } |
23 | .format_specifier { color: #CC696B; } | ||
23 | .mutable { text-decoration: underline; } | 24 | .mutable { text-decoration: underline; } |
24 | 25 | ||
25 | .keyword { color: #F0DFAF; font-weight: bold; } | 26 | .keyword { color: #F0DFAF; font-weight: bold; } |
@@ -40,43 +41,43 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
40 | <span class="keyword">fn</span> <span class="function declaration">main</span>() { | 41 | <span class="keyword">fn</span> <span class="function declaration">main</span>() { |
41 | <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> | 42 | <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> |
42 | <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// => "Hello"</span> | 43 | <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// => "Hello"</span> |
43 | <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// => "Hello, world!"</span> | 44 | <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// => "Hello, world!"</span> |
44 | <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>); <span class="comment">// => "The number is 1"</span> | 45 | <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>); <span class="comment">// => "The number is 1"</span> |
45 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">?</span><span class="attribute">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// => "(3, 4)"</span> | 46 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// => "(3, 4)"</span> |
46 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">value</span><span class="attribute">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>); <span class="comment">// => "4"</span> | 47 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>); <span class="comment">// => "4"</span> |
47 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "1 2"</span> | 48 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "1 2"</span> |
48 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>); <span class="comment">// => "0042" with leading zerosV</span> | 49 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>); <span class="comment">// => "0042" with leading zerosV</span> |
49 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "2 1 1 2"</span> | 50 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "2 1 1 2"</span> |
50 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">argument</span><span class="attribute">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// => "test"</span> | 51 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// => "test"</span> |
51 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// => "2 1"</span> | 52 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// => "2 1"</span> |
52 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">a</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">c</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">b</span><span class="attribute">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// => "a 3 b"</span> | 53 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// => "a 3 b"</span> |
53 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 54 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
54 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); | 55 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); |
55 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); | 56 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); |
56 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="variable">width</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>); | 57 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>); |
57 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute"><</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 58 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
58 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">-</span><span class="attribute"><</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 59 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
59 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">^</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 60 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
60 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">></span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 61 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
61 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">+</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); | 62 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); |
62 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>); | 63 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>); |
63 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); | 64 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); |
64 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>); | 65 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>); |
65 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>); | 66 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>); |
66 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); | 67 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); |
67 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); | 68 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); |
68 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); | 69 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); |
69 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); | 70 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); |
70 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); | 71 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); |
71 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="variable">number</span><span class="attribute">:</span><span class="attribute">.</span><span class="variable">prec</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>); | 72 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>); |
72 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>); | 73 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>); |
73 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); | 74 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); |
74 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">></span><span class="numeric_literal">8</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); | 75 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); |
75 | <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>); | 76 | <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>); |
76 | <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>); | 77 | <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>); |
77 | 78 | ||
78 | <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); | 79 | <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); |
79 | 80 | ||
80 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">\x41</span><span class="attribute">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>); | 81 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>); |
81 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">ничоси</span><span class="attribute">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>); | 82 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>); |
82 | }</code></pre> \ No newline at end of file | 83 | }</code></pre> \ No newline at end of file |
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index ccb1fc751..4b12fe823 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
20 | .macro { color: #94BFF3; } | 20 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 21 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 22 | .variable { color: #DCDCCC; } |
23 | .format_specifier { color: #CC696B; } | ||
23 | .mutable { text-decoration: underline; } | 24 | .mutable { text-decoration: underline; } |
24 | 25 | ||
25 | .keyword { color: #F0DFAF; font-weight: bold; } | 26 | .keyword { color: #F0DFAF; font-weight: bold; } |
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 3df82c45f..11e1f3e44 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html | |||
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
20 | .macro { color: #94BFF3; } | 20 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 21 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 22 | .variable { color: #DCDCCC; } |
23 | .format_specifier { color: #CC696B; } | ||
23 | .mutable { text-decoration: underline; } | 24 | .mutable { text-decoration: underline; } |
24 | 25 | ||
25 | .keyword { color: #F0DFAF; font-weight: bold; } | 26 | .keyword { color: #F0DFAF; font-weight: bold; } |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index be0f8c827..6658c7bb2 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -290,7 +290,7 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { | |||
290 | | FormatSpecifier::DollarSign | 290 | | FormatSpecifier::DollarSign |
291 | | FormatSpecifier::Dot | 291 | | FormatSpecifier::Dot |
292 | | FormatSpecifier::Asterisk | 292 | | FormatSpecifier::Asterisk |
293 | | FormatSpecifier::QuestionMark => HighlightTag::Attribute, | 293 | | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier, |
294 | FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, | 294 | FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, |
295 | FormatSpecifier::Identifier => HighlightTag::Local, | 295 | FormatSpecifier::Identifier => HighlightTag::Local, |
296 | }) | 296 | }) |
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs index 010db4017..ff0eeeb52 100644 --- a/crates/ra_ide/src/syntax_highlighting/html.rs +++ b/crates/ra_ide/src/syntax_highlighting/html.rs | |||
@@ -79,6 +79,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
79 | .macro { color: #94BFF3; } | 79 | .macro { color: #94BFF3; } |
80 | .module { color: #AFD8AF; } | 80 | .module { color: #AFD8AF; } |
81 | .variable { color: #DCDCCC; } | 81 | .variable { color: #DCDCCC; } |
82 | .format_specifier { color: #CC696B; } | ||
82 | .mutable { text-decoration: underline; } | 83 | .mutable { text-decoration: underline; } |
83 | 84 | ||
84 | .keyword { color: #F0DFAF; font-weight: bold; } | 85 | .keyword { color: #F0DFAF; font-weight: bold; } |
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs index f2c421654..be1a0f12b 100644 --- a/crates/ra_ide/src/syntax_highlighting/tags.rs +++ b/crates/ra_ide/src/syntax_highlighting/tags.rs | |||
@@ -39,6 +39,7 @@ pub enum HighlightTag { | |||
39 | Union, | 39 | Union, |
40 | Local, | 40 | Local, |
41 | UnresolvedReference, | 41 | UnresolvedReference, |
42 | FormatSpecifier, | ||
42 | } | 43 | } |
43 | 44 | ||
44 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 45 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
@@ -81,6 +82,7 @@ impl HighlightTag { | |||
81 | HighlightTag::Union => "union", | 82 | HighlightTag::Union => "union", |
82 | HighlightTag::Local => "variable", | 83 | HighlightTag::Local => "variable", |
83 | HighlightTag::UnresolvedReference => "unresolved_reference", | 84 | HighlightTag::UnresolvedReference => "unresolved_reference", |
85 | HighlightTag::FormatSpecifier => "format_specifier", | ||
84 | } | 86 | } |
85 | } | 87 | } |
86 | } | 88 | } |
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs index bf97f8c56..86c70ff83 100644 --- a/crates/ra_ide/src/syntax_tree.rs +++ b/crates/ra_ide/src/syntax_tree.rs | |||
@@ -120,9 +120,8 @@ [email protected] | |||
120 | [email protected] ")" | 120 | [email protected] ")" |
121 | [email protected] " " | 121 | [email protected] " " |
122 | [email protected] | 122 | [email protected] |
123 | [email protected] | 123 | [email protected] "{" |
124 | [email protected] "{" | 124 | [email protected] "}" |
125 | [email protected] "}" | ||
126 | "# | 125 | "# |
127 | .trim() | 126 | .trim() |
128 | ); | 127 | ); |
@@ -153,26 +152,25 @@ [email protected] | |||
153 | [email protected] ")" | 152 | [email protected] ")" |
154 | [email protected] " " | 153 | [email protected] " " |
155 | [email protected] | 154 | [email protected] |
156 | [email protected] | 155 | [email protected] "{" |
157 | [email protected] "{" | 156 | [email protected] "\n " |
158 | [email protected] "\n " | 157 | [email protected] |
159 | [email protected] | 158 | [email protected] |
160 | [email protected] | 159 | [email protected] |
161 | [email protected] | 160 | [email protected] |
162 | [email protected] | 161 | [email protected] |
163 | [email protected] | 162 | [email protected] "assert" |
164 | [email protected] "assert" | 163 | [email protected] "!" |
165 | [email protected] "!" | 164 | [email protected] |
166 | [email protected] | 165 | [email protected] "(" |
167 | [email protected] "(" | 166 | [email protected] "\"\n fn foo() {\n ..." |
168 | [email protected] "\"\n fn foo() {\n ..." | 167 | [email protected] "," |
169 | [email protected] "," | 168 | [email protected] " " |
170 | [email protected] " " | 169 | [email protected] "\"\"" |
171 | [email protected] "\"\"" | 170 | [email protected] ")" |
172 | [email protected] ")" | 171 | [email protected] ";" |
173 | [email protected] ";" | 172 | [email protected] "\n" |
174 | [email protected] "\n" | 173 | [email protected] "}" |
175 | [email protected] "}" | ||
176 | "# | 174 | "# |
177 | .trim() | 175 | .trim() |
178 | ); | 176 | ); |
@@ -196,9 +194,8 @@ [email protected] | |||
196 | [email protected] ")" | 194 | [email protected] ")" |
197 | [email protected] " " | 195 | [email protected] " " |
198 | [email protected] | 196 | [email protected] |
199 | [email protected] | 197 | [email protected] "{" |
200 | [email protected] "{" | 198 | [email protected] "}" |
201 | [email protected] "}" | ||
202 | "# | 199 | "# |
203 | .trim() | 200 | .trim() |
204 | ); | 201 | ); |
@@ -265,10 +262,9 @@ [email protected] | |||
265 | [email protected] ")" | 262 | [email protected] ")" |
266 | [email protected] " " | 263 | [email protected] " " |
267 | [email protected] | 264 | [email protected] |
268 | [email protected] | 265 | [email protected] "{" |
269 | [email protected] "{" | 266 | [email protected] "\n" |
270 | [email protected] "\n" | 267 | [email protected] "}" |
271 | [email protected] "}" | ||
272 | "# | 268 | "# |
273 | .trim() | 269 | .trim() |
274 | ); | 270 | ); |
@@ -300,10 +296,9 @@ [email protected] | |||
300 | [email protected] ")" | 296 | [email protected] ")" |
301 | [email protected] " " | 297 | [email protected] " " |
302 | [email protected] | 298 | [email protected] |
303 | [email protected] | 299 | [email protected] "{" |
304 | [email protected] "{" | 300 | [email protected] "\n" |
305 | [email protected] "\n" | 301 | [email protected] "}" |
306 | [email protected] "}" | ||
307 | "# | 302 | "# |
308 | .trim() | 303 | .trim() |
309 | ); | 304 | ); |
@@ -334,10 +329,9 @@ [email protected] | |||
334 | [email protected] ")" | 329 | [email protected] ")" |
335 | [email protected] " " | 330 | [email protected] " " |
336 | [email protected] | 331 | [email protected] |
337 | [email protected] | 332 | [email protected] "{" |
338 | [email protected] "{" | 333 | [email protected] "\n" |
339 | [email protected] "\n" | 334 | [email protected] "}" |
340 | [email protected] "}" | ||
341 | [email protected] "\n" | 335 | [email protected] "\n" |
342 | [email protected] | 336 | [email protected] |
343 | [email protected] "fn" | 337 | [email protected] "fn" |
@@ -349,10 +343,9 @@ [email protected] | |||
349 | [email protected] ")" | 343 | [email protected] ")" |
350 | [email protected] " " | 344 | [email protected] " " |
351 | [email protected] | 345 | [email protected] |
352 | [email protected] | 346 | [email protected] "{" |
353 | [email protected] "{" | 347 | [email protected] "\n" |
354 | [email protected] "\n" | 348 | [email protected] "}" |
355 | [email protected] "}" | ||
356 | "# | 349 | "# |
357 | .trim() | 350 | .trim() |
358 | ); | 351 | ); |