aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2018-12-25 14:15:40 +0000
committerFlorian Diebold <[email protected]>2018-12-25 14:27:15 +0000
commitab0b63992be0cec4999810096a53b40f63f90349 (patch)
tree5e6dca70c4e842bc87e6e1287cca176031ecc92f /crates
parent0d724ea572a5dd26acbbf2eb4538eabe454fb894 (diff)
Implement basic completion for fields
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/completion.rs8
-rw-r--r--crates/ra_analysis/src/completion/complete_dot.rs98
-rw-r--r--crates/ra_analysis/src/completion/completion_context.rs34
-rw-r--r--crates/ra_analysis/src/completion/completion_item.rs1
-rw-r--r--crates/ra_hir/src/adt.rs14
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir/src/ty.rs5
-rw-r--r--crates/ra_lsp_server/src/conv.rs1
-rw-r--r--crates/ra_syntax/src/ast/generated.rs4
-rw-r--r--crates/ra_syntax/src/grammar.ron2
10 files changed, 156 insertions, 12 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index d742d6295..fe580700f 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -1,6 +1,7 @@
1mod completion_item; 1mod completion_item;
2mod completion_context; 2mod completion_context;
3 3
4mod complete_dot;
4mod complete_fn_param; 5mod complete_fn_param;
5mod complete_keyword; 6mod complete_keyword;
6mod complete_snippet; 7mod complete_snippet;
@@ -20,13 +21,13 @@ use crate::{
20 21
21pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind}; 22pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind};
22 23
23/// Main entry point for copmletion. We run comletion as a two-phase process. 24/// Main entry point for completion. We run completion as a two-phase process.
24/// 25///
25/// First, we look at the position and collect a so-called `CompletionContext. 26/// First, we look at the position and collect a so-called `CompletionContext.
26/// This is a somewhat messy process, because, during completion, syntax tree is 27/// This is a somewhat messy process, because, during completion, syntax tree is
27/// incomplete and can look readlly weired. 28/// incomplete and can look really weird.
28/// 29///
29/// Once the context is collected, we run a series of completion routines whihc 30/// Once the context is collected, we run a series of completion routines which
30/// look at the context and produce completion items. 31/// look at the context and produce completion items.
31pub(crate) fn completions( 32pub(crate) fn completions(
32 db: &db::RootDatabase, 33 db: &db::RootDatabase,
@@ -43,6 +44,7 @@ pub(crate) fn completions(
43 complete_snippet::complete_item_snippet(&mut acc, &ctx); 44 complete_snippet::complete_item_snippet(&mut acc, &ctx);
44 complete_path::complete_path(&mut acc, &ctx)?; 45 complete_path::complete_path(&mut acc, &ctx)?;
45 complete_scope::complete_scope(&mut acc, &ctx)?; 46 complete_scope::complete_scope(&mut acc, &ctx)?;
47 complete_dot::complete_dot(&mut acc, &ctx)?;
46 48
47 Ok(Some(acc)) 49 Ok(Some(acc))
48} 50}
diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs
new file mode 100644
index 000000000..fa62da210
--- /dev/null
+++ b/crates/ra_analysis/src/completion/complete_dot.rs
@@ -0,0 +1,98 @@
1use ra_syntax::ast::AstNode;
2use hir::{Ty, Def};
3
4use crate::Cancelable;
5use crate::completion::{CompletionContext, Completions, CompletionKind, CompletionItem, CompletionItemKind};
6
7/// Complete dot accesses, i.e. fields or methods (currently only fields).
8pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> {
9 let module = if let Some(module) = &ctx.module {
10 module
11 } else {
12 return Ok(());
13 };
14 let function = if let Some(fn_def) = ctx.enclosing_fn {
15 hir::source_binder::function_from_module(ctx.db, module, fn_def)
16 } else {
17 return Ok(());
18 };
19 let receiver = if let Some(receiver) = ctx.dot_receiver {
20 receiver
21 } else {
22 return Ok(());
23 };
24 let infer_result = function.infer(ctx.db)?;
25 let receiver_ty = if let Some(ty) = infer_result.type_of_node(receiver.syntax()) {
26 ty
27 } else {
28 return Ok(());
29 };
30 if !ctx.is_method_call {
31 complete_fields(acc, ctx, receiver_ty)?;
32 }
33 Ok(())
34}
35
36fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, ty: Ty) -> Cancelable<()> {
37 // TODO: autoderef etc.
38 match ty {
39 Ty::Adt { def_id, .. } => {
40 match def_id.resolve(ctx.db)? {
41 Def::Struct(s) => {
42 let variant_data = s.variant_data(ctx.db)?;
43 for field in variant_data.fields() {
44 CompletionItem::new(CompletionKind::Reference, field.name().to_string())
45 .kind(CompletionItemKind::Field)
46 .add_to(acc);
47 }
48 }
49 // TODO unions
50 _ => {}
51 }
52 }
53 Ty::Tuple(fields) => {
54 for (i, _ty) in fields.iter().enumerate() {
55 CompletionItem::new(CompletionKind::Reference, i.to_string())
56 .kind(CompletionItemKind::Field)
57 .add_to(acc);
58 }
59 }
60 _ => {}
61 };
62 Ok(())
63}
64
65#[cfg(test)]
66mod tests {
67 use crate::completion::*;
68
69 fn check_ref_completion(code: &str, expected_completions: &str) {
70 check_completion(code, expected_completions, CompletionKind::Reference);
71 }
72
73 #[test]
74 fn test_struct_field_completion() {
75 check_ref_completion(
76 r"
77 struct A { the_field: u32 }
78 fn foo(a: A) {
79 a.<|>
80 }
81 ",
82 r#"the_field"#,
83 );
84 }
85
86 #[test]
87 fn test_no_struct_field_completion_for_method_call() {
88 check_ref_completion(
89 r"
90 struct A { the_field: u32 }
91 fn foo(a: A) {
92 a.<|>()
93 }
94 ",
95 r#""#,
96 );
97 }
98}
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs
index 064fbc6f7..12e98e870 100644
--- a/crates/ra_analysis/src/completion/completion_context.rs
+++ b/crates/ra_analysis/src/completion/completion_context.rs
@@ -31,6 +31,10 @@ pub(super) struct CompletionContext<'a> {
31 pub(super) is_stmt: bool, 31 pub(super) is_stmt: bool,
32 /// Something is typed at the "top" level, in module or impl/trait. 32 /// Something is typed at the "top" level, in module or impl/trait.
33 pub(super) is_new_item: bool, 33 pub(super) is_new_item: bool,
34 /// The receiver if this is a field or method access, i.e. writing something.<|>
35 pub(super) dot_receiver: Option<ast::Expr<'a>>,
36 /// If this is a method call in particular, i.e. the () are already there.
37 pub(super) is_method_call: bool,
34} 38}
35 39
36impl<'a> CompletionContext<'a> { 40impl<'a> CompletionContext<'a> {
@@ -54,6 +58,8 @@ impl<'a> CompletionContext<'a> {
54 after_if: false, 58 after_if: false,
55 is_stmt: false, 59 is_stmt: false,
56 is_new_item: false, 60 is_new_item: false,
61 dot_receiver: None,
62 is_method_call: false,
57 }; 63 };
58 ctx.fill(original_file, position.offset); 64 ctx.fill(original_file, position.offset);
59 Ok(Some(ctx)) 65 Ok(Some(ctx))
@@ -105,6 +111,12 @@ impl<'a> CompletionContext<'a> {
105 _ => (), 111 _ => (),
106 } 112 }
107 113
114 self.enclosing_fn = self
115 .leaf
116 .ancestors()
117 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
118 .find_map(ast::FnDef::cast);
119
108 let parent = match name_ref.syntax().parent() { 120 let parent = match name_ref.syntax().parent() {
109 Some(it) => it, 121 Some(it) => it,
110 None => return, 122 None => return,
@@ -120,11 +132,6 @@ impl<'a> CompletionContext<'a> {
120 } 132 }
121 if path.qualifier().is_none() { 133 if path.qualifier().is_none() {
122 self.is_trivial_path = true; 134 self.is_trivial_path = true;
123 self.enclosing_fn = self
124 .leaf
125 .ancestors()
126 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
127 .find_map(ast::FnDef::cast);
128 135
129 self.is_stmt = match name_ref 136 self.is_stmt = match name_ref
130 .syntax() 137 .syntax()
@@ -145,6 +152,23 @@ impl<'a> CompletionContext<'a> {
145 } 152 }
146 } 153 }
147 } 154 }
155 if let Some(_field_expr) = ast::FieldExpr::cast(parent) {
156 self.dot_receiver = self
157 .leaf
158 .ancestors()
159 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
160 .find_map(ast::FieldExpr::cast)
161 .and_then(ast::FieldExpr::expr);
162 }
163 if let Some(_method_call_expr) = ast::MethodCallExpr::cast(parent) {
164 self.dot_receiver = self
165 .leaf
166 .ancestors()
167 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
168 .find_map(ast::MethodCallExpr::cast)
169 .and_then(ast::MethodCallExpr::expr);
170 self.is_method_call = true;
171 }
148 } 172 }
149} 173}
150 174
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs
index 6d466c8bd..c9f9f495d 100644
--- a/crates/ra_analysis/src/completion/completion_item.rs
+++ b/crates/ra_analysis/src/completion/completion_item.rs
@@ -30,6 +30,7 @@ pub enum CompletionItemKind {
30 Struct, 30 Struct,
31 Enum, 31 Enum,
32 Binding, 32 Binding,
33 Field,
33} 34}
34 35
35#[derive(Debug, PartialEq, Eq)] 36#[derive(Debug, PartialEq, Eq)]
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index 03770ed7d..e65f8deb8 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -124,6 +124,15 @@ pub struct StructField {
124 ty: Ty, 124 ty: Ty,
125} 125}
126 126
127impl StructField {
128 pub fn name(&self) -> SmolStr {
129 self.name.clone()
130 }
131 pub fn ty(&self) -> Ty {
132 self.ty.clone()
133 }
134}
135
127/// Fields of an enum variant or struct 136/// Fields of an enum variant or struct
128#[derive(Debug, Clone, PartialEq, Eq)] 137#[derive(Debug, Clone, PartialEq, Eq)]
129pub enum VariantData { 138pub enum VariantData {
@@ -168,7 +177,10 @@ impl VariantData {
168 } 177 }
169 178
170 pub(crate) fn get_field_ty(&self, field_name: &str) -> Option<Ty> { 179 pub(crate) fn get_field_ty(&self, field_name: &str) -> Option<Ty> {
171 self.fields().iter().find(|f| f.name == field_name).map(|f| f.ty.clone()) 180 self.fields()
181 .iter()
182 .find(|f| f.name == field_name)
183 .map(|f| f.ty.clone())
172 } 184 }
173 185
174 pub fn fields(&self) -> &[StructField] { 186 pub fn fields(&self) -> &[StructField] {
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 796970d8a..68fdbb7ea 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -44,6 +44,7 @@ pub use self::{
44 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, 44 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution},
45 function::{Function, FnScopes}, 45 function::{Function, FnScopes},
46 adt::{Struct, Enum}, 46 adt::{Struct, Enum},
47 ty::Ty,
47}; 48};
48 49
49pub use self::function::FnSignatureInfo; 50pub use self::function::FnSignatureInfo;
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index f931f3c87..83da13f1a 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -574,7 +574,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
574 match receiver_ty { 574 match receiver_ty {
575 Ty::Tuple(fields) => { 575 Ty::Tuple(fields) => {
576 let i = text.parse::<usize>().ok(); 576 let i = text.parse::<usize>().ok();
577 i.and_then(|i| fields.get(i).cloned()).unwrap_or(Ty::Unknown) 577 i.and_then(|i| fields.get(i).cloned())
578 .unwrap_or(Ty::Unknown)
578 } 579 }
579 Ty::Adt { def_id, .. } => { 580 Ty::Adt { def_id, .. } => {
580 let field_ty = match def_id.resolve(self.db)? { 581 let field_ty = match def_id.resolve(self.db)? {
@@ -589,7 +590,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
589 } else { 590 } else {
590 Ty::Unknown 591 Ty::Unknown
591 } 592 }
592 }, 593 }
593 ast::Expr::TryExpr(e) => { 594 ast::Expr::TryExpr(e) => {
594 let _inner_ty = if let Some(e) = e.expr() { 595 let _inner_ty = if let Some(e) = e.expr() {
595 self.infer_expr(e)? 596 self.infer_expr(e)?
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index af5289311..c0e4e3a36 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -58,6 +58,7 @@ impl Conv for CompletionItemKind {
58 CompletionItemKind::Struct => Struct, 58 CompletionItemKind::Struct => Struct,
59 CompletionItemKind::Enum => Enum, 59 CompletionItemKind::Enum => Enum,
60 CompletionItemKind::Binding => Variable, 60 CompletionItemKind::Binding => Variable,
61 CompletionItemKind::Field => Field,
61 } 62 }
62 } 63 }
63} 64}
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 4e0550487..6b2800a0e 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -2030,6 +2030,10 @@ impl<'a> MethodCallExpr<'a> {
2030 pub fn expr(self) -> Option<Expr<'a>> { 2030 pub fn expr(self) -> Option<Expr<'a>> {
2031 super::child_opt(self) 2031 super::child_opt(self)
2032 } 2032 }
2033
2034 pub fn name_ref(self) -> Option<NameRef<'a>> {
2035 super::child_opt(self)
2036 }
2033} 2037}
2034 2038
2035// Module 2039// Module
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 923da0324..dcde32923 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -403,7 +403,7 @@ Grammar(
403 ), 403 ),
404 "MethodCallExpr": ( 404 "MethodCallExpr": (
405 traits: ["ArgListOwner"], 405 traits: ["ArgListOwner"],
406 options: [ "Expr" ], 406 options: [ "Expr", "NameRef" ],
407 ), 407 ),
408 "IndexExpr": (), 408 "IndexExpr": (),
409 "FieldExpr": (options: ["Expr", "NameRef"]), 409 "FieldExpr": (options: ["Expr", "NameRef"]),