Determine receiver for completion in a more robust way

Also rename a parameter.
This commit is contained in:
Florian Diebold 2018-12-25 17:43:58 +01:00
parent 3befd1a9e8
commit 3e4d41d1e4
2 changed files with 31 additions and 21 deletions

View file

@ -33,9 +33,9 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Ca
Ok(()) Ok(())
} }
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, ty: Ty) -> Cancelable<()> { fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) -> Cancelable<()> {
// TODO: autoderef etc. // TODO: autoderef etc.
match ty { match receiver {
Ty::Adt { def_id, .. } => { Ty::Adt { def_id, .. } => {
match def_id.resolve(ctx.db)? { match def_id.resolve(ctx.db)? {
Def::Struct(s) => { Def::Struct(s) => {

View file

@ -1,12 +1,13 @@
use ra_editor::find_node_at_offset; use ra_editor::find_node_at_offset;
use ra_text_edit::AtomTextEdit; use ra_text_edit::AtomTextEdit;
use ra_syntax::{ use ra_syntax::{
algo::find_leaf_at_offset, algo::{find_leaf_at_offset, find_covering_node},
ast, ast,
AstNode, AstNode,
SyntaxNodeRef, SyntaxNodeRef,
SourceFileNode, SourceFileNode,
TextUnit, TextUnit,
TextRange,
SyntaxKind::*, SyntaxKind::*,
}; };
use hir::source_binder; use hir::source_binder;
@ -65,7 +66,7 @@ impl<'a> CompletionContext<'a> {
Ok(Some(ctx)) Ok(Some(ctx))
} }
fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) { fn fill(&mut self, original_file: &'a SourceFileNode, offset: TextUnit) {
// Insert a fake ident to get a valid parse tree. We will use this file // Insert a fake ident to get a valid parse tree. We will use this file
// to determine context, though the original_file will be used for // to determine context, though the original_file will be used for
// actual completion. // actual completion.
@ -82,7 +83,7 @@ impl<'a> CompletionContext<'a> {
self.is_param = true; self.is_param = true;
return; return;
} }
self.classify_name_ref(&file, name_ref); self.classify_name_ref(original_file, name_ref);
} }
// Otherwise, see if this is a declaration. We can use heuristics to // Otherwise, see if this is a declaration. We can use heuristics to
@ -94,7 +95,7 @@ impl<'a> CompletionContext<'a> {
} }
} }
} }
fn classify_name_ref(&mut self, file: &SourceFileNode, name_ref: ast::NameRef) { fn classify_name_ref(&mut self, original_file: &'a SourceFileNode, name_ref: ast::NameRef) {
let name_range = name_ref.syntax().range(); let name_range = name_ref.syntax().range();
let top_node = name_ref let top_node = name_ref
.syntax() .syntax()
@ -144,7 +145,9 @@ impl<'a> CompletionContext<'a> {
}; };
if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) { if let Some(if_expr) =
find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off)
{
if if_expr.syntax().range().end() < name_ref.syntax().range().start() { if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
self.after_if = true; self.after_if = true;
} }
@ -152,26 +155,33 @@ impl<'a> CompletionContext<'a> {
} }
} }
} }
if let Some(_field_expr) = ast::FieldExpr::cast(parent) { if let Some(field_expr) = ast::FieldExpr::cast(parent) {
self.dot_receiver = self // The receiver comes before the point of insertion of the fake
.leaf // ident, so it should have the same range in the non-modified file
.ancestors() self.dot_receiver = field_expr
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) .expr()
.find_map(ast::FieldExpr::cast) .map(|e| e.syntax().range())
.and_then(ast::FieldExpr::expr); .and_then(|r| find_node_with_range(original_file.syntax(), r));
} }
if let Some(_method_call_expr) = ast::MethodCallExpr::cast(parent) { if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
self.dot_receiver = self // As above
.leaf self.dot_receiver = method_call_expr
.ancestors() .expr()
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) .map(|e| e.syntax().range())
.find_map(ast::MethodCallExpr::cast) .and_then(|r| find_node_with_range(original_file.syntax(), r));
.and_then(ast::MethodCallExpr::expr);
self.is_method_call = true; self.is_method_call = true;
} }
} }
} }
fn find_node_with_range<'a, N: AstNode<'a>>(
syntax: SyntaxNodeRef<'a>,
range: TextRange,
) -> Option<N> {
let node = find_covering_node(syntax, range);
node.ancestors().find_map(N::cast)
}
fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
match node.ancestors().filter_map(N::cast).next() { match node.ancestors().filter_map(N::cast).next() {
None => false, None => false,