aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-04-06 22:55:39 +0100
committerJonas Schievink <[email protected]>2021-04-07 15:38:04 +0100
commit36cd724b7b146c33804db4b110111ad71be9cb72 (patch)
treec23b6b685a78c92a02d7102f7c5204dd5c16ba47
parent8e900cb4a1c5a4faef801518272d56a5683dd488 (diff)
Autoclose blocks when typing `{`
-rw-r--r--crates/ide/src/typing.rs47
-rw-r--r--crates/rust-analyzer/src/caps.rs2
-rw-r--r--crates/rust-analyzer/src/handlers.rs1
3 files changed, 47 insertions, 3 deletions
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 11408d445..de65632e3 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -33,7 +33,8 @@ use crate::SourceChange;
33 33
34pub(crate) use on_enter::on_enter; 34pub(crate) use on_enter::on_enter;
35 35
36pub(crate) const TRIGGER_CHARS: &str = ".=>"; 36// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
37pub(crate) const TRIGGER_CHARS: &str = ".=>{";
37 38
38// Feature: On Typing Assists 39// Feature: On Typing Assists
39// 40//
@@ -70,10 +71,47 @@ fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) ->
70 '.' => on_dot_typed(file, offset), 71 '.' => on_dot_typed(file, offset),
71 '=' => on_eq_typed(file, offset), 72 '=' => on_eq_typed(file, offset),
72 '>' => on_arrow_typed(file, offset), 73 '>' => on_arrow_typed(file, offset),
74 '{' => on_opening_brace_typed(file, offset),
73 _ => unreachable!(), 75 _ => unreachable!(),
74 } 76 }
75} 77}
76 78
79/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
80/// block.
81fn 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
77/// Returns an edit which should be applied after `=` was typed. Primarily, 115/// Returns an edit which should be applied after `=` was typed. Primarily,
78/// this works when adding `let =`. 116/// this works when adding `let =`.
79// FIXME: use a snippet completion instead of this hack here. 117// FIXME: use a snippet completion instead of this hack here.
@@ -373,4 +411,11 @@ fn main() {
373 fn adds_space_after_return_type() { 411 fn adds_space_after_return_type() {
374 type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }") 412 type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }")
375 } 413 }
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 }
376} 421}
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 7a5bcb8c7..3c87782f2 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -57,7 +57,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
57 document_range_formatting_provider: None, 57 document_range_formatting_provider: None,
58 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 58 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
59 first_trigger_character: "=".to_string(), 59 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()]),
61 }), 61 }),
62 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), 62 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
63 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), 63 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4d10a2ead..31d8c487b 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -231,7 +231,6 @@ pub(crate) fn handle_on_enter(
231 Ok(Some(edit)) 231 Ok(Some(edit))
232} 232}
233 233
234// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
235pub(crate) fn handle_on_type_formatting( 234pub(crate) fn handle_on_type_formatting(
236 snap: GlobalStateSnapshot, 235 snap: GlobalStateSnapshot,
237 params: lsp_types::DocumentOnTypeFormattingParams, 236 params: lsp_types::DocumentOnTypeFormattingParams,