diff options
author | Evgenii P <[email protected]> | 2019-08-02 19:15:43 +0100 |
---|---|---|
committer | Evgenii P <[email protected]> | 2019-08-02 19:15:43 +0100 |
commit | c417b98f02004a10819111903882482b39e50d17 (patch) | |
tree | 4ab6cce4d3b2c6dc5342cfa6ecfd04f281d8660b /crates | |
parent | 831d39b0f7ed0041cbc2fb8beb4cb375ab9652b1 (diff) |
Implement completion for the .await syntax
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 37 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 16 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_dot.rs | 80 |
3 files changed, 122 insertions, 11 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index c2c6921cb..8496b143a 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -23,6 +23,7 @@ use crate::{ | |||
23 | scope::{ExprScopes, ScopeId}, | 23 | scope::{ExprScopes, ScopeId}, |
24 | BodySourceMap, | 24 | BodySourceMap, |
25 | }, | 25 | }, |
26 | ty::method_resolution::implements_trait, | ||
26 | ids::LocationCtx, | 27 | ids::LocationCtx, |
27 | AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId, | 28 | AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId, |
28 | MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty, | 29 | MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty, |
@@ -409,6 +410,42 @@ impl SourceAnalyzer { | |||
409 | crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value) | 410 | crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value) |
410 | } | 411 | } |
411 | 412 | ||
413 | /// Checks that particular type `ty` implements `std::future::Future` trait. | ||
414 | /// This function is used in `.await` syntax completion. | ||
415 | pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool { | ||
416 | // Search for std::future::Future trait in scope | ||
417 | let future_trait = self.resolver.traits_in_scope(db) | ||
418 | .into_iter() | ||
419 | .filter(|t| { | ||
420 | let std = t.module(db).parent(db) | ||
421 | .and_then(|m| m | ||
422 | .name(db) | ||
423 | .and_then(|n| Some(n.to_string() == "std"))) | ||
424 | .unwrap_or(false); | ||
425 | |||
426 | let future = t.module(db).name(db) | ||
427 | .and_then(|n| Some(n.to_string() == "future")) | ||
428 | .unwrap_or(false); | ||
429 | |||
430 | let future_trait = t.name(db) | ||
431 | .and_then(|n| Some(n.to_string() == "Future")) | ||
432 | .unwrap_or(false); | ||
433 | |||
434 | std && future && future_trait | ||
435 | }) | ||
436 | .nth(0); | ||
437 | |||
438 | if let Some(trait_) = future_trait { | ||
439 | let krate = self.resolver.krate(); | ||
440 | if let Some(krate) = krate { | ||
441 | let canonical_ty = crate::ty::Canonical { value: ty, num_vars: 0 }; | ||
442 | return implements_trait(&canonical_ty, db, &self.resolver, krate, trait_); | ||
443 | } | ||
444 | } | ||
445 | |||
446 | false | ||
447 | } | ||
448 | |||
412 | #[cfg(test)] | 449 | #[cfg(test)] |
413 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { | 450 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { |
414 | self.body_source_map.clone().unwrap() | 451 | self.body_source_map.clone().unwrap() |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index d421bf9ef..2e2f88138 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -15,7 +15,7 @@ use crate::{ | |||
15 | resolve::Resolver, | 15 | resolve::Resolver, |
16 | traits::TraitItem, | 16 | traits::TraitItem, |
17 | ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy}, | 17 | ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy}, |
18 | ty::{Ty, TypeCtor}, | 18 | ty::{Ty, TypeCtor, traits::Solution}, |
19 | Crate, Function, HirDatabase, Module, Name, Trait, | 19 | Crate, Function, HirDatabase, Module, Name, Trait, |
20 | }; | 20 | }; |
21 | 21 | ||
@@ -255,6 +255,20 @@ fn iterate_inherent_methods<T>( | |||
255 | None | 255 | None |
256 | } | 256 | } |
257 | 257 | ||
258 | pub(crate) fn implements_trait(ty: &Canonical<Ty>, db: &impl HirDatabase, resolver: &Resolver, krate: Crate, trait_: Trait) -> bool { | ||
259 | let env = lower::trait_env(db, resolver); | ||
260 | let goal = generic_implements_goal(db, env.clone(), trait_, ty.clone()); | ||
261 | let solution = db.trait_solve(krate, goal); | ||
262 | |||
263 | if let Some(solution) = solution { | ||
264 | if let Solution::Unique(_) = solution { | ||
265 | return true | ||
266 | } | ||
267 | } | ||
268 | |||
269 | false | ||
270 | } | ||
271 | |||
258 | impl Ty { | 272 | impl Ty { |
259 | // This would be nicer if it just returned an iterator, but that runs into | 273 | // This would be nicer if it just returned an iterator, but that runs into |
260 | // lifetime problems, because we need to borrow temp `CrateImplBlocks`. | 274 | // lifetime problems, because we need to borrow temp `CrateImplBlocks`. |
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index 536ba36df..b6579be63 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs | |||
@@ -1,19 +1,49 @@ | |||
1 | use hir::{AdtDef, Ty, TypeCtor}; | 1 | use hir::{AdtDef, Ty, TypeCtor}; |
2 | 2 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::{completion::{ |
4 | completion_context::CompletionContext, | ||
5 | completion_item::Completions, | ||
6 | }, CompletionItem}; | ||
7 | use ra_syntax::ast::AstNode; | ||
8 | use ra_text_edit::TextEditBuilder; | ||
4 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
10 | use crate::completion::completion_item::{Builder, CompletionKind}; | ||
11 | use ra_syntax::TextRange; | ||
5 | 12 | ||
6 | /// Complete dot accesses, i.e. fields or methods (currently only fields). | 13 | /// Applies postfix edition but with CompletionKind::Reference |
14 | fn postfix_reference(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | ||
15 | let edit = { | ||
16 | let receiver_range = | ||
17 | ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); | ||
18 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); | ||
19 | let mut builder = TextEditBuilder::default(); | ||
20 | builder.replace(delete_range, snippet.to_string()); | ||
21 | builder.finish() | ||
22 | }; | ||
23 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), label) | ||
24 | .detail(detail) | ||
25 | .snippet_edit(edit) | ||
26 | } | ||
27 | |||
28 | /// Complete dot accesses, i.e. fields or methods (and .await syntax). | ||
7 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 29 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { |
8 | let receiver_ty = | 30 | if let Some(dot_receiver) = &ctx.dot_receiver { |
9 | match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) { | 31 | let receiver_text = dot_receiver.syntax().text().to_string(); |
10 | Some(it) => it, | 32 | let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver); |
11 | None => return, | 33 | |
12 | }; | 34 | if let Some(receiver_ty) = receiver_ty { |
13 | if !ctx.is_call { | 35 | if !ctx.is_call { |
14 | complete_fields(acc, ctx, receiver_ty.clone()); | 36 | complete_fields(acc, ctx, receiver_ty.clone()); |
37 | } | ||
38 | complete_methods(acc, ctx, receiver_ty.clone()); | ||
39 | |||
40 | // Suggest .await syntax for types that implement std::future::Future | ||
41 | if ctx.analyzer.impls_future(ctx.db, receiver_ty) { | ||
42 | postfix_reference(ctx, ".await", "expr.await", &format!("{}.await", receiver_text)) | ||
43 | .add_to(acc); | ||
44 | } | ||
45 | } | ||
15 | } | 46 | } |
16 | complete_methods(acc, ctx, receiver_ty); | ||
17 | } | 47 | } |
18 | 48 | ||
19 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { | 49 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { |
@@ -406,4 +436,34 @@ mod tests { | |||
406 | "### | 436 | "### |
407 | ); | 437 | ); |
408 | } | 438 | } |
439 | |||
440 | #[test] | ||
441 | fn test_completion_await_impls_future() { | ||
442 | assert_debug_snapshot_matches!( | ||
443 | do_ref_completion( | ||
444 | r" | ||
445 | // Mock Future trait from stdlib | ||
446 | pub mod std { pub mod future { pub trait Future {} } } | ||
447 | |||
448 | use std::future::*; | ||
449 | struct A {} | ||
450 | impl Future for A {} | ||
451 | |||
452 | fn foo(a: A) { | ||
453 | a.<|> | ||
454 | } | ||
455 | "), | ||
456 | @r###" | ||
457 | ⋮[ | ||
458 | ⋮ CompletionItem { | ||
459 | ⋮ label: ".await", | ||
460 | ⋮ source_range: [249; 249), | ||
461 | ⋮ delete: [247; 249), | ||
462 | ⋮ insert: "a.await", | ||
463 | ⋮ detail: "expr.await", | ||
464 | ⋮ }, | ||
465 | ⋮] | ||
466 | "### | ||
467 | ) | ||
468 | } | ||
409 | } | 469 | } |