diff options
author | Aleksey Kladov <[email protected]> | 2018-08-28 09:12:42 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-28 09:12:42 +0100 |
commit | 2fa90e736b026ee979d9eb59178dc1f792228250 (patch) | |
tree | adba3a569241a2030bf66e4e0b24ac5ffeaccbc3 /crates | |
parent | 13110f48e948d7554500aefc336e72f96041386b (diff) |
better recovery for exprs
Diffstat (limited to 'crates')
-rw-r--r-- | crates/libeditor/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/libeditor/src/typing.rs | 24 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 46 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/generated.rs | 78 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/mod.rs | 9 | ||||
-rw-r--r-- | crates/libsyntax2/src/grammar.ron | 10 | ||||
-rw-r--r-- | crates/libsyntax2/src/grammar/expressions/atom.rs | 5 | ||||
-rw-r--r-- | crates/libsyntax2/src/parser_api.rs | 19 | ||||
-rw-r--r-- | crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt | 5 | ||||
-rw-r--r-- | crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt | 19 | ||||
-rw-r--r-- | crates/libsyntax2/tests/data/parser/err/0019_let_recover.rs | 4 | ||||
-rw-r--r-- | crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt | 39 | ||||
-rw-r--r-- | crates/server/src/caps.rs | 6 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 19 | ||||
-rw-r--r-- | crates/server/src/main_loop/mod.rs | 4 | ||||
-rw-r--r-- | crates/server/src/req.rs | 1 |
16 files changed, 263 insertions, 27 deletions
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index d39e56d81..34056b3c0 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs | |||
@@ -27,7 +27,7 @@ pub use self::{ | |||
27 | ActionResult, | 27 | ActionResult, |
28 | flip_comma, add_derive, add_impl, | 28 | flip_comma, add_derive, add_impl, |
29 | }, | 29 | }, |
30 | typing::join_lines, | 30 | typing::{join_lines, on_eq_typed}, |
31 | completion::scope_completion, | 31 | completion::scope_completion, |
32 | }; | 32 | }; |
33 | 33 | ||
diff --git a/crates/libeditor/src/typing.rs b/crates/libeditor/src/typing.rs index 060095f28..48a8d6bb0 100644 --- a/crates/libeditor/src/typing.rs +++ b/crates/libeditor/src/typing.rs | |||
@@ -7,11 +7,11 @@ use libsyntax2::{ | |||
7 | walk::preorder, | 7 | walk::preorder, |
8 | find_covering_node, | 8 | find_covering_node, |
9 | }, | 9 | }, |
10 | text_utils::intersect, | 10 | text_utils::{intersect, contains_offset_nonstrict}, |
11 | SyntaxKind::*, | 11 | SyntaxKind::*, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use {ActionResult, EditBuilder}; | 14 | use {ActionResult, EditBuilder, find_node_at_offset}; |
15 | 15 | ||
16 | pub fn join_lines(file: &File, range: TextRange) -> ActionResult { | 16 | pub fn join_lines(file: &File, range: TextRange) -> ActionResult { |
17 | let range = if range.is_empty() { | 17 | let range = if range.is_empty() { |
@@ -56,6 +56,26 @@ pub fn join_lines(file: &File, range: TextRange) -> ActionResult { | |||
56 | } | 56 | } |
57 | } | 57 | } |
58 | 58 | ||
59 | pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<ActionResult> { | ||
60 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | ||
61 | if let_stmt.has_semi() { | ||
62 | return None; | ||
63 | } | ||
64 | if let Some(expr) = let_stmt.initializer() { | ||
65 | let expr_range = expr.syntax().range(); | ||
66 | if contains_offset_nonstrict(expr_range, offset) && offset != expr_range.start() { | ||
67 | return None; | ||
68 | } | ||
69 | } | ||
70 | let offset = let_stmt.syntax().range().end(); | ||
71 | let mut edit = EditBuilder::new(); | ||
72 | edit.insert(offset, ";".to_string()); | ||
73 | Some(ActionResult { | ||
74 | edit: edit.finish(), | ||
75 | cursor_position: None, | ||
76 | }) | ||
77 | } | ||
78 | |||
59 | fn remove_newline( | 79 | fn remove_newline( |
60 | edit: &mut EditBuilder, | 80 | edit: &mut EditBuilder, |
61 | node: SyntaxNodeRef, | 81 | node: SyntaxNodeRef, |
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index d8c24610d..17926d5ae 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs | |||
@@ -9,7 +9,7 @@ use libeditor::{ | |||
9 | ActionResult, | 9 | ActionResult, |
10 | highlight, runnables, extend_selection, file_structure, | 10 | highlight, runnables, extend_selection, file_structure, |
11 | flip_comma, add_derive, add_impl, matching_brace, | 11 | flip_comma, add_derive, add_impl, matching_brace, |
12 | join_lines, scope_completion, | 12 | join_lines, on_eq_typed, scope_completion, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | #[test] | 15 | #[test] |
@@ -227,7 +227,7 @@ pub fn reparse(&self, edit: &AtomEdit) -> File { | |||
227 | #[test] | 227 | #[test] |
228 | fn test_join_lines_selection() { | 228 | fn test_join_lines_selection() { |
229 | fn do_check(before: &str, after: &str) { | 229 | fn do_check(before: &str, after: &str) { |
230 | let (sel, before) = extract_range(&before); | 230 | let (sel, before) = extract_range(before); |
231 | let file = file(&before); | 231 | let file = file(&before); |
232 | let result = join_lines(&file, sel); | 232 | let result = join_lines(&file, sel); |
233 | let actual = result.edit.apply(&before); | 233 | let actual = result.edit.apply(&before); |
@@ -257,6 +257,48 @@ struct Foo { f: u32 } | |||
257 | } | 257 | } |
258 | 258 | ||
259 | #[test] | 259 | #[test] |
260 | fn test_on_eq_typed() { | ||
261 | fn do_check(before: &str, after: &str) { | ||
262 | let (offset, before) = extract_offset(before); | ||
263 | let file = file(&before); | ||
264 | let result = on_eq_typed(&file, offset).unwrap(); | ||
265 | let actual = result.edit.apply(&before); | ||
266 | assert_eq_text!(after, &actual); | ||
267 | } | ||
268 | |||
269 | do_check(r" | ||
270 | fn foo() { | ||
271 | let foo =<|> | ||
272 | } | ||
273 | ", r" | ||
274 | fn foo() { | ||
275 | let foo =; | ||
276 | } | ||
277 | "); | ||
278 | do_check(r" | ||
279 | fn foo() { | ||
280 | let foo =<|> 1 + 1 | ||
281 | } | ||
282 | ", r" | ||
283 | fn foo() { | ||
284 | let foo = 1 + 1; | ||
285 | } | ||
286 | "); | ||
287 | // do_check(r" | ||
288 | // fn foo() { | ||
289 | // let foo =<|> | ||
290 | // let bar = 1; | ||
291 | // } | ||
292 | // ", r" | ||
293 | // fn foo() { | ||
294 | // let foo =; | ||
295 | // let bar = 1; | ||
296 | // } | ||
297 | // "); | ||
298 | |||
299 | } | ||
300 | |||
301 | #[test] | ||
260 | fn test_completion() { | 302 | fn test_completion() { |
261 | fn do_check(code: &str, expected_completions: &str) { | 303 | fn do_check(code: &str, expected_completions: &str) { |
262 | let (off, code) = extract_offset(&code); | 304 | let (off, code) = extract_offset(&code); |
diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index f99d1274a..6181aada8 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs | |||
@@ -439,6 +439,24 @@ impl<'a> ExprStmt<'a> { | |||
439 | } | 439 | } |
440 | } | 440 | } |
441 | 441 | ||
442 | // ExternCrateItem | ||
443 | #[derive(Debug, Clone, Copy)] | ||
444 | pub struct ExternCrateItem<'a> { | ||
445 | syntax: SyntaxNodeRef<'a>, | ||
446 | } | ||
447 | |||
448 | impl<'a> AstNode<'a> for ExternCrateItem<'a> { | ||
449 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
450 | match syntax.kind() { | ||
451 | EXTERN_CRATE_ITEM => Some(ExternCrateItem { syntax }), | ||
452 | _ => None, | ||
453 | } | ||
454 | } | ||
455 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
456 | } | ||
457 | |||
458 | impl<'a> ExternCrateItem<'a> {} | ||
459 | |||
442 | // FieldExpr | 460 | // FieldExpr |
443 | #[derive(Debug, Clone, Copy)] | 461 | #[derive(Debug, Clone, Copy)] |
444 | pub struct FieldExpr<'a> { | 462 | pub struct FieldExpr<'a> { |
@@ -839,11 +857,51 @@ impl<'a> AstNode<'a> for Module<'a> { | |||
839 | impl<'a> ast::NameOwner<'a> for Module<'a> {} | 857 | impl<'a> ast::NameOwner<'a> for Module<'a> {} |
840 | impl<'a> ast::AttrsOwner<'a> for Module<'a> {} | 858 | impl<'a> ast::AttrsOwner<'a> for Module<'a> {} |
841 | impl<'a> Module<'a> { | 859 | impl<'a> Module<'a> { |
842 | pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a { | 860 | pub fn items(self) -> impl Iterator<Item = ModuleItem<'a>> + 'a { |
843 | super::children(self) | 861 | super::children(self) |
844 | } | 862 | } |
845 | } | 863 | } |
846 | 864 | ||
865 | // ModuleItem | ||
866 | #[derive(Debug, Clone, Copy)] | ||
867 | pub enum ModuleItem<'a> { | ||
868 | StructDef(StructDef<'a>), | ||
869 | EnumDef(EnumDef<'a>), | ||
870 | FnDef(FnDef<'a>), | ||
871 | TraitDef(TraitDef<'a>), | ||
872 | ImplItem(ImplItem<'a>), | ||
873 | UseItem(UseItem<'a>), | ||
874 | ExternCrateItem(ExternCrateItem<'a>), | ||
875 | } | ||
876 | |||
877 | impl<'a> AstNode<'a> for ModuleItem<'a> { | ||
878 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
879 | match syntax.kind() { | ||
880 | STRUCT_DEF => Some(ModuleItem::StructDef(StructDef { syntax })), | ||
881 | ENUM_DEF => Some(ModuleItem::EnumDef(EnumDef { syntax })), | ||
882 | FN_DEF => Some(ModuleItem::FnDef(FnDef { syntax })), | ||
883 | TRAIT_DEF => Some(ModuleItem::TraitDef(TraitDef { syntax })), | ||
884 | IMPL_ITEM => Some(ModuleItem::ImplItem(ImplItem { syntax })), | ||
885 | USE_ITEM => Some(ModuleItem::UseItem(UseItem { syntax })), | ||
886 | EXTERN_CRATE_ITEM => Some(ModuleItem::ExternCrateItem(ExternCrateItem { syntax })), | ||
887 | _ => None, | ||
888 | } | ||
889 | } | ||
890 | fn syntax(self) -> SyntaxNodeRef<'a> { | ||
891 | match self { | ||
892 | ModuleItem::StructDef(inner) => inner.syntax(), | ||
893 | ModuleItem::EnumDef(inner) => inner.syntax(), | ||
894 | ModuleItem::FnDef(inner) => inner.syntax(), | ||
895 | ModuleItem::TraitDef(inner) => inner.syntax(), | ||
896 | ModuleItem::ImplItem(inner) => inner.syntax(), | ||
897 | ModuleItem::UseItem(inner) => inner.syntax(), | ||
898 | ModuleItem::ExternCrateItem(inner) => inner.syntax(), | ||
899 | } | ||
900 | } | ||
901 | } | ||
902 | |||
903 | impl<'a> ModuleItem<'a> {} | ||
904 | |||
847 | // Name | 905 | // Name |
848 | #[derive(Debug, Clone, Copy)] | 906 | #[derive(Debug, Clone, Copy)] |
849 | pub struct Name<'a> { | 907 | pub struct Name<'a> { |
@@ -1762,6 +1820,24 @@ impl<'a> AstNode<'a> for TypeRef<'a> { | |||
1762 | 1820 | ||
1763 | impl<'a> TypeRef<'a> {} | 1821 | impl<'a> TypeRef<'a> {} |
1764 | 1822 | ||
1823 | // UseItem | ||
1824 | #[derive(Debug, Clone, Copy)] | ||
1825 | pub struct UseItem<'a> { | ||
1826 | syntax: SyntaxNodeRef<'a>, | ||
1827 | } | ||
1828 | |||
1829 | impl<'a> AstNode<'a> for UseItem<'a> { | ||
1830 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
1831 | match syntax.kind() { | ||
1832 | USE_ITEM => Some(UseItem { syntax }), | ||
1833 | _ => None, | ||
1834 | } | ||
1835 | } | ||
1836 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
1837 | } | ||
1838 | |||
1839 | impl<'a> UseItem<'a> {} | ||
1840 | |||
1765 | // WhereClause | 1841 | // WhereClause |
1766 | #[derive(Debug, Clone, Copy)] | 1842 | #[derive(Debug, Clone, Copy)] |
1767 | pub struct WhereClause<'a> { | 1843 | pub struct WhereClause<'a> { |
diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs index 2ebee6a4f..9941138a7 100644 --- a/crates/libsyntax2/src/ast/mod.rs +++ b/crates/libsyntax2/src/ast/mod.rs | |||
@@ -115,6 +115,15 @@ impl<'a> Module<'a> { | |||
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
118 | impl<'a> LetStmt<'a> { | ||
119 | pub fn has_semi(self) -> bool { | ||
120 | match self.syntax().last_child() { | ||
121 | None => false, | ||
122 | Some(node) => node.kind() == SEMI, | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
118 | impl<'a> IfExpr<'a> { | 127 | impl<'a> IfExpr<'a> { |
119 | pub fn then_branch(self) -> Option<Block<'a>> { | 128 | pub fn then_branch(self) -> Option<Block<'a>> { |
120 | self.blocks().nth(0) | 129 | self.blocks().nth(0) |
diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index a98e9e2fd..7217a4633 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron | |||
@@ -273,7 +273,7 @@ Grammar( | |||
273 | "Module": ( | 273 | "Module": ( |
274 | traits: ["NameOwner", "AttrsOwner"], | 274 | traits: ["NameOwner", "AttrsOwner"], |
275 | collections: [ | 275 | collections: [ |
276 | ["modules", "Module"] | 276 | ["items", "ModuleItem"] |
277 | ] | 277 | ] |
278 | ), | 278 | ), |
279 | "ConstDef": ( traits: [ | 279 | "ConstDef": ( traits: [ |
@@ -331,6 +331,10 @@ Grammar( | |||
331 | "AttrsOwner" | 331 | "AttrsOwner" |
332 | ], | 332 | ], |
333 | ), | 333 | ), |
334 | "ModuleItem": ( | ||
335 | enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "ImplItem", | ||
336 | "UseItem", "ExternCrateItem" ] | ||
337 | ), | ||
334 | 338 | ||
335 | "TupleExpr": (), | 339 | "TupleExpr": (), |
336 | "ArrayExpr": (), | 340 | "ArrayExpr": (), |
@@ -479,6 +483,8 @@ Grammar( | |||
479 | ), | 483 | ), |
480 | "Param": ( | 484 | "Param": ( |
481 | options: [["pat", "Pat"]], | 485 | options: [["pat", "Pat"]], |
482 | ) | 486 | ), |
487 | "UseItem": (), | ||
488 | "ExternCrateItem": (), | ||
483 | }, | 489 | }, |
484 | ) | 490 | ) |
diff --git a/crates/libsyntax2/src/grammar/expressions/atom.rs b/crates/libsyntax2/src/grammar/expressions/atom.rs index ab4aa49d2..0769bb5a8 100644 --- a/crates/libsyntax2/src/grammar/expressions/atom.rs +++ b/crates/libsyntax2/src/grammar/expressions/atom.rs | |||
@@ -33,6 +33,9 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet = | |||
33 | RETURN_KW, IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ], | 33 | RETURN_KW, IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ], |
34 | ]; | 34 | ]; |
35 | 35 | ||
36 | const EXPR_RECOVERY_SET: TokenSet = | ||
37 | token_set![LET_KW]; | ||
38 | |||
36 | pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> { | 39 | pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> { |
37 | match literal(p) { | 40 | match literal(p) { |
38 | Some(m) => return Some(m), | 41 | Some(m) => return Some(m), |
@@ -73,7 +76,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMark | |||
73 | CONTINUE_KW => continue_expr(p), | 76 | CONTINUE_KW => continue_expr(p), |
74 | BREAK_KW => break_expr(p), | 77 | BREAK_KW => break_expr(p), |
75 | _ => { | 78 | _ => { |
76 | p.err_and_bump("expected expression"); | 79 | p.err_recover("expected expression", EXPR_RECOVERY_SET); |
77 | return None; | 80 | return None; |
78 | } | 81 | } |
79 | }; | 82 | }; |
diff --git a/crates/libsyntax2/src/parser_api.rs b/crates/libsyntax2/src/parser_api.rs index 0a3b29b70..10b9b64ac 100644 --- a/crates/libsyntax2/src/parser_api.rs +++ b/crates/libsyntax2/src/parser_api.rs | |||
@@ -12,6 +12,8 @@ fn mask(kind: SyntaxKind) -> u128 { | |||
12 | } | 12 | } |
13 | 13 | ||
14 | impl TokenSet { | 14 | impl TokenSet { |
15 | const EMPTY: TokenSet = TokenSet(0); | ||
16 | |||
15 | pub fn contains(&self, kind: SyntaxKind) -> bool { | 17 | pub fn contains(&self, kind: SyntaxKind) -> bool { |
16 | self.0 & mask(kind) != 0 | 18 | self.0 & mask(kind) != 0 |
17 | } | 19 | } |
@@ -139,12 +141,21 @@ impl<'t> Parser<'t> { | |||
139 | 141 | ||
140 | /// Create an error node and consume the next token. | 142 | /// Create an error node and consume the next token. |
141 | pub(crate) fn err_and_bump(&mut self, message: &str) { | 143 | pub(crate) fn err_and_bump(&mut self, message: &str) { |
142 | let m = self.start(); | 144 | self.err_recover(message, TokenSet::EMPTY); |
143 | self.error(message); | 145 | } |
144 | if !self.at(SyntaxKind::L_CURLY) && !self.at(SyntaxKind::R_CURLY) { | 146 | |
147 | /// Create an error node and consume the next token. | ||
148 | pub(crate) fn err_recover(&mut self, message: &str, recovery_set: TokenSet) { | ||
149 | if self.at(SyntaxKind::L_CURLY) | ||
150 | || self.at(SyntaxKind::R_CURLY) | ||
151 | || recovery_set.contains(self.current()) { | ||
152 | self.error(message); | ||
153 | } else { | ||
154 | let m = self.start(); | ||
155 | self.error(message); | ||
145 | self.bump(); | 156 | self.bump(); |
157 | m.complete(self, ERROR); | ||
146 | } | 158 | } |
147 | m.complete(self, ERROR); | ||
148 | } | 159 | } |
149 | } | 160 | } |
150 | 161 | ||
diff --git a/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt index db9a2f175..f0be287ad 100644 --- a/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt +++ b/crates/libsyntax2/tests/data/parser/err/0017_incomplete_binexpr.txt | |||
@@ -35,13 +35,12 @@ ROOT@[0; 47) | |||
35 | INT_NUMBER@[33; 35) "92" | 35 | INT_NUMBER@[33; 35) "92" |
36 | SEMI@[35; 36) | 36 | SEMI@[35; 36) |
37 | WHITESPACE@[36; 41) | 37 | WHITESPACE@[36; 41) |
38 | BIN_EXPR@[41; 45) | 38 | BIN_EXPR@[41; 44) |
39 | LITERAL@[41; 42) | 39 | LITERAL@[41; 42) |
40 | INT_NUMBER@[41; 42) "1" | 40 | INT_NUMBER@[41; 42) "1" |
41 | WHITESPACE@[42; 43) | 41 | WHITESPACE@[42; 43) |
42 | PLUS@[43; 44) | 42 | PLUS@[43; 44) |
43 | WHITESPACE@[44; 45) | ||
44 | err: `expected expression` | 43 | err: `expected expression` |
45 | ERROR@[45; 45) | 44 | WHITESPACE@[44; 45) |
46 | R_CURLY@[45; 46) | 45 | R_CURLY@[45; 46) |
47 | WHITESPACE@[46; 47) | 46 | WHITESPACE@[46; 47) |
diff --git a/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt b/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt index 3f3a7784b..58e39a341 100644 --- a/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt +++ b/crates/libsyntax2/tests/data/parser/err/0018_incomplete_fn.txt | |||
@@ -11,12 +11,12 @@ ROOT@[0; 183) | |||
11 | ITEM_LIST@[14; 182) | 11 | ITEM_LIST@[14; 182) |
12 | L_CURLY@[14; 15) | 12 | L_CURLY@[14; 15) |
13 | WHITESPACE@[15; 20) | 13 | WHITESPACE@[15; 20) |
14 | FN_DEF@[20; 181) | 14 | FN_DEF@[20; 180) |
15 | FN_KW@[20; 22) | 15 | FN_KW@[20; 22) |
16 | WHITESPACE@[22; 23) | 16 | WHITESPACE@[22; 23) |
17 | NAME@[23; 32) | 17 | NAME@[23; 32) |
18 | IDENT@[23; 32) "new_scope" | 18 | IDENT@[23; 32) "new_scope" |
19 | PARAM_LIST@[32; 181) | 19 | PARAM_LIST@[32; 180) |
20 | L_PAREN@[32; 33) | 20 | L_PAREN@[32; 33) |
21 | PARAM@[33; 38) | 21 | PARAM@[33; 38) |
22 | REF_PAT@[33; 35) | 22 | REF_PAT@[33; 35) |
@@ -163,17 +163,16 @@ ROOT@[0; 183) | |||
163 | err: `expected parameters` | 163 | err: `expected parameters` |
164 | err: `expected COMMA` | 164 | err: `expected COMMA` |
165 | WHITESPACE@[169; 170) | 165 | WHITESPACE@[169; 170) |
166 | PARAM@[170; 181) | 166 | PARAM@[170; 180) |
167 | BIND_PAT@[170; 180) | 167 | BIND_PAT@[170; 180) |
168 | NAME@[170; 180) | 168 | NAME@[170; 180) |
169 | IDENT@[170; 180) "set_parent" | 169 | IDENT@[170; 180) "set_parent" |
170 | err: `expected COLON` | 170 | err: `expected COLON` |
171 | WHITESPACE@[180; 181) | 171 | err: `expected type` |
172 | err: `expected type` | 172 | err: `expected COMMA` |
173 | err: `expected COMMA` | 173 | err: `expected value parameter` |
174 | err: `expected value parameter` | 174 | err: `expected R_PAREN` |
175 | err: `expected R_PAREN` | 175 | err: `expected a block` |
176 | err: `expected a block` | 176 | WHITESPACE@[180; 181) |
177 | ERROR@[181; 181) | ||
178 | R_CURLY@[181; 182) | 177 | R_CURLY@[181; 182) |
179 | WHITESPACE@[182; 183) | 178 | WHITESPACE@[182; 183) |
diff --git a/crates/libsyntax2/tests/data/parser/err/0019_let_recover.rs b/crates/libsyntax2/tests/data/parser/err/0019_let_recover.rs new file mode 100644 index 000000000..52bddd494 --- /dev/null +++ b/crates/libsyntax2/tests/data/parser/err/0019_let_recover.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | fn foo() { | ||
2 | let foo = | ||
3 | let bar = 1; | ||
4 | } | ||
diff --git a/crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt b/crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt new file mode 100644 index 000000000..9ffa9d781 --- /dev/null +++ b/crates/libsyntax2/tests/data/parser/err/0019_let_recover.txt | |||
@@ -0,0 +1,39 @@ | |||
1 | ROOT@[0; 44) | ||
2 | FN_DEF@[0; 43) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 6) | ||
6 | IDENT@[3; 6) "foo" | ||
7 | PARAM_LIST@[6; 8) | ||
8 | L_PAREN@[6; 7) | ||
9 | R_PAREN@[7; 8) | ||
10 | WHITESPACE@[8; 9) | ||
11 | BLOCK@[9; 43) | ||
12 | L_CURLY@[9; 10) | ||
13 | WHITESPACE@[10; 15) | ||
14 | LET_STMT@[15; 24) | ||
15 | LET_KW@[15; 18) | ||
16 | WHITESPACE@[18; 19) | ||
17 | BIND_PAT@[19; 22) | ||
18 | NAME@[19; 22) | ||
19 | IDENT@[19; 22) "foo" | ||
20 | WHITESPACE@[22; 23) | ||
21 | EQ@[23; 24) | ||
22 | err: `expected expression` | ||
23 | err: `expected SEMI` | ||
24 | WHITESPACE@[24; 29) | ||
25 | LET_STMT@[29; 41) | ||
26 | LET_KW@[29; 32) | ||
27 | WHITESPACE@[32; 33) | ||
28 | BIND_PAT@[33; 36) | ||
29 | NAME@[33; 36) | ||
30 | IDENT@[33; 36) "bar" | ||
31 | WHITESPACE@[36; 37) | ||
32 | EQ@[37; 38) | ||
33 | WHITESPACE@[38; 39) | ||
34 | LITERAL@[39; 40) | ||
35 | INT_NUMBER@[39; 40) "1" | ||
36 | SEMI@[40; 41) | ||
37 | WHITESPACE@[41; 42) | ||
38 | R_CURLY@[42; 43) | ||
39 | WHITESPACE@[43; 44) | ||
diff --git a/crates/server/src/caps.rs b/crates/server/src/caps.rs index 98499bb05..7456aea8a 100644 --- a/crates/server/src/caps.rs +++ b/crates/server/src/caps.rs | |||
@@ -5,6 +5,7 @@ use languageserver_types::{ | |||
5 | TextDocumentSyncKind, | 5 | TextDocumentSyncKind, |
6 | ExecuteCommandOptions, | 6 | ExecuteCommandOptions, |
7 | CompletionOptions, | 7 | CompletionOptions, |
8 | DocumentOnTypeFormattingOptions, | ||
8 | }; | 9 | }; |
9 | 10 | ||
10 | pub fn server_capabilities() -> ServerCapabilities { | 11 | pub fn server_capabilities() -> ServerCapabilities { |
@@ -35,7 +36,10 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
35 | code_lens_provider: None, | 36 | code_lens_provider: None, |
36 | document_formatting_provider: None, | 37 | document_formatting_provider: None, |
37 | document_range_formatting_provider: None, | 38 | document_range_formatting_provider: None, |
38 | document_on_type_formatting_provider: None, | 39 | document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { |
40 | first_trigger_character: "=".to_string(), | ||
41 | more_trigger_character: None, | ||
42 | }), | ||
39 | rename_provider: None, | 43 | rename_provider: None, |
40 | color_provider: None, | 44 | color_provider: None, |
41 | execute_command_provider: Some(ExecuteCommandOptions { | 45 | execute_command_provider: Some(ExecuteCommandOptions { |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index ec5421f06..ca5cd5ab1 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -314,6 +314,25 @@ pub fn handle_completion( | |||
314 | Ok(Some(req::CompletionResponse::Array(items))) | 314 | Ok(Some(req::CompletionResponse::Array(items))) |
315 | } | 315 | } |
316 | 316 | ||
317 | pub fn handle_on_type_formatting( | ||
318 | world: ServerWorld, | ||
319 | params: req::DocumentOnTypeFormattingParams, | ||
320 | ) -> Result<Option<Vec<TextEdit>>> { | ||
321 | if params.ch != "=" { | ||
322 | return Ok(None); | ||
323 | } | ||
324 | |||
325 | let file_id = params.text_document.try_conv_with(&world)?; | ||
326 | let line_index = world.analysis().file_line_index(file_id)?; | ||
327 | let offset = params.position.conv_with(&line_index); | ||
328 | let file = world.analysis().file_syntax(file_id)?; | ||
329 | let action = match libeditor::on_eq_typed(&file, offset) { | ||
330 | None => return Ok(None), | ||
331 | Some(action) => action, | ||
332 | }; | ||
333 | Ok(Some(action.edit.conv_with(&line_index))) | ||
334 | } | ||
335 | |||
317 | pub fn handle_execute_command( | 336 | pub fn handle_execute_command( |
318 | world: ServerWorld, | 337 | world: ServerWorld, |
319 | mut params: req::ExecuteCommandParams, | 338 | mut params: req::ExecuteCommandParams, |
diff --git a/crates/server/src/main_loop/mod.rs b/crates/server/src/main_loop/mod.rs index da1121cb4..accb13878 100644 --- a/crates/server/src/main_loop/mod.rs +++ b/crates/server/src/main_loop/mod.rs | |||
@@ -31,6 +31,7 @@ use { | |||
31 | handle_completion, | 31 | handle_completion, |
32 | handle_runnables, | 32 | handle_runnables, |
33 | handle_decorations, | 33 | handle_decorations, |
34 | handle_on_type_formatting, | ||
34 | }, | 35 | }, |
35 | }; | 36 | }; |
36 | 37 | ||
@@ -161,6 +162,9 @@ fn on_request( | |||
161 | handle_request_on_threadpool::<req::DecorationsRequest>( | 162 | handle_request_on_threadpool::<req::DecorationsRequest>( |
162 | &mut req, pool, world, sender, handle_decorations, | 163 | &mut req, pool, world, sender, handle_decorations, |
163 | )?; | 164 | )?; |
165 | handle_request_on_threadpool::<req::OnTypeFormatting>( | ||
166 | &mut req, pool, world, sender, handle_on_type_formatting, | ||
167 | )?; | ||
164 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { | 168 | dispatch::handle_request::<req::ExecuteCommand, _>(&mut req, |params, resp| { |
165 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); | 169 | io.send(RawMsg::Response(resp.into_response(Ok(None))?)); |
166 | 170 | ||
diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index 999fdb7c2..881069b1f 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs | |||
@@ -14,6 +14,7 @@ pub use languageserver_types::{ | |||
14 | TextDocumentPositionParams, | 14 | TextDocumentPositionParams, |
15 | TextEdit, | 15 | TextEdit, |
16 | CompletionParams, CompletionResponse, | 16 | CompletionParams, CompletionResponse, |
17 | DocumentOnTypeFormattingParams, | ||
17 | }; | 18 | }; |
18 | 19 | ||
19 | 20 | ||