Skip to content

Commit 36cd724

Browse files
Autoclose blocks when typing {
1 parent 8e900cb commit 36cd724

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

crates/ide/src/typing.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ use crate::SourceChange;
3333

3434
pub(crate) use on_enter::on_enter;
3535

36-
pub(crate) const TRIGGER_CHARS: &str = ".=>";
36+
// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
37+
pub(crate) const TRIGGER_CHARS: &str = ".=>{";
3738

3839
// Feature: On Typing Assists
3940
//
@@ -70,10 +71,47 @@ fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) ->
7071
'.' => on_dot_typed(file, offset),
7172
'=' => on_eq_typed(file, offset),
7273
'>' => on_arrow_typed(file, offset),
74+
'{' => on_opening_brace_typed(file, offset),
7375
_ => unreachable!(),
7476
}
7577
}
7678

79+
/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
80+
/// block.
81+
fn on_opening_brace_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
82+
assert_eq!(file.syntax().text().char_at(offset), Some('{'));
83+
let brace_token = file.syntax().token_at_offset(offset).right_biased()?;
84+
let block = ast::BlockExpr::cast(brace_token.parent()?)?;
85+
86+
// We expect a block expression enclosing exactly 1 preexisting expression. It can be parsed as
87+
// either the trailing expr or an ExprStmt.
88+
let offset = {
89+
match block.tail_expr() {
90+
Some(expr) => {
91+
if block.statements().next().is_some() {
92+
return None;
93+
}
94+
expr.syntax().text_range().end()
95+
}
96+
None => {
97+
if block.statements().count() != 1 {
98+
return None;
99+
}
100+
101+
match block.statements().next()? {
102+
ast::Stmt::ExprStmt(it) => {
103+
// Use the expression span to place `}` before the `;`
104+
it.expr()?.syntax().text_range().end()
105+
}
106+
_ => return None,
107+
}
108+
}
109+
}
110+
};
111+
112+
Some(TextEdit::insert(offset, "}".to_string()))
113+
}
114+
77115
/// Returns an edit which should be applied after `=` was typed. Primarily,
78116
/// this works when adding `let =`.
79117
// FIXME: use a snippet completion instead of this hack here.
@@ -373,4 +411,11 @@ fn main() {
373411
fn adds_space_after_return_type() {
374412
type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }")
375413
}
414+
415+
#[test]
416+
fn adds_closing_brace() {
417+
type_char('{', r"fn f() { match () { _ => $0() } }", r"fn f() { match () { _ => {()} } }");
418+
type_char('{', r"fn f() { $0(); }", r"fn f() { {()}; }");
419+
type_char('{', r"fn f() { let x = $0(); }", r"fn f() { let x = {()}; }");
420+
}
376421
}

crates/rust-analyzer/src/caps.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
5757
document_range_formatting_provider: None,
5858
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
5959
first_trigger_character: "=".to_string(),
60-
more_trigger_character: Some(vec![".".to_string(), ">".to_string()]),
60+
more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
6161
}),
6262
selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
6363
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),

crates/rust-analyzer/src/handlers.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,6 @@ pub(crate) fn handle_on_enter(
231231
Ok(Some(edit))
232232
}
233233

234-
// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
235234
pub(crate) fn handle_on_type_formatting(
236235
snap: GlobalStateSnapshot,
237236
params: lsp_types::DocumentOnTypeFormattingParams,

0 commit comments

Comments
 (0)