]> git.lizzy.rs Git - rust.git/commitdiff
Implement completion for the .await syntax
authorEvgenii P <eupn@protonmail.com>
Fri, 2 Aug 2019 18:15:43 +0000 (01:15 +0700)
committerEvgenii P <eupn@protonmail.com>
Fri, 2 Aug 2019 18:15:43 +0000 (01:15 +0700)
crates/ra_hir/src/source_binder.rs
crates/ra_hir/src/ty/method_resolution.rs
crates/ra_ide_api/src/completion/complete_dot.rs

index c2c6921cbc3a187d3204545f2967844af0df57d8..8496b143aea6dda92456d97e069404b76ed92a18 100644 (file)
@@ -23,6 +23,7 @@
         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 @@ pub fn autoderef<'a>(
         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<BodySourceMap> {
         self.body_source_map.clone().unwrap()
index d421bf9efeeb6a74ab7d029ecefb9d55c0627866..2e2f881388a4ad360ebb9ff89c57854384780b81 100644 (file)
@@ -15,7 +15,7 @@
     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<T>(
     None
 }
 
+pub(crate) fn implements_trait(ty: &Canonical<Ty>, 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`.
index 536ba36dff3e25f4ddd68c12111c076c0fb3ed9e..b6579be63f8c9799fb85d38ac46496f2a44bed86 100644 (file)
@@ -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 @@ struct A { the_field: u32 }
         "###
         );
     }
+
+    #[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",
+       ⋮    },
+       ⋮]
+        "###
+        )
+    }
 }