Add .let postfix completion in let-chain support

This commit is contained in:
A4-Tacks 2025-08-24 19:26:12 +08:00
parent db7b3b1b91
commit afbecea5e9
No known key found for this signature in database
GPG key ID: 86AC1F526BA06668
2 changed files with 63 additions and 33 deletions

View file

@ -11,11 +11,10 @@ use ide_db::{
text_edit::TextEdit,
ty_filter::TryEnum,
};
use itertools::Either;
use stdx::never;
use syntax::{
SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
TextRange, TextSize,
T, TextRange, TextSize,
ast::{self, AstNode, AstToken},
};
@ -113,12 +112,8 @@ pub(crate) fn complete_postfix(
if let Some(parent) = dot_receiver_including_refs.syntax().parent()
&& let Some(second_ancestor) = parent.parent()
{
let sec_ancestor_kind = second_ancestor.kind();
if let Some(expr) = <Either<ast::IfExpr, ast::WhileExpr>>::cast(second_ancestor) {
is_in_cond = match expr {
Either::Left(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent),
Either::Right(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent),
}
if let Some(parent_expr) = ast::Expr::cast(parent) {
is_in_cond = is_in_condition(&parent_expr);
}
match &try_enum {
Some(try_enum) if is_in_cond => match try_enum {
@ -147,7 +142,7 @@ pub(crate) fn complete_postfix(
.add_to(acc, ctx.db);
}
},
_ if matches!(sec_ancestor_kind, STMT_LIST | EXPR_STMT) => {
_ if matches!(second_ancestor.kind(), STMT_LIST | EXPR_STMT) => {
postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
.add_to(acc, ctx.db);
postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};"))
@ -454,6 +449,24 @@ fn add_custom_postfix_completions(
None
}
pub(crate) fn is_in_condition(it: &ast::Expr) -> bool {
(|| {
let parent = it.syntax().parent()?;
if let Some(expr) = ast::WhileExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
} else if let Some(expr) = ast::IfExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
} else if let Some(expr) = ast::BinExpr::cast(parent)
&& expr.op_token()?.kind() == T![&&]
{
Some(is_in_condition(&expr.into()))
} else {
None
}
})()
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use expect_test::expect;
@ -648,6 +661,38 @@ fn main() {
let bar = Some(true);
if let Some($0) = bar
}
"#,
);
check_edit(
"let",
r#"
//- minicore: option
fn main() {
let bar = Some(true);
if true && bar.$0
}
"#,
r#"
fn main() {
let bar = Some(true);
if true && let Some($0) = bar
}
"#,
);
check_edit(
"let",
r#"
//- minicore: option
fn main() {
let bar = Some(true);
if true && true && bar.$0
}
"#,
r#"
fn main() {
let bar = Some(true);
if true && true && let Some($0) = bar
}
"#,
);
}

View file

@ -19,12 +19,15 @@ use syntax::{
match_ast,
};
use crate::context::{
AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx,
DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind,
NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx, PathKind,
PatternContext, PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget,
TypeLocation,
use crate::{
completions::postfix::is_in_condition,
context::{
AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx,
DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind,
NameRefContext, NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathExprCtx,
PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx,
TypeAscriptionTarget, TypeLocation,
},
};
#[derive(Debug)]
@ -1188,24 +1191,6 @@ fn classify_name_ref<'db>(
Some(res)
};
fn is_in_condition(it: &ast::Expr) -> bool {
(|| {
let parent = it.syntax().parent()?;
if let Some(expr) = ast::WhileExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
} else if let Some(expr) = ast::IfExpr::cast(parent.clone()) {
Some(expr.condition()? == *it)
} else if let Some(expr) = ast::BinExpr::cast(parent)
&& expr.op_token()?.kind() == T![&&]
{
Some(is_in_condition(&expr.into()))
} else {
None
}
})()
.unwrap_or(false)
}
let make_path_kind_expr = |expr: ast::Expr| {
let it = expr.syntax();
let in_block_expr = is_in_block(it);