11714: fix: Fix completions not always working in for-loop patterns r=Veykril a=Veykril

Fixes https://github.com/rust-analyzer/rust-analyzer/issues/11205
bors r+

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2022-03-15 17:58:21 +00:00 committed by GitHub
commit 61b1449387
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 17 deletions

View file

@ -24,8 +24,8 @@ use text_edit::Indel;
use crate::{
patterns::{
determine_location, determine_prev_sibling, for_is_prev2, is_in_loop_body, previous_token,
ImmediateLocation, ImmediatePrevSibling,
determine_location, determine_prev_sibling, is_in_loop_body, is_in_token_of_for_loop,
previous_token, ImmediateLocation, ImmediatePrevSibling,
},
CompletionConfig,
};
@ -729,7 +729,7 @@ impl<'a> CompletionContext<'a> {
) {
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
let syntax_element = NodeOrToken::Token(fake_ident_token);
if for_is_prev2(syntax_element.clone()) {
if is_in_token_of_for_loop(syntax_element.clone()) {
// for pat $0
// there is nothing to complete here except `in` keyword
// don't bother populating the context

View file

@ -11,7 +11,7 @@ use syntax::{
ast::{self, HasArgList, HasLoopBody},
match_ast, AstNode, Direction, SyntaxElement,
SyntaxKind::*,
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
SyntaxNode, SyntaxToken, TextRange, TextSize,
};
#[cfg(test)]
@ -295,19 +295,37 @@ pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
element.into_token().and_then(previous_non_trivia_token)
}
/// Check if the token previous to the previous one is `for`.
/// For example, `for _ i$0` => true.
pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
element
.into_token()
.and_then(previous_non_trivia_token)
.and_then(previous_non_trivia_token)
.filter(|it| it.kind() == T![for])
.is_some()
pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool {
// oh my ...
(|| {
let syntax_token = element.into_token()?;
let range = syntax_token.text_range();
let for_expr = syntax_token.ancestors().find_map(ast::ForExpr::cast)?;
// check if the current token is the `in` token of a for loop
if let Some(token) = for_expr.in_token() {
return Some(syntax_token == token);
}
let pat = for_expr.pat()?;
if range.end() < pat.syntax().text_range().end() {
// if we are inside or before the pattern we can't be at the `in` token position
return None;
}
let next_sibl = next_non_trivia_sibling(pat.syntax().clone().into())?;
Some(match next_sibl {
// the loop body is some node, if our token is at the start we are at the `in` position,
// otherwise we could be in a recovered expression, we don't wanna ruin completions there
syntax::NodeOrToken::Node(n) => n.text_range().start() == range.start(),
// the loop body consists of a single token, if we are this we are certainly at the `in` token position
syntax::NodeOrToken::Token(t) => t == syntax_token,
})
})()
.unwrap_or(false)
}
#[test]
fn test_for_is_prev2() {
check_pattern_is_applicable(r"for i i$0", for_is_prev2);
check_pattern_is_applicable(r"fn __() { for i i$0 }", is_in_token_of_for_loop);
}
pub(crate) fn is_in_loop_body(node: &SyntaxNode) -> bool {
@ -329,7 +347,7 @@ pub(crate) fn is_in_loop_body(node: &SyntaxNode) -> bool {
fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
let mut token = token.prev_token();
while let Some(inner) = token.clone() {
while let Some(inner) = token {
if !inner.kind().is_trivia() {
return Some(inner);
} else {
@ -339,6 +357,18 @@ fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
None
}
fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> {
let mut e = ele.next_sibling_or_token();
while let Some(inner) = e {
if !inner.kind().is_trivia() {
return Some(inner);
} else {
e = inner.next_sibling_or_token();
}
}
None
}
#[cfg(test)]
mod tests {
use syntax::algo::find_node_at_offset;

View file

@ -76,8 +76,14 @@ fn after_target_name_in_impl() {
kw for
"#]],
);
// FIXME: This should emit `kw where`
check(r"impl Trait for Type $0", expect![[r#""#]]);
// FIXME: This should not emit `kw for`
check(
r"impl Trait for Type $0",
expect![[r#"
kw where
kw for
"#]],
);
}
#[test]

View file

@ -92,6 +92,16 @@ fn quux() {
"#,
expect![[r#""#]],
);
check_empty(
r#"
fn foo() {
for &$0 in () {}
}
"#,
expect![[r#"
kw mut
"#]],
);
}
#[test]