diff options
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/typing.rs | 47 |
1 files changed, 46 insertions, 1 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 | ||
34 | pub(crate) use on_enter::on_enter; | 34 | pub(crate) use on_enter::on_enter; |
35 | 35 | ||
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 = ".=>{"; | ||
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. | ||
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 | |||
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 | } |