mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-25 11:17:13 +00:00
Merge pull request #20620 from A4-Tacks/let-else-completion
fix: add `else` keyword completion after `let` statements
This commit is contained in:
commit
a91fb2b9a1
@ -61,6 +61,7 @@ pub(crate) fn complete_expr_path(
|
||||
after_if_expr,
|
||||
in_condition,
|
||||
incomplete_let,
|
||||
after_incomplete_let,
|
||||
in_value,
|
||||
ref ref_expr_parent,
|
||||
after_amp,
|
||||
@ -385,8 +386,11 @@ pub(crate) fn complete_expr_path(
|
||||
add_keyword("let", "let $1 = $0;");
|
||||
}
|
||||
|
||||
if after_if_expr {
|
||||
if after_if_expr || after_incomplete_let {
|
||||
add_keyword("else", "else {\n $0\n}");
|
||||
}
|
||||
|
||||
if after_if_expr {
|
||||
add_keyword("else if", "else if $1 {\n $0\n}");
|
||||
}
|
||||
|
||||
|
@ -247,6 +247,46 @@ fn main() {
|
||||
"#,
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"else",
|
||||
r#"
|
||||
fn main() {
|
||||
let x = if true {
|
||||
()
|
||||
} $0
|
||||
let y = 92;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let x = if true {
|
||||
()
|
||||
} else {
|
||||
$0
|
||||
};
|
||||
let y = 92;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"else",
|
||||
r#"
|
||||
fn main() {
|
||||
let x = 2 $0
|
||||
let y = 92;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let x = 2 else {
|
||||
$0
|
||||
};
|
||||
let y = 92;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"loop",
|
||||
r#"
|
||||
|
@ -147,6 +147,7 @@ pub(crate) struct PathExprCtx<'db> {
|
||||
/// Whether this expression is the direct condition of an if or while expression
|
||||
pub(crate) in_condition: bool,
|
||||
pub(crate) incomplete_let: bool,
|
||||
pub(crate) after_incomplete_let: bool,
|
||||
pub(crate) in_value: bool,
|
||||
pub(crate) ref_expr_parent: Option<ast::RefExpr>,
|
||||
pub(crate) after_amp: bool,
|
||||
|
@ -947,25 +947,29 @@ fn classify_name_ref<'db>(
|
||||
None
|
||||
}
|
||||
};
|
||||
let after_if_expr = |node: SyntaxNode| {
|
||||
let prev_expr = (|| {
|
||||
let node = match node.parent().and_then(ast::ExprStmt::cast) {
|
||||
Some(stmt) => stmt.syntax().clone(),
|
||||
None => node,
|
||||
};
|
||||
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
|
||||
let prev_expr = |node: SyntaxNode| {
|
||||
let node = match node.parent().and_then(ast::ExprStmt::cast) {
|
||||
Some(stmt) => stmt.syntax().clone(),
|
||||
None => node,
|
||||
};
|
||||
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
|
||||
|
||||
match_ast! {
|
||||
match prev_sibling {
|
||||
ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()),
|
||||
ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()),
|
||||
ast::Expr(expr) => Some(expr),
|
||||
_ => None,
|
||||
}
|
||||
match_ast! {
|
||||
match prev_sibling {
|
||||
ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()),
|
||||
ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()),
|
||||
ast::Expr(expr) => Some(expr),
|
||||
_ => None,
|
||||
}
|
||||
})();
|
||||
}
|
||||
};
|
||||
let after_if_expr = |node: SyntaxNode| {
|
||||
let prev_expr = prev_expr(node);
|
||||
matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
|
||||
};
|
||||
let after_incomplete_let = |node: SyntaxNode| {
|
||||
prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast)
|
||||
};
|
||||
|
||||
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
|
||||
// ex. trait Foo $0 {}
|
||||
@ -1265,10 +1269,14 @@ fn classify_name_ref<'db>(
|
||||
};
|
||||
let is_func_update = func_update_record(it);
|
||||
let in_condition = is_in_condition(&expr);
|
||||
let after_incomplete_let = after_incomplete_let(it.clone()).is_some();
|
||||
let incomplete_expr_stmt =
|
||||
it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none());
|
||||
let incomplete_let = it
|
||||
.parent()
|
||||
.and_then(ast::LetStmt::cast)
|
||||
.is_some_and(|it| it.semicolon_token().is_none());
|
||||
.is_some_and(|it| it.semicolon_token().is_none())
|
||||
|| after_incomplete_let && incomplete_expr_stmt.unwrap_or(true);
|
||||
let in_value = it.parent().and_then(Either::<ast::LetStmt, ast::ArgList>::cast).is_some();
|
||||
let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
|
||||
|
||||
@ -1292,6 +1300,7 @@ fn classify_name_ref<'db>(
|
||||
self_param,
|
||||
in_value,
|
||||
incomplete_let,
|
||||
after_incomplete_let,
|
||||
impl_,
|
||||
in_match_guard,
|
||||
},
|
||||
|
@ -450,6 +450,155 @@ fn completes_in_let_initializer() {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_let_else() {
|
||||
check(
|
||||
r#"fn main() { let _ = 2 $0 }"#,
|
||||
expect![[r#"
|
||||
fn main() fn()
|
||||
bt u32 u32
|
||||
kw async
|
||||
kw const
|
||||
kw crate::
|
||||
kw else
|
||||
kw enum
|
||||
kw extern
|
||||
kw false
|
||||
kw fn
|
||||
kw for
|
||||
kw if
|
||||
kw if let
|
||||
kw impl
|
||||
kw impl for
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
kw return
|
||||
kw self::
|
||||
kw static
|
||||
kw struct
|
||||
kw trait
|
||||
kw true
|
||||
kw type
|
||||
kw union
|
||||
kw unsafe
|
||||
kw use
|
||||
kw while
|
||||
kw while let
|
||||
sn macro_rules
|
||||
sn pd
|
||||
sn ppd
|
||||
"#]],
|
||||
);
|
||||
|
||||
check(
|
||||
r#"fn main() { let _ = 2 el$0 }"#,
|
||||
expect![[r#"
|
||||
fn main() fn()
|
||||
bt u32 u32
|
||||
kw async
|
||||
kw const
|
||||
kw crate::
|
||||
kw else
|
||||
kw enum
|
||||
kw extern
|
||||
kw false
|
||||
kw fn
|
||||
kw for
|
||||
kw if
|
||||
kw if let
|
||||
kw impl
|
||||
kw impl for
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
kw return
|
||||
kw self::
|
||||
kw static
|
||||
kw struct
|
||||
kw trait
|
||||
kw true
|
||||
kw type
|
||||
kw union
|
||||
kw unsafe
|
||||
kw use
|
||||
kw while
|
||||
kw while let
|
||||
sn macro_rules
|
||||
sn pd
|
||||
sn ppd
|
||||
"#]],
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"else",
|
||||
r#"
|
||||
fn main() {
|
||||
let _ = 2 $0
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let _ = 2 else {
|
||||
$0
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"else",
|
||||
r#"
|
||||
fn main() {
|
||||
let _ = 2 el$0
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let _ = 2 else {
|
||||
$0
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"else",
|
||||
r#"
|
||||
fn main() {
|
||||
let _ = 2 $0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let _ = 2 else {
|
||||
$0
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_edit(
|
||||
"else",
|
||||
r#"
|
||||
fn main() {
|
||||
let _ = 2 el$0;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
let _ = 2 else {
|
||||
$0
|
||||
};
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_after_ref_expr() {
|
||||
check(
|
||||
|
Loading…
x
Reference in New Issue
Block a user