use ide_db::RootDatabase;
use syntax::{
algo::non_trivia_sibling,
- ast::{self, ArgListOwner, LoopBodyOwner},
+ ast::{self, HasArgList, HasLoopBody},
match_ast, AstNode, Direction, SyntaxElement,
SyntaxKind::*,
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
}
/// Direct parent "thing" of what we are currently completing.
+///
+/// This may contain nodes of the fake file as well as the original, comments on the variants specify
+/// from which file the nodes are.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum ImmediateLocation {
Use,
UseTree,
+ Rename,
Impl,
Trait,
RecordField,
TupleField,
RefExpr,
IdentPat,
- BlockExpr,
+ StmtList,
ItemList,
TypeBound,
- // Fake file ast node
+ Variant,
+ /// Fake file ast node
Attribute(ast::Attr),
- // Fake file ast node
+ /// Fake file ast node
ModDeclaration(ast::Module),
Visibility(ast::Visibility),
- // Original file ast node
+ /// Original file ast node
MethodCall {
receiver: Option<ast::Expr>,
has_parens: bool,
},
- // Original file ast node
+ /// Original file ast node
FieldAccess {
receiver: Option<ast::Expr>,
receiver_is_ambiguous_float_literal: bool,
},
- // Original file ast node
// Only set from a type arg
+ /// Original file ast node
GenericArgList(ast::GenericArgList),
- // Original file ast node
/// The record expr of the field name we are completing
+ ///
+ /// Original file ast node
RecordExpr(ast::RecordExpr),
- // Original file ast node
+ /// The record expr of the functional update syntax we are completing
+ ///
+ /// Original file ast node
+ RecordExprUpdate(ast::RecordExpr),
/// The record pat of the field name we are completing
+ ///
+ /// Original file ast node
RecordPat(ast::RecordPat),
}
}
}
};
+
let res = match_ast! {
match parent {
ast::IdentPat(_it) => ImmediateLocation::IdentPat,
ast::Use(_it) => ImmediateLocation::Use,
ast::UseTree(_it) => ImmediateLocation::UseTree,
ast::UseTreeList(_it) => ImmediateLocation::UseTree,
- ast::BlockExpr(_it) => ImmediateLocation::BlockExpr,
+ ast::Rename(_it) => ImmediateLocation::Rename,
+ ast::StmtList(_it) => ImmediateLocation::StmtList,
ast::SourceFile(_it) => ImmediateLocation::ItemList,
ast::ItemList(_it) => ImmediateLocation::ItemList,
ast::RefExpr(_it) => ImmediateLocation::RefExpr,
+ ast::Variant(_it) => ImmediateLocation::Variant,
ast::RecordField(it) => if it.ty().map_or(false, |it| it.syntax().text_range().contains(offset)) {
return None;
} else {
ImmediateLocation::RecordField
},
+ ast::RecordExprFieldList(_it) => sema
+ .find_node_at_offset_with_macros(original_file, offset)
+ .map(ImmediateLocation::RecordExprUpdate)?,
ast::TupleField(_it) => ImmediateLocation::TupleField,
ast::TupleFieldList(_it) => ImmediateLocation::TupleField,
ast::TypeBound(_it) => ImmediateLocation::TypeBound,
Some(res)
}
+/// Maximize a nameref to its enclosing path if its the last segment of said path.
+/// That is, when completing a [`NameRef`] we actually handle it as the path it is part of when determining
+/// its location.
fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
- // Maximize a nameref to its enclosing path if its the last segment of said path
if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
let p = segment.parent_path();
if p.parent_path().is_none() {
- if let Some(it) = p
+ // Get rid of PathExpr, PathType, etc...
+ let path = p
.syntax()
.ancestors()
.take_while(|it| it.text_range() == p.syntax().text_range())
- .last()
- {
+ .last();
+ if let Some(it) = path {
return it;
}
}
}
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
+ let range = syntax.text_range().intersect(range)?;
syntax.covering_element(range).ancestors().find_map(N::cast)
}
#[test]
fn test_block_expr_loc() {
- check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr);
- check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::BlockExpr);
+ check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::StmtList);
+ check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::StmtList);
}
#[test]