mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
Determine receiver for completion in a more robust way
Also rename a parameter.
This commit is contained in:
parent
3befd1a9e8
commit
3e4d41d1e4
2 changed files with 31 additions and 21 deletions
|
@ -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) => {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue