Add heuristic sensing is_in_block

Example
---
```rust
fn foo() -> [i32; 2] {
    l$0
    [0, n]
}
```

**Before this PR**:

```text
loop~
line!(…)~ macro_rules! line
```

**After this PR**:

```text
let~
loop~
letm~
line!(…)~ macro_rules! line
```
This commit is contained in:
A4-Tacks 2025-09-15 17:42:55 +08:00
parent 3643535c16
commit 911edbfe81
No known key found for this signature in database
GPG key ID: DBD861323040663B
2 changed files with 193 additions and 0 deletions

View file

@ -1890,11 +1890,37 @@ fn is_in_breakable(node: &SyntaxNode) -> Option<(BreakableKind, SyntaxNode)> {
}
fn is_in_block(node: &SyntaxNode) -> bool {
if has_in_newline_expr_first(node) {
return true;
};
node.parent()
.map(|node| ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind()))
.unwrap_or(false)
}
/// Similar to `has_parens`, heuristic sensing incomplete statement before ambigiguous `Expr`
///
/// Heuristic:
///
/// If the `PathExpr` is left part of the `Expr` and there is a newline after the `PathExpr`,
/// it is considered that the `PathExpr` is not part of the `Expr`.
fn has_in_newline_expr_first(node: &SyntaxNode) -> bool {
if ast::PathExpr::can_cast(node.kind())
&& let Some(NodeOrToken::Token(next)) = node.next_sibling_or_token()
&& next.kind() == SyntaxKind::WHITESPACE
&& next.text().contains('\n')
&& let Some(stmt_like) = node
.ancestors()
.take_while(|it| it.text_range().start() == node.text_range().start())
.filter_map(Either::<ast::ExprStmt, ast::Expr>::cast)
.last()
{
stmt_like.syntax().parent().and_then(ast::StmtList::cast).is_some()
} else {
false
}
}
fn next_non_trivia_token(e: impl Into<SyntaxElement>) -> Option<SyntaxToken> {
let mut token = match e.into() {
SyntaxElement::Node(n) => n.last_token()?,

View file

@ -2946,6 +2946,173 @@ fn let_in_let_chain() {
check_edit("let", r#"fn f() { if true && $0 {} }"#, r#"fn f() { if true && let $1 = $0 {} }"#);
}
#[test]
fn let_in_previous_line_of_ambiguous_expr() {
check_edit(
"let",
r#"
fn f() {
$0
(1, 2).foo();
}"#,
r#"
fn f() {
let $1 = $0;
(1, 2).foo();
}"#,
);
check_edit(
"let",
r#"
fn f() {
$0
(1, 2)
}"#,
r#"
fn f() {
let $1 = $0;
(1, 2)
}"#,
);
check_edit(
"let",
r#"
fn f() -> i32 {
$0
-2
}"#,
r#"
fn f() -> i32 {
let $1 = $0;
-2
}"#,
);
check_edit(
"let",
r#"
fn f() -> [i32; 2] {
$0
[1, 2]
}"#,
r#"
fn f() -> [i32; 2] {
let $1 = $0;
[1, 2]
}"#,
);
check_edit(
"let",
r#"
fn f() -> [u8; 2] {
$0
*b"01"
}"#,
r#"
fn f() -> [u8; 2] {
let $1 = $0;
*b"01"
}"#,
);
check(
r#"
fn foo() {
$0
*b"01"
}"#,
expect![[r#"
fn foo() fn()
bt u32 u32
kw async
kw const
kw crate::
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 foo() {
match $0 {}
}"#,
expect![[r#"
fn foo() fn()
bt u32 u32
kw const
kw crate::
kw false
kw for
kw if
kw if let
kw loop
kw match
kw return
kw self::
kw true
kw unsafe
kw while
kw while let
"#]],
);
check(
r#"
fn foo() {
$0 *b"01"
}"#,
expect![[r#"
fn foo() fn()
bt u32 u32
kw const
kw crate::
kw false
kw for
kw if
kw if let
kw loop
kw match
kw return
kw self::
kw true
kw unsafe
kw while
kw while let
"#]],
);
}
#[test]
fn private_inherent_and_public_trait() {
check(