aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/completion_context.rs
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2020-03-07 14:27:03 +0000
committerFlorian Diebold <[email protected]>2020-03-07 14:48:06 +0000
commit24e98121d81b75bafcd9c6005548776c00de8401 (patch)
tree976841b2501ab4501613a5f66b1004cd67f7e369 /crates/ra_ide/src/completion/completion_context.rs
parentaff82cf7ac172f213cb5dcca637cb2c5332294c1 (diff)
Try to complete within macros
Diffstat (limited to 'crates/ra_ide/src/completion/completion_context.rs')
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs87
1 files changed, 62 insertions, 25 deletions
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 9aa5a705d..81a033fcb 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;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, AstNode, SourceFile, 8 ast, AstNode,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextUnit, 10 SyntaxNode, SyntaxToken, TextRange, TextUnit,
11}; 11};
@@ -20,6 +20,9 @@ pub(crate) struct CompletionContext<'a> {
20 pub(super) sema: Semantics<'a, RootDatabase>, 20 pub(super) sema: Semantics<'a, RootDatabase>,
21 pub(super) db: &'a RootDatabase, 21 pub(super) db: &'a RootDatabase,
22 pub(super) offset: TextUnit, 22 pub(super) offset: TextUnit,
23 /// The token before the cursor, in the original file.
24 pub(super) original_token: SyntaxToken,
25 /// The token before the cursor, in the macro-expanded file.
23 pub(super) token: SyntaxToken, 26 pub(super) token: SyntaxToken,
24 pub(super) module: Option<hir::Module>, 27 pub(super) module: Option<hir::Module>,
25 pub(super) name_ref_syntax: Option<ast::NameRef>, 28 pub(super) name_ref_syntax: Option<ast::NameRef>,
@@ -67,12 +70,18 @@ impl<'a> CompletionContext<'a> {
67 let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); 70 let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string());
68 parse.reparse(&edit).tree() 71 parse.reparse(&edit).tree()
69 }; 72 };
73 let fake_ident_token =
74 file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap();
70 75
76 // TODO: shouldn't this take the position into account? (in case we're inside a mod {})
71 let module = sema.to_module_def(position.file_id); 77 let module = sema.to_module_def(position.file_id);
72 let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; 78 let original_token =
79 original_file.syntax().token_at_offset(position.offset).left_biased()?;
80 let token = sema.descend_into_macros(original_token.clone());
73 let mut ctx = CompletionContext { 81 let mut ctx = CompletionContext {
74 sema, 82 sema,
75 db, 83 db,
84 original_token,
76 token, 85 token,
77 offset: position.offset, 86 offset: position.offset,
78 module, 87 module,
@@ -95,15 +104,45 @@ impl<'a> CompletionContext<'a> {
95 has_type_args: false, 104 has_type_args: false,
96 dot_receiver_is_ambiguous_float_literal: false, 105 dot_receiver_is_ambiguous_float_literal: false,
97 }; 106 };
98 ctx.fill(&original_file, file_with_fake_ident, position.offset); 107
108 let mut original_file = original_file.syntax().clone();
109 let mut hypothetical_file = file_with_fake_ident.syntax().clone();
110 let mut offset = position.offset;
111 let mut fake_ident_token = fake_ident_token;
112
113 // Are we inside a macro call?
114 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
115 find_node_at_offset::<ast::MacroCall>(&original_file, offset),
116 find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset),
117 ) {
118 if let (Some(actual_expansion), Some(hypothetical_expansion)) = (
119 ctx.sema.expand(&actual_macro_call),
120 ctx.sema.expand_hypothetical(
121 &actual_macro_call,
122 &macro_call_with_fake_ident,
123 fake_ident_token,
124 ),
125 ) {
126 // TODO check that the expansions 'look the same' up to the inserted token?
127 original_file = actual_expansion;
128 hypothetical_file = hypothetical_expansion.0;
129 fake_ident_token = hypothetical_expansion.1;
130 offset = fake_ident_token.text_range().start();
131 } else {
132 break;
133 }
134 }
135
136 ctx.fill(&original_file, hypothetical_file, offset);
99 Some(ctx) 137 Some(ctx)
100 } 138 }
101 139
102 // The range of the identifier that is being completed. 140 // The range of the identifier that is being completed.
103 pub(crate) fn source_range(&self) -> TextRange { 141 pub(crate) fn source_range(&self) -> TextRange {
142 // check kind of macro-expanded token, but use range of original token
104 match self.token.kind() { 143 match self.token.kind() {
105 // workaroud when completion is triggered by trigger characters. 144 // workaroud when completion is triggered by trigger characters.
106 IDENT => self.token.text_range(), 145 IDENT => self.original_token.text_range(),
107 _ => TextRange::offset_len(self.offset, 0.into()), 146 _ => TextRange::offset_len(self.offset, 0.into()),
108 } 147 }
109 } 148 }
@@ -114,14 +153,12 @@ impl<'a> CompletionContext<'a> {
114 153
115 fn fill( 154 fn fill(
116 &mut self, 155 &mut self,
117 original_file: &ast::SourceFile, 156 original_file: &SyntaxNode,
118 file_with_fake_ident: ast::SourceFile, 157 file_with_fake_ident: SyntaxNode,
119 offset: TextUnit, 158 offset: TextUnit,
120 ) { 159 ) {
121 // First, let's try to complete a reference to some declaration. 160 // First, let's try to complete a reference to some declaration.
122 if let Some(name_ref) = 161 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
123 find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset)
124 {
125 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. 162 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
126 // See RFC#1685. 163 // See RFC#1685.
127 if is_node::<ast::Param>(name_ref.syntax()) { 164 if is_node::<ast::Param>(name_ref.syntax()) {
@@ -133,8 +170,7 @@ impl<'a> CompletionContext<'a> {
133 170
134 // Otherwise, see if this is a declaration. We can use heuristics to 171 // Otherwise, see if this is a declaration. We can use heuristics to
135 // suggest declaration names, see `CompletionKind::Magic`. 172 // suggest declaration names, see `CompletionKind::Magic`.
136 if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) 173 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
137 {
138 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { 174 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
139 let parent = bind_pat.syntax().parent(); 175 let parent = bind_pat.syntax().parent();
140 if parent.clone().and_then(ast::MatchArm::cast).is_some() 176 if parent.clone().and_then(ast::MatchArm::cast).is_some()
@@ -148,23 +184,24 @@ impl<'a> CompletionContext<'a> {
148 return; 184 return;
149 } 185 }
150 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { 186 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
151 self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); 187 self.record_lit_pat =
188 self.sema.find_node_at_offset_with_macros(&original_file, self.offset);
152 } 189 }
153 } 190 }
154 } 191 }
155 192
156 fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { 193 fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) {
157 self.name_ref_syntax = 194 self.name_ref_syntax =
158 find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); 195 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
159 let name_range = name_ref.syntax().text_range(); 196 let name_range = name_ref.syntax().text_range();
160 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { 197 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() {
161 self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); 198 self.record_lit_syntax =
199 self.sema.find_node_at_offset_with_macros(&original_file, self.offset);
162 } 200 }
163 201
164 self.impl_def = self 202 self.impl_def = self
165 .token 203 .sema
166 .parent() 204 .ancestors_with_macros(self.token.parent())
167 .ancestors()
168 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 205 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
169 .find_map(ast::ImplDef::cast); 206 .find_map(ast::ImplDef::cast);
170 207
@@ -183,12 +220,12 @@ impl<'a> CompletionContext<'a> {
183 _ => (), 220 _ => (),
184 } 221 }
185 222
186 self.use_item_syntax = self.token.parent().ancestors().find_map(ast::UseItem::cast); 223 self.use_item_syntax =
224 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast);
187 225
188 self.function_syntax = self 226 self.function_syntax = self
189 .token 227 .sema
190 .parent() 228 .ancestors_with_macros(self.token.parent())
191 .ancestors()
192 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 229 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
193 .find_map(ast::FnDef::cast); 230 .find_map(ast::FnDef::cast);
194 231
@@ -242,7 +279,7 @@ impl<'a> CompletionContext<'a> {
242 279
243 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { 280 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
244 if let Some(if_expr) = 281 if let Some(if_expr) =
245 find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) 282 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
246 { 283 {
247 if if_expr.syntax().text_range().end() 284 if if_expr.syntax().text_range().end()
248 < name_ref.syntax().text_range().start() 285 < name_ref.syntax().text_range().start()
@@ -259,7 +296,7 @@ impl<'a> CompletionContext<'a> {
259 self.dot_receiver = field_expr 296 self.dot_receiver = field_expr
260 .expr() 297 .expr()
261 .map(|e| e.syntax().text_range()) 298 .map(|e| e.syntax().text_range())
262 .and_then(|r| find_node_with_range(original_file.syntax(), r)); 299 .and_then(|r| find_node_with_range(original_file, r));
263 self.dot_receiver_is_ambiguous_float_literal = 300 self.dot_receiver_is_ambiguous_float_literal =
264 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { 301 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
265 match l.kind() { 302 match l.kind() {
@@ -275,7 +312,7 @@ impl<'a> CompletionContext<'a> {
275 self.dot_receiver = method_call_expr 312 self.dot_receiver = method_call_expr
276 .expr() 313 .expr()
277 .map(|e| e.syntax().text_range()) 314 .map(|e| e.syntax().text_range())
278 .and_then(|r| find_node_with_range(original_file.syntax(), r)); 315 .and_then(|r| find_node_with_range(original_file, r));
279 self.is_call = true; 316 self.is_call = true;
280 } 317 }
281 } 318 }