diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir_def/src/body/tests.rs | 2 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/hir_expand/src/builtin_macro.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/chalk_ext.rs | 35 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/unify.rs | 3 | ||||
-rw-r--r-- | crates/hir_ty/src/lib.rs | 32 | ||||
-rw-r--r-- | crates/ide/src/typing.rs | 168 | ||||
-rw-r--r-- | crates/rust-analyzer/src/caps.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 1 |
9 files changed, 194 insertions, 53 deletions
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index faa133297..c1d3e998f 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -143,7 +143,7 @@ fn f() { | |||
143 | //^^^^^^^^^^^^^ could not convert tokens | 143 | //^^^^^^^^^^^^^ could not convert tokens |
144 | 144 | ||
145 | env!("OUT_DIR"); | 145 | env!("OUT_DIR"); |
146 | //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix | 146 | //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix |
147 | 147 | ||
148 | compile_error!("compile_error works"); | 148 | compile_error!("compile_error works"); |
149 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works | 149 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index fefdadb22..1ac88fc89 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -233,7 +233,7 @@ fn good_out_dir_diagnostic() { | |||
233 | macro_rules! concat { () => {} } | 233 | macro_rules! concat { () => {} } |
234 | 234 | ||
235 | include!(concat!(env!("OUT_DIR"), "/out.rs")); | 235 | include!(concat!(env!("OUT_DIR"), "/out.rs")); |
236 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix | 236 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix |
237 | "#, | 237 | "#, |
238 | ); | 238 | ); |
239 | } | 239 | } |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 75ec4196b..a7d0f5b1f 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -490,7 +490,7 @@ fn env_expand( | |||
490 | // unnecessary diagnostics for eg. `CARGO_PKG_NAME`. | 490 | // unnecessary diagnostics for eg. `CARGO_PKG_NAME`. |
491 | if key == "OUT_DIR" { | 491 | if key == "OUT_DIR" { |
492 | err = Some(mbe::ExpandError::Other( | 492 | err = Some(mbe::ExpandError::Other( |
493 | r#"`OUT_DIR` not set, enable "load out dirs from check" to fix"#.into(), | 493 | r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(), |
494 | )); | 494 | )); |
495 | } | 495 | } |
496 | 496 | ||
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs index 4ea91787e..28ed3aac6 100644 --- a/crates/hir_ty/src/chalk_ext.rs +++ b/crates/hir_ty/src/chalk_ext.rs | |||
@@ -8,7 +8,7 @@ use hir_def::{ | |||
8 | use crate::{ | 8 | use crate::{ |
9 | db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, | 9 | db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, |
10 | from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId, | 10 | from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId, |
11 | CallableSig, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause, | 11 | CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause, |
12 | Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause, | 12 | Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause, |
13 | }; | 13 | }; |
14 | 14 | ||
@@ -34,6 +34,9 @@ pub trait TyExt { | |||
34 | 34 | ||
35 | fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>; | 35 | fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>; |
36 | fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>; | 36 | fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>; |
37 | |||
38 | /// FIXME: Get rid of this, it's not a good abstraction | ||
39 | fn equals_ctor(&self, other: &Ty) -> bool; | ||
37 | } | 40 | } |
38 | 41 | ||
39 | impl TyExt for Ty { | 42 | impl TyExt for Ty { |
@@ -238,6 +241,36 @@ impl TyExt for Ty { | |||
238 | _ => None, | 241 | _ => None, |
239 | } | 242 | } |
240 | } | 243 | } |
244 | |||
245 | fn equals_ctor(&self, other: &Ty) -> bool { | ||
246 | match (self.kind(&Interner), other.kind(&Interner)) { | ||
247 | (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, | ||
248 | (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_, _), TyKind::Array(_, _)) => { | ||
249 | true | ||
250 | } | ||
251 | (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2, | ||
252 | (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2, | ||
253 | (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => { | ||
254 | ty_id == ty_id2 | ||
255 | } | ||
256 | (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2, | ||
257 | (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2, | ||
258 | (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..)) | ||
259 | | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => { | ||
260 | mutability == mutability2 | ||
261 | } | ||
262 | ( | ||
263 | TyKind::Function(FnPointer { num_binders, sig, .. }), | ||
264 | TyKind::Function(FnPointer { num_binders: num_binders2, sig: sig2, .. }), | ||
265 | ) => num_binders == num_binders2 && sig == sig2, | ||
266 | (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => { | ||
267 | cardinality == cardinality2 | ||
268 | } | ||
269 | (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true, | ||
270 | (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2, | ||
271 | _ => false, | ||
272 | } | ||
273 | } | ||
241 | } | 274 | } |
242 | 275 | ||
243 | pub trait ProjectionTyExt { | 276 | pub trait ProjectionTyExt { |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index d717e3375..2ea9dd920 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -8,7 +8,8 @@ use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; | |||
8 | use super::{DomainGoal, InferenceContext}; | 8 | use super::{DomainGoal, InferenceContext}; |
9 | use crate::{ | 9 | use crate::{ |
10 | AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, FnSubst, | 10 | AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, FnSubst, |
11 | InEnvironment, InferenceVar, Interner, Scalar, Substitution, Ty, TyKind, TypeWalk, WhereClause, | 11 | InEnvironment, InferenceVar, Interner, Scalar, Substitution, Ty, TyExt, TyKind, TypeWalk, |
12 | WhereClause, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | impl<'a> InferenceContext<'a> { | 15 | impl<'a> InferenceContext<'a> { |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 915b1028f..87f10e9d5 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -164,37 +164,7 @@ impl CallableSig { | |||
164 | } | 164 | } |
165 | } | 165 | } |
166 | 166 | ||
167 | impl Ty { | 167 | impl Ty {} |
168 | pub fn equals_ctor(&self, other: &Ty) -> bool { | ||
169 | match (self.kind(&Interner), other.kind(&Interner)) { | ||
170 | (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, | ||
171 | (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_, _), TyKind::Array(_, _)) => { | ||
172 | true | ||
173 | } | ||
174 | (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2, | ||
175 | (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2, | ||
176 | (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => { | ||
177 | ty_id == ty_id2 | ||
178 | } | ||
179 | (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2, | ||
180 | (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2, | ||
181 | (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..)) | ||
182 | | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => { | ||
183 | mutability == mutability2 | ||
184 | } | ||
185 | ( | ||
186 | TyKind::Function(FnPointer { num_binders, sig, .. }), | ||
187 | TyKind::Function(FnPointer { num_binders: num_binders2, sig: sig2, .. }), | ||
188 | ) => num_binders == num_binders2 && sig == sig2, | ||
189 | (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => { | ||
190 | cardinality == cardinality2 | ||
191 | } | ||
192 | (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true, | ||
193 | (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2, | ||
194 | _ => false, | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | 168 | ||
199 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] | 169 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] |
200 | pub enum ImplTraitId { | 170 | pub enum ImplTraitId { |
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index 11408d445..1378048e5 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs | |||
@@ -22,18 +22,19 @@ use ide_db::{ | |||
22 | use syntax::{ | 22 | use syntax::{ |
23 | algo::find_node_at_offset, | 23 | algo::find_node_at_offset, |
24 | ast::{self, edit::IndentLevel, AstToken}, | 24 | ast::{self, edit::IndentLevel, AstToken}, |
25 | AstNode, SourceFile, | 25 | AstNode, Parse, SourceFile, |
26 | SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR}, | 26 | SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR}, |
27 | TextRange, TextSize, | 27 | TextRange, TextSize, |
28 | }; | 28 | }; |
29 | 29 | ||
30 | use text_edit::TextEdit; | 30 | use text_edit::{Indel, TextEdit}; |
31 | 31 | ||
32 | use crate::SourceChange; | 32 | 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 | // |
@@ -41,6 +42,7 @@ pub(crate) const TRIGGER_CHARS: &str = ".=>"; | |||
41 | // | 42 | // |
42 | // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression | 43 | // - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression |
43 | // - typing `.` in a chain method call auto-indents | 44 | // - typing `.` in a chain method call auto-indents |
45 | // - typing `{` in front of an expression inserts a closing `}` after the expression | ||
44 | // | 46 | // |
45 | // VS Code:: | 47 | // VS Code:: |
46 | // | 48 | // |
@@ -57,28 +59,79 @@ pub(crate) fn on_char_typed( | |||
57 | position: FilePosition, | 59 | position: FilePosition, |
58 | char_typed: char, | 60 | char_typed: char, |
59 | ) -> Option<SourceChange> { | 61 | ) -> Option<SourceChange> { |
60 | assert!(TRIGGER_CHARS.contains(char_typed)); | 62 | if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { |
61 | let file = &db.parse(position.file_id).tree(); | 63 | return None; |
62 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); | 64 | } |
65 | let file = &db.parse(position.file_id); | ||
66 | if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) { | ||
67 | return None; | ||
68 | } | ||
63 | let edit = on_char_typed_inner(file, position.offset, char_typed)?; | 69 | let edit = on_char_typed_inner(file, position.offset, char_typed)?; |
64 | Some(SourceChange::from_text_edit(position.file_id, edit)) | 70 | Some(SourceChange::from_text_edit(position.file_id, edit)) |
65 | } | 71 | } |
66 | 72 | ||
67 | fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { | 73 | fn on_char_typed_inner( |
68 | assert!(TRIGGER_CHARS.contains(char_typed)); | 74 | file: &Parse<SourceFile>, |
75 | offset: TextSize, | ||
76 | char_typed: char, | ||
77 | ) -> Option<TextEdit> { | ||
78 | if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { | ||
79 | return None; | ||
80 | } | ||
69 | match char_typed { | 81 | match char_typed { |
70 | '.' => on_dot_typed(file, offset), | 82 | '.' => on_dot_typed(&file.tree(), offset), |
71 | '=' => on_eq_typed(file, offset), | 83 | '=' => on_eq_typed(&file.tree(), offset), |
72 | '>' => on_arrow_typed(file, offset), | 84 | '>' => on_arrow_typed(&file.tree(), offset), |
85 | '{' => on_opening_brace_typed(file, offset), | ||
73 | _ => unreachable!(), | 86 | _ => unreachable!(), |
74 | } | 87 | } |
75 | } | 88 | } |
76 | 89 | ||
90 | /// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a | ||
91 | /// block. | ||
92 | fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { | ||
93 | if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { | ||
94 | return None; | ||
95 | } | ||
96 | |||
97 | let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?; | ||
98 | |||
99 | // Remove the `{` to get a better parse tree, and reparse | ||
100 | let file = file.reparse(&Indel::delete(brace_token.text_range())); | ||
101 | |||
102 | let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?; | ||
103 | if expr.syntax().text_range().start() != offset { | ||
104 | return None; | ||
105 | } | ||
106 | |||
107 | // Enclose the outermost expression starting at `offset` | ||
108 | while let Some(parent) = expr.syntax().parent() { | ||
109 | if parent.text_range().start() != expr.syntax().text_range().start() { | ||
110 | break; | ||
111 | } | ||
112 | |||
113 | match ast::Expr::cast(parent) { | ||
114 | Some(parent) => expr = parent, | ||
115 | None => break, | ||
116 | } | ||
117 | } | ||
118 | |||
119 | // If it's a statement in a block, we don't know how many statements should be included | ||
120 | if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { | ||
121 | return None; | ||
122 | } | ||
123 | |||
124 | // Insert `}` right after the expression. | ||
125 | Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string())) | ||
126 | } | ||
127 | |||
77 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 128 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
78 | /// this works when adding `let =`. | 129 | /// this works when adding `let =`. |
79 | // FIXME: use a snippet completion instead of this hack here. | 130 | // FIXME: use a snippet completion instead of this hack here. |
80 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | 131 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
81 | assert_eq!(file.syntax().text().char_at(offset), Some('=')); | 132 | if !stdx::always!(file.syntax().text().char_at(offset) == Some('=')) { |
133 | return None; | ||
134 | } | ||
82 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | 135 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; |
83 | if let_stmt.semicolon_token().is_some() { | 136 | if let_stmt.semicolon_token().is_some() { |
84 | return None; | 137 | return None; |
@@ -100,7 +153,9 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | |||
100 | 153 | ||
101 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. | 154 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. |
102 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | 155 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
103 | assert_eq!(file.syntax().text().char_at(offset), Some('.')); | 156 | if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) { |
157 | return None; | ||
158 | } | ||
104 | let whitespace = | 159 | let whitespace = |
105 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; | 160 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; |
106 | 161 | ||
@@ -129,7 +184,9 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | |||
129 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` | 184 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` |
130 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { | 185 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
131 | let file_text = file.syntax().text(); | 186 | let file_text = file.syntax().text(); |
132 | assert_eq!(file_text.char_at(offset), Some('>')); | 187 | if !stdx::always!(file_text.char_at(offset) == Some('>')) { |
188 | return None; | ||
189 | } | ||
133 | let after_arrow = offset + TextSize::of('>'); | 190 | let after_arrow = offset + TextSize::of('>'); |
134 | if file_text.char_at(after_arrow) != Some('{') { | 191 | if file_text.char_at(after_arrow) != Some('{') { |
135 | return None; | 192 | return None; |
@@ -152,7 +209,7 @@ mod tests { | |||
152 | let edit = TextEdit::insert(offset, char_typed.to_string()); | 209 | let edit = TextEdit::insert(offset, char_typed.to_string()); |
153 | edit.apply(&mut before); | 210 | edit.apply(&mut before); |
154 | let parse = SourceFile::parse(&before); | 211 | let parse = SourceFile::parse(&before); |
155 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { | 212 | on_char_typed_inner(&parse, offset, char_typed).map(|it| { |
156 | it.apply(&mut before); | 213 | it.apply(&mut before); |
157 | before.to_string() | 214 | before.to_string() |
158 | }) | 215 | }) |
@@ -373,4 +430,85 @@ fn main() { | |||
373 | fn adds_space_after_return_type() { | 430 | fn adds_space_after_return_type() { |
374 | type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }") | 431 | type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }") |
375 | } | 432 | } |
433 | |||
434 | #[test] | ||
435 | fn adds_closing_brace() { | ||
436 | type_char( | ||
437 | '{', | ||
438 | r#" | ||
439 | fn f() { match () { _ => $0() } } | ||
440 | "#, | ||
441 | r#" | ||
442 | fn f() { match () { _ => {()} } } | ||
443 | "#, | ||
444 | ); | ||
445 | type_char( | ||
446 | '{', | ||
447 | r#" | ||
448 | fn f() { $0() } | ||
449 | "#, | ||
450 | r#" | ||
451 | fn f() { {()} } | ||
452 | "#, | ||
453 | ); | ||
454 | type_char( | ||
455 | '{', | ||
456 | r#" | ||
457 | fn f() { let x = $0(); } | ||
458 | "#, | ||
459 | r#" | ||
460 | fn f() { let x = {()}; } | ||
461 | "#, | ||
462 | ); | ||
463 | type_char( | ||
464 | '{', | ||
465 | r#" | ||
466 | fn f() { let x = $0a.b(); } | ||
467 | "#, | ||
468 | r#" | ||
469 | fn f() { let x = {a.b()}; } | ||
470 | "#, | ||
471 | ); | ||
472 | type_char( | ||
473 | '{', | ||
474 | r#" | ||
475 | const S: () = $0(); | ||
476 | fn f() {} | ||
477 | "#, | ||
478 | r#" | ||
479 | const S: () = {()}; | ||
480 | fn f() {} | ||
481 | "#, | ||
482 | ); | ||
483 | type_char( | ||
484 | '{', | ||
485 | r#" | ||
486 | const S: () = $0a.b(); | ||
487 | fn f() {} | ||
488 | "#, | ||
489 | r#" | ||
490 | const S: () = {a.b()}; | ||
491 | fn f() {} | ||
492 | "#, | ||
493 | ); | ||
494 | type_char( | ||
495 | '{', | ||
496 | r#" | ||
497 | fn f() { | ||
498 | match x { | ||
499 | 0 => $0(), | ||
500 | 1 => (), | ||
501 | } | ||
502 | } | ||
503 | "#, | ||
504 | r#" | ||
505 | fn f() { | ||
506 | match x { | ||
507 | 0 => {()}, | ||
508 | 1 => (), | ||
509 | } | ||
510 | } | ||
511 | "#, | ||
512 | ); | ||
513 | } | ||
376 | } | 514 | } |
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`. | ||
235 | pub(crate) fn handle_on_type_formatting( | 234 | pub(crate) fn handle_on_type_formatting( |
236 | snap: GlobalStateSnapshot, | 235 | snap: GlobalStateSnapshot, |
237 | params: lsp_types::DocumentOnTypeFormattingParams, | 236 | params: lsp_types::DocumentOnTypeFormattingParams, |