From c417b98f02004a10819111903882482b39e50d17 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sat, 3 Aug 2019 01:15:43 +0700 Subject: Implement completion for the .await syntax --- crates/ra_hir/src/source_binder.rs | 37 +++++++++++ crates/ra_hir/src/ty/method_resolution.rs | 16 ++++- crates/ra_ide_api/src/completion/complete_dot.rs | 80 +++++++++++++++++++++--- 3 files changed, 122 insertions(+), 11 deletions(-) (limited to 'crates') 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::{ scope::{ExprScopes, ScopeId}, BodySourceMap, }, + ty::method_resolution::implements_trait, ids::LocationCtx, AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HirDatabase, HirFileId, MacroDef, Module, Name, Path, PerNs, Resolver, Static, Struct, Trait, Ty, @@ -409,6 +410,42 @@ impl SourceAnalyzer { crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value) } + /// Checks that particular type `ty` implements `std::future::Future` trait. + /// This function is used in `.await` syntax completion. + pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool { + // Search for std::future::Future trait in scope + let future_trait = self.resolver.traits_in_scope(db) + .into_iter() + .filter(|t| { + let std = t.module(db).parent(db) + .and_then(|m| m + .name(db) + .and_then(|n| Some(n.to_string() == "std"))) + .unwrap_or(false); + + let future = t.module(db).name(db) + .and_then(|n| Some(n.to_string() == "future")) + .unwrap_or(false); + + let future_trait = t.name(db) + .and_then(|n| Some(n.to_string() == "Future")) + .unwrap_or(false); + + std && future && future_trait + }) + .nth(0); + + if let Some(trait_) = future_trait { + let krate = self.resolver.krate(); + if let Some(krate) = krate { + let canonical_ty = crate::ty::Canonical { value: ty, num_vars: 0 }; + return implements_trait(&canonical_ty, db, &self.resolver, krate, trait_); + } + } + + false + } + #[cfg(test)] pub(crate) fn body_source_map(&self) -> Arc { 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::{ resolve::Resolver, traits::TraitItem, ty::primitive::{FloatBitness, UncertainFloatTy, UncertainIntTy}, - ty::{Ty, TypeCtor}, + ty::{Ty, TypeCtor, traits::Solution}, Crate, Function, HirDatabase, Module, Name, Trait, }; @@ -255,6 +255,20 @@ fn iterate_inherent_methods( None } +pub(crate) fn implements_trait(ty: &Canonical, db: &impl HirDatabase, resolver: &Resolver, krate: Crate, trait_: Trait) -> bool { + let env = lower::trait_env(db, resolver); + let goal = generic_implements_goal(db, env.clone(), trait_, ty.clone()); + let solution = db.trait_solve(krate, goal); + + if let Some(solution) = solution { + if let Solution::Unique(_) = solution { + return true + } + } + + false +} + impl Ty { // This would be nicer if it just returned an iterator, but that runs into // 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 @@ use hir::{AdtDef, Ty, TypeCtor}; -use crate::completion::{CompletionContext, Completions}; +use crate::{completion::{ + completion_context::CompletionContext, + completion_item::Completions, +}, CompletionItem}; +use ra_syntax::ast::AstNode; +use ra_text_edit::TextEditBuilder; use rustc_hash::FxHashSet; +use crate::completion::completion_item::{Builder, CompletionKind}; +use ra_syntax::TextRange; -/// Complete dot accesses, i.e. fields or methods (currently only fields). +/// Applies postfix edition but with CompletionKind::Reference +fn postfix_reference(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { + let edit = { + let receiver_range = + ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); + let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); + let mut builder = TextEditBuilder::default(); + builder.replace(delete_range, snippet.to_string()); + builder.finish() + }; + CompletionItem::new(CompletionKind::Reference, ctx.source_range(), label) + .detail(detail) + .snippet_edit(edit) +} + +/// Complete dot accesses, i.e. fields or methods (and .await syntax). pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { - let receiver_ty = - match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) { - Some(it) => it, - None => return, - }; - if !ctx.is_call { - complete_fields(acc, ctx, receiver_ty.clone()); + if let Some(dot_receiver) = &ctx.dot_receiver { + let receiver_text = dot_receiver.syntax().text().to_string(); + let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver); + + if let Some(receiver_ty) = receiver_ty { + if !ctx.is_call { + complete_fields(acc, ctx, receiver_ty.clone()); + } + complete_methods(acc, ctx, receiver_ty.clone()); + + // Suggest .await syntax for types that implement std::future::Future + if ctx.analyzer.impls_future(ctx.db, receiver_ty) { + postfix_reference(ctx, ".await", "expr.await", &format!("{}.await", receiver_text)) + .add_to(acc); + } + } } - complete_methods(acc, ctx, receiver_ty); } fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { @@ -406,4 +436,34 @@ mod tests { "### ); } + + #[test] + fn test_completion_await_impls_future() { + assert_debug_snapshot_matches!( + do_ref_completion( + r" + // Mock Future trait from stdlib + pub mod std { pub mod future { pub trait Future {} } } + + use std::future::*; + struct A {} + impl Future for A {} + + fn foo(a: A) { + a.<|> + } + "), + @r###" + ⋮[ + ⋮ CompletionItem { + ⋮ label: ".await", + ⋮ source_range: [249; 249), + ⋮ delete: [247; 249), + ⋮ insert: "a.await", + ⋮ detail: "expr.await", + ⋮ }, + ⋮] + "### + ) + } } -- cgit v1.2.3