diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_fmt/src/lib.rs | 23 | ||||
-rw-r--r-- | crates/ra_ide/src/join_lines.rs | 66 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/items/use_item.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/expr_extensions.rs | 5 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated/nodes.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 29 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast | 113 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast | 49 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/caps.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 130 |
13 files changed, 321 insertions, 105 deletions
diff --git a/crates/ra_fmt/src/lib.rs b/crates/ra_fmt/src/lib.rs index 0b4ba1bbe..1a30b2b3a 100644 --- a/crates/ra_fmt/src/lib.rs +++ b/crates/ra_fmt/src/lib.rs | |||
@@ -57,18 +57,17 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { | |||
57 | return None; | 57 | return None; |
58 | } | 58 | } |
59 | return Some(expr); | 59 | return Some(expr); |
60 | } else { | 60 | } |
61 | // Unwrap `{ continue; }` | 61 | // Unwrap `{ continue; }` |
62 | let (stmt,) = block.statements().next_tuple()?; | 62 | let (stmt,) = block.statements().next_tuple()?; |
63 | if let ast::Stmt::ExprStmt(expr_stmt) = stmt { | 63 | if let ast::Stmt::ExprStmt(expr_stmt) = stmt { |
64 | if has_anything_else(expr_stmt.syntax()) { | 64 | if has_anything_else(expr_stmt.syntax()) { |
65 | return None; | 65 | return None; |
66 | } | 66 | } |
67 | let expr = expr_stmt.expr()?; | 67 | let expr = expr_stmt.expr()?; |
68 | match expr.syntax().kind() { | 68 | match expr.syntax().kind() { |
69 | CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR => return Some(expr), | 69 | CONTINUE_EXPR | BREAK_EXPR | RETURN_EXPR => return Some(expr), |
70 | _ => (), | 70 | _ => (), |
71 | } | ||
72 | } | 71 | } |
73 | } | 72 | } |
74 | None | 73 | None |
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index fde0bfa98..d0def7eaa 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs | |||
@@ -131,6 +131,9 @@ fn has_comma_after(node: &SyntaxNode) -> bool { | |||
131 | fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> { | 131 | fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> { |
132 | let block = ast::Block::cast(token.parent())?; | 132 | let block = ast::Block::cast(token.parent())?; |
133 | let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?; | 133 | let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?; |
134 | if !block_expr.is_standalone() { | ||
135 | return None; | ||
136 | } | ||
134 | let expr = extract_trivial_expression(&block_expr)?; | 137 | let expr = extract_trivial_expression(&block_expr)?; |
135 | 138 | ||
136 | let block_range = block_expr.syntax().text_range(); | 139 | let block_range = block_expr.syntax().text_range(); |
@@ -662,4 +665,67 @@ fn main() { | |||
662 | ", | 665 | ", |
663 | ) | 666 | ) |
664 | } | 667 | } |
668 | |||
669 | #[test] | ||
670 | fn join_lines_mandatory_blocks_block() { | ||
671 | check_join_lines( | ||
672 | r" | ||
673 | <|>fn foo() { | ||
674 | 92 | ||
675 | } | ||
676 | ", | ||
677 | r" | ||
678 | <|>fn foo() { 92 | ||
679 | } | ||
680 | ", | ||
681 | ); | ||
682 | |||
683 | check_join_lines( | ||
684 | r" | ||
685 | fn foo() { | ||
686 | <|>if true { | ||
687 | 92 | ||
688 | } | ||
689 | } | ||
690 | ", | ||
691 | r" | ||
692 | fn foo() { | ||
693 | <|>if true { 92 | ||
694 | } | ||
695 | } | ||
696 | ", | ||
697 | ); | ||
698 | |||
699 | check_join_lines( | ||
700 | r" | ||
701 | fn foo() { | ||
702 | <|>loop { | ||
703 | 92 | ||
704 | } | ||
705 | } | ||
706 | ", | ||
707 | r" | ||
708 | fn foo() { | ||
709 | <|>loop { 92 | ||
710 | } | ||
711 | } | ||
712 | ", | ||
713 | ); | ||
714 | |||
715 | check_join_lines( | ||
716 | r" | ||
717 | fn foo() { | ||
718 | <|>unsafe { | ||
719 | 92 | ||
720 | } | ||
721 | } | ||
722 | ", | ||
723 | r" | ||
724 | fn foo() { | ||
725 | <|>unsafe { 92 | ||
726 | } | ||
727 | } | ||
728 | ", | ||
729 | ); | ||
730 | } | ||
665 | } | 731 | } |
diff --git a/crates/ra_parser/src/grammar/items/use_item.rs b/crates/ra_parser/src/grammar/items/use_item.rs index e3b991c8c..3a0c7a31a 100644 --- a/crates/ra_parser/src/grammar/items/use_item.rs +++ b/crates/ra_parser/src/grammar/items/use_item.rs | |||
@@ -47,7 +47,7 @@ fn use_tree(p: &mut Parser, top_level: bool) { | |||
47 | // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) | 47 | // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) |
48 | // use {path::from::root}; // Rust 2015 | 48 | // use {path::from::root}; // Rust 2015 |
49 | // use ::{some::arbritrary::path}; // Rust 2015 | 49 | // use ::{some::arbritrary::path}; // Rust 2015 |
50 | // use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig | 50 | // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting |
51 | T!['{'] => { | 51 | T!['{'] => { |
52 | use_tree_list(p); | 52 | use_tree_list(p); |
53 | } | 53 | } |
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 93aa3d45f..ecf74fd36 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs | |||
@@ -368,12 +368,15 @@ impl ast::BlockExpr { | |||
368 | /// const FOO: () = { stand_alone }; | 368 | /// const FOO: () = { stand_alone }; |
369 | /// ``` | 369 | /// ``` |
370 | pub fn is_standalone(&self) -> bool { | 370 | pub fn is_standalone(&self) -> bool { |
371 | if self.unsafe_token().is_some() || self.async_token().is_some() { | ||
372 | return false; | ||
373 | } | ||
371 | let kind = match self.syntax().parent() { | 374 | let kind = match self.syntax().parent() { |
372 | None => return true, | 375 | None => return true, |
373 | Some(it) => it.kind(), | 376 | Some(it) => it.kind(), |
374 | }; | 377 | }; |
375 | match kind { | 378 | match kind { |
376 | FN_DEF | MATCH_ARM | IF_EXPR | WHILE_EXPR | LOOP_EXPR | TRY_BLOCK_EXPR => false, | 379 | FN_DEF | IF_EXPR | WHILE_EXPR | LOOP_EXPR | TRY_BLOCK_EXPR => false, |
377 | _ => true, | 380 | _ => true, |
378 | } | 381 | } |
379 | } | 382 | } |
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index 3b5e05af9..d2253d4af 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs | |||
@@ -554,6 +554,7 @@ impl ast::AttrsOwner for BlockExpr {} | |||
554 | impl BlockExpr { | 554 | impl BlockExpr { |
555 | pub fn label(&self) -> Option<Label> { support::child(&self.syntax) } | 555 | pub fn label(&self) -> Option<Label> { support::child(&self.syntax) } |
556 | pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } | 556 | pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } |
557 | pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) } | ||
557 | pub fn block(&self) -> Option<Block> { support::child(&self.syntax) } | 558 | pub fn block(&self) -> Option<Block> { support::child(&self.syntax) } |
558 | } | 559 | } |
559 | 560 | ||
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index a30bc97bb..f0b3dec63 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -236,21 +236,40 @@ fn validate_crate_keyword_in_path_segment( | |||
236 | }; | 236 | }; |
237 | 237 | ||
238 | // Disallow both ::crate and foo::crate | 238 | // Disallow both ::crate and foo::crate |
239 | let path = segment.parent_path(); | 239 | let mut path = segment.parent_path(); |
240 | if segment.coloncolon_token().is_some() || path.qualifier().is_some() { | 240 | if segment.coloncolon_token().is_some() || path.qualifier().is_some() { |
241 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | 241 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); |
242 | return; | 242 | return; |
243 | } | 243 | } |
244 | 244 | ||
245 | // We now know that the path variable describes a complete path. | ||
246 | // For expressions and types, validation is complete, but we still have | 245 | // For expressions and types, validation is complete, but we still have |
247 | // to handle UseItems like this: | 246 | // to handle invalid UseItems like this: |
248 | // use foo:{crate}; | 247 | // |
249 | // so we crawl upwards looking for any preceding paths on `UseTree`s | 248 | // use foo:{crate::bar::baz}; |
249 | // | ||
250 | // To handle this we must inspect the parent `UseItem`s and `UseTree`s | ||
251 | // but right now we're looking deep inside the nested `Path` nodes because | ||
252 | // `Path`s are left-associative: | ||
253 | // | ||
254 | // ((crate)::bar)::baz) | ||
255 | // ^ current value of path | ||
256 | // | ||
257 | // So we need to climb to the top | ||
258 | while let Some(parent) = path.parent_path() { | ||
259 | path = parent; | ||
260 | } | ||
261 | |||
262 | // Now that we've found the whole path we need to see if there's a prefix | ||
263 | // somewhere in the UseTree hierarchy. This check is arbitrarily deep | ||
264 | // because rust allows arbitrary nesting like so: | ||
265 | // | ||
266 | // use {foo::{{{{crate::bar::baz}}}}}; | ||
250 | for node in path.syntax().ancestors().skip(1) { | 267 | for node in path.syntax().ancestors().skip(1) { |
251 | match_ast! { | 268 | match_ast! { |
252 | match node { | 269 | match node { |
253 | ast::UseTree(it) => if let Some(tree_path) = it.path() { | 270 | ast::UseTree(it) => if let Some(tree_path) = it.path() { |
271 | // Even a top-level path exists within a `UseTree` so we must explicitly | ||
272 | // allow our path but disallow anything else | ||
254 | if tree_path != path { | 273 | if tree_path != path { |
255 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); | 274 | errors.push(SyntaxError::new(ERR_MSG, crate_token.text_range())); |
256 | } | 275 | } |
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast index 8306f7361..d2a549273 100644 --- a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast | |||
@@ -1,4 +1,4 @@ | |||
1 | [email protected]3 | 1 | SOURCE_FILE@0..98 |
2 | [email protected] | 2 | [email protected] |
3 | [email protected] "use" | 3 | [email protected] "use" |
4 | [email protected] " " | 4 | [email protected] " " |
@@ -9,11 +9,11 @@ [email protected] | |||
9 | [email protected] "crate" | 9 | [email protected] "crate" |
10 | [email protected] ";" | 10 | [email protected] ";" |
11 | [email protected] "\n" | 11 | [email protected] "\n" |
12 | USE_ITEM@13..39 | 12 | USE_ITEM@13..54 |
13 | [email protected] "use" | 13 | [email protected] "use" |
14 | [email protected] " " | 14 | [email protected] " " |
15 | [email protected]8 | 15 | USE_TREE@17..53 |
16 | [email protected]8 | 16 | USE_TREE_LIST@17..53 |
17 | [email protected] "{" | 17 | [email protected] "{" |
18 | [email protected] | 18 | [email protected] |
19 | [email protected] | 19 | [email protected] |
@@ -21,56 +21,71 @@ [email protected] | |||
21 | [email protected] "crate" | 21 | [email protected] "crate" |
22 | [email protected] "," | 22 | [email protected] "," |
23 | [email protected] " " | 23 | [email protected] " " |
24 | USE_TREE@25..37 | 24 | USE_TREE@25..52 |
25 | [email protected] | 25 | [email protected] |
26 | [email protected] | 26 | [email protected] |
27 | [email protected] | 27 | [email protected] |
28 | [email protected] "foo" | 28 | [email protected] "foo" |
29 | [email protected] "::" | 29 | [email protected] "::" |
30 | USE_TREE_LIST@30..37 | 30 | USE_TREE_LIST@30..52 |
31 | [email protected] "{" | 31 | [email protected] "{" |
32 | [email protected] | 32 | [email protected] |
33 | [email protected] | 33 | [email protected] |
34 | [email protected] | 34 | [email protected] |
35 | [email protected] "crate" | 35 | [email protected] |
36 | [email protected] "}" | 36 | [email protected] |
37 | [email protected] "}" | 37 | [email protected] |
38 | [email protected] ";" | 38 | [email protected] "crate" |
39 | [email protected] "\n" | 39 | [email protected] "::" |
40 | [email protected] | 40 | [email protected] |
41 | [email protected] "use" | 41 | [email protected] |
42 | [email protected] " " | 42 | [email protected] "foo" |
43 | [email protected] | 43 | [email protected] "::" |
44 | [email protected] | 44 | [email protected] |
45 | [email protected] | 45 | [email protected] |
46 | [email protected] | 46 | [email protected] "bar" |
47 | [email protected] | 47 | [email protected] "::" |
48 | [email protected] "hello" | 48 | [email protected] |
49 | [email protected] "::" | 49 | [email protected] |
50 | [email protected] | 50 | [email protected] "baz" |
51 | [email protected] "crate" | 51 | [email protected] "}" |
52 | [email protected] ";" | 52 | [email protected] "}" |
53 | [email protected] "\n" | 53 | [email protected] ";" |
54 | [email protected] | 54 | [email protected] "\n" |
55 | [email protected] "use" | 55 | [email protected] |
56 | [email protected] " " | 56 | [email protected] "use" |
57 | [email protected] | 57 | [email protected] " " |
58 | [email protected] | 58 | [email protected] |
59 | [email protected] | 59 | [email protected] |
60 | [email protected] | 60 | [email protected] |
61 | [email protected] | 61 | [email protected] |
62 | [email protected] | 62 | [email protected] |
63 | [email protected] "hello" | 63 | [email protected] "hello" |
64 | [email protected] "::" | 64 | [email protected] "::" |
65 | [email protected] | 65 | [email protected] |
66 | [email protected] "crate" | 66 | [email protected] "crate" |
67 | [email protected] "::" | 67 | [email protected] ";" |
68 | [email protected] | 68 | [email protected] "\n" |
69 | [email protected] | 69 | [email protected] |
70 | [email protected] "there" | 70 | [email protected] "use" |
71 | [email protected] ";" | 71 | [email protected] " " |
72 | [email protected] "\n" | 72 | [email protected] |
73 | [email protected] | ||
74 | [email protected] | ||
75 | [email protected] | ||
76 | [email protected] | ||
77 | [email protected] | ||
78 | [email protected] "hello" | ||
79 | [email protected] "::" | ||
80 | [email protected] | ||
81 | [email protected] "crate" | ||
82 | [email protected] "::" | ||
83 | [email protected] | ||
84 | [email protected] | ||
85 | [email protected] "there" | ||
86 | [email protected] ";" | ||
87 | [email protected] "\n" | ||
73 | error 6..11: The `crate` keyword is only allowed as the first segment of a path | 88 | error 6..11: The `crate` keyword is only allowed as the first segment of a path |
74 | error 31..36: The `crate` keyword is only allowed as the first segment of a path | 89 | error 31..36: The `crate` keyword is only allowed as the first segment of a path |
75 | error 51..56: The `crate` keyword is only allowed as the first segment of a path | 90 | error 66..71: The `crate` keyword is only allowed as the first segment of a path |
76 | error 69..74: The `crate` keyword is only allowed as the first segment of a path | 91 | error 84..89: The `crate` keyword is only allowed as the first segment of a path |
diff --git a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs index bead4c0b6..508def2c7 100644 --- a/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs +++ b/crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ::crate; | 1 | use ::crate; |
2 | use {crate, foo::{crate}}; | 2 | use {crate, foo::{crate::foo::bar::baz}}; |
3 | use hello::crate; | 3 | use hello::crate; |
4 | use hello::crate::there; | 4 | use hello::crate::there; |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast b/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast index bd74b44a6..cf3a90400 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast +++ b/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast | |||
@@ -1,4 +1,4 @@ | |||
1 | [email protected]50 | 1 | [email protected]49 |
2 | [email protected] | 2 | [email protected] |
3 | [email protected] "use" | 3 | [email protected] "use" |
4 | [email protected] " " | 4 | [email protected] " " |
@@ -104,32 +104,33 @@ [email protected] | |||
104 | [email protected] " " | 104 | [email protected] " " |
105 | [email protected] "// Rust 2015" | 105 | [email protected] "// Rust 2015" |
106 | [email protected] "\n" | 106 | [email protected] "\n" |
107 | [email protected]6 | 107 | [email protected]5 |
108 | [email protected] "use" | 108 | [email protected] "use" |
109 | [email protected] " " | 109 | [email protected] " " |
110 | [email protected]5 | 110 | [email protected]4 |
111 | [email protected] "::" | 111 | [email protected] "::" |
112 | [email protected]5 | 112 | [email protected]4 |
113 | [email protected] "{" | 113 | [email protected] "{" |
114 | [email protected]4 | 114 | [email protected]3 |
115 | [email protected]4 | 115 | [email protected]3 |
116 | [email protected] "{" | 116 | [email protected] "{" |
117 | [email protected]3 | 117 | [email protected]2 |
118 | [email protected]3 | 118 | [email protected]2 |
119 | [email protected] "{" | 119 | [email protected] "{" |
120 | [email protected] | 120 | [email protected] |
121 | [email protected] | 121 | [email protected] |
122 | [email protected] | 122 | [email protected] |
123 | [email protected] | 123 | [email protected] |
124 | [email protected] "crate" | 124 | [email protected] |
125 | [email protected] "::" | 125 | [email protected] "root" |
126 | [email protected] | 126 | [email protected] "::" |
127 | [email protected] | 127 | [email protected] |
128 | [email protected] "export" | 128 | [email protected] |
129 | [email protected] "}" | 129 | [email protected] "export" |
130 | [email protected] "}" | 130 | [email protected] "}" |
131 | [email protected] "}" | 131 | [email protected] "}" |
132 | [email protected] ";" | 132 | [email protected] "}" |
133 | [email protected] " " | 133 | [email protected] ";" |
134 | [email protected] "// Nonsensical but pe ..." | 134 | [email protected] " " |
135 | [email protected] "\n" | 135 | [email protected] "// Nonsensical but pe ..." |
136 | [email protected] "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs b/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs index 06c387cee..381cba1e2 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) | 1 | use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) |
2 | use {path::from::root}; // Rust 2015 | 2 | use {path::from::root}; // Rust 2015 |
3 | use ::{some::arbritrary::path}; // Rust 2015 | 3 | use ::{some::arbritrary::path}; // Rust 2015 |
4 | use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig | 4 | use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 514d6d1a9..0459807fc 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -39,7 +39,7 @@ ra_prof = { path = "../ra_prof" } | |||
39 | ra_project_model = { path = "../ra_project_model" } | 39 | ra_project_model = { path = "../ra_project_model" } |
40 | ra_syntax = { path = "../ra_syntax" } | 40 | ra_syntax = { path = "../ra_syntax" } |
41 | ra_text_edit = { path = "../ra_text_edit" } | 41 | ra_text_edit = { path = "../ra_text_edit" } |
42 | ra_vfs = "0.5.2" | 42 | ra_vfs = "0.6.0" |
43 | 43 | ||
44 | # This should only be used in CLI | 44 | # This should only be used in CLI |
45 | ra_db = { path = "../ra_db" } | 45 | ra_db = { path = "../ra_db" } |
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 45b60768a..e22ab8402 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs | |||
@@ -16,7 +16,7 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
16 | ServerCapabilities { | 16 | ServerCapabilities { |
17 | text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { | 17 | text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { |
18 | open_close: Some(true), | 18 | open_close: Some(true), |
19 | change: Some(TextDocumentSyncKind::Full), | 19 | change: Some(TextDocumentSyncKind::Incremental), |
20 | will_save: None, | 20 | will_save: None, |
21 | will_save_wait_until: None, | 21 | will_save_wait_until: None, |
22 | save: Some(SaveOptions::default()), | 22 | save: Some(SaveOptions::default()), |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f3aef3f0f..0a0e616c9 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -6,9 +6,12 @@ mod subscriptions; | |||
6 | pub(crate) mod pending_requests; | 6 | pub(crate) mod pending_requests; |
7 | 7 | ||
8 | use std::{ | 8 | use std::{ |
9 | borrow::Cow, | ||
9 | env, | 10 | env, |
10 | error::Error, | 11 | error::Error, |
11 | fmt, panic, | 12 | fmt, |
13 | ops::Range, | ||
14 | panic, | ||
12 | path::PathBuf, | 15 | path::PathBuf, |
13 | sync::Arc, | 16 | sync::Arc, |
14 | time::{Duration, Instant}, | 17 | time::{Duration, Instant}, |
@@ -18,11 +21,12 @@ use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; | |||
18 | use itertools::Itertools; | 21 | use itertools::Itertools; |
19 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 22 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
20 | use lsp_types::{ | 23 | use lsp_types::{ |
21 | NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, | 24 | DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, |
22 | WorkDoneProgressEnd, WorkDoneProgressReport, | 25 | WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, |
26 | WorkDoneProgressReport, | ||
23 | }; | 27 | }; |
24 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask}; | 28 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask}; |
25 | use ra_ide::{Canceled, FileId, LibraryData, SourceRootId}; | 29 | use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; |
26 | use ra_prof::profile; | 30 | use ra_prof::profile; |
27 | use ra_project_model::{PackageRoot, ProjectWorkspace}; | 31 | use ra_project_model::{PackageRoot, ProjectWorkspace}; |
28 | use ra_vfs::{VfsFile, VfsTask, Watch}; | 32 | use ra_vfs::{VfsFile, VfsTask, Watch}; |
@@ -33,6 +37,7 @@ use threadpool::ThreadPool; | |||
33 | 37 | ||
34 | use crate::{ | 38 | use crate::{ |
35 | config::{Config, FilesWatcher}, | 39 | config::{Config, FilesWatcher}, |
40 | conv::{ConvWith, TryConvWith}, | ||
36 | diagnostics::DiagnosticTask, | 41 | diagnostics::DiagnosticTask, |
37 | main_loop::{ | 42 | main_loop::{ |
38 | pending_requests::{PendingRequest, PendingRequests}, | 43 | pending_requests::{PendingRequest, PendingRequests}, |
@@ -579,12 +584,16 @@ fn on_notification( | |||
579 | Err(not) => not, | 584 | Err(not) => not, |
580 | }; | 585 | }; |
581 | let not = match notification_cast::<req::DidChangeTextDocument>(not) { | 586 | let not = match notification_cast::<req::DidChangeTextDocument>(not) { |
582 | Ok(mut params) => { | 587 | Ok(params) => { |
583 | let uri = params.text_document.uri; | 588 | let DidChangeTextDocumentParams { text_document, content_changes } = params; |
589 | let world = state.snapshot(); | ||
590 | let file_id = text_document.try_conv_with(&world)?; | ||
591 | let line_index = world.analysis().file_line_index(file_id)?; | ||
592 | let uri = text_document.uri; | ||
584 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | 593 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; |
585 | let text = | 594 | state.vfs.write().change_file_overlay(&path, |old_text| { |
586 | params.content_changes.pop().ok_or_else(|| "empty changes".to_string())?.text; | 595 | apply_document_changes(old_text, Cow::Borrowed(&line_index), content_changes); |
587 | state.vfs.write().change_file_overlay(path.as_path(), text); | 596 | }); |
588 | return Ok(()); | 597 | return Ok(()); |
589 | } | 598 | } |
590 | Err(not) => not, | 599 | Err(not) => not, |
@@ -653,6 +662,48 @@ fn on_notification( | |||
653 | Ok(()) | 662 | Ok(()) |
654 | } | 663 | } |
655 | 664 | ||
665 | fn apply_document_changes( | ||
666 | old_text: &mut String, | ||
667 | mut line_index: Cow<'_, LineIndex>, | ||
668 | content_changes: Vec<TextDocumentContentChangeEvent>, | ||
669 | ) { | ||
670 | // The changes we got must be applied sequentially, but can cross lines so we | ||
671 | // have to keep our line index updated. | ||
672 | // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we | ||
673 | // remember the last valid line in the index and only rebuild it if needed. | ||
674 | enum IndexValid { | ||
675 | All, | ||
676 | UpToLine(u64), | ||
677 | } | ||
678 | |||
679 | impl IndexValid { | ||
680 | fn covers(&self, line: u64) -> bool { | ||
681 | match *self { | ||
682 | IndexValid::UpToLine(to) => to >= line, | ||
683 | _ => true, | ||
684 | } | ||
685 | } | ||
686 | } | ||
687 | |||
688 | let mut index_valid = IndexValid::All; | ||
689 | for change in content_changes { | ||
690 | match change.range { | ||
691 | Some(range) => { | ||
692 | if !index_valid.covers(range.start.line) { | ||
693 | line_index = Cow::Owned(LineIndex::new(&old_text)); | ||
694 | } | ||
695 | index_valid = IndexValid::UpToLine(range.start.line); | ||
696 | let range = range.conv_with(&line_index); | ||
697 | old_text.replace_range(Range::<usize>::from(range), &change.text); | ||
698 | } | ||
699 | None => { | ||
700 | *old_text = change.text; | ||
701 | index_valid = IndexValid::UpToLine(0); | ||
702 | } | ||
703 | } | ||
704 | } | ||
705 | } | ||
706 | |||
656 | fn on_check_task( | 707 | fn on_check_task( |
657 | task: CheckTask, | 708 | task: CheckTask, |
658 | world_state: &mut WorldState, | 709 | world_state: &mut WorldState, |
@@ -958,3 +1009,64 @@ where | |||
958 | { | 1009 | { |
959 | Request::new(id, R::METHOD.to_string(), params) | 1010 | Request::new(id, R::METHOD.to_string(), params) |
960 | } | 1011 | } |
1012 | |||
1013 | #[cfg(test)] | ||
1014 | mod tests { | ||
1015 | use std::borrow::Cow; | ||
1016 | |||
1017 | use lsp_types::{Position, Range, TextDocumentContentChangeEvent}; | ||
1018 | use ra_ide::LineIndex; | ||
1019 | |||
1020 | #[test] | ||
1021 | fn apply_document_changes() { | ||
1022 | fn run(text: &mut String, changes: Vec<TextDocumentContentChangeEvent>) { | ||
1023 | let line_index = Cow::Owned(LineIndex::new(&text)); | ||
1024 | super::apply_document_changes(text, line_index, changes); | ||
1025 | } | ||
1026 | |||
1027 | macro_rules! c { | ||
1028 | [$($sl:expr, $sc:expr; $el:expr, $ec:expr => $text:expr),+] => { | ||
1029 | vec![$(TextDocumentContentChangeEvent { | ||
1030 | range: Some(Range { | ||
1031 | start: Position { line: $sl, character: $sc }, | ||
1032 | end: Position { line: $el, character: $ec }, | ||
1033 | }), | ||
1034 | range_length: None, | ||
1035 | text: String::from($text), | ||
1036 | }),+] | ||
1037 | }; | ||
1038 | } | ||
1039 | |||
1040 | let mut text = String::new(); | ||
1041 | run(&mut text, vec![]); | ||
1042 | assert_eq!(text, ""); | ||
1043 | run( | ||
1044 | &mut text, | ||
1045 | vec![TextDocumentContentChangeEvent { | ||
1046 | range: None, | ||
1047 | range_length: None, | ||
1048 | text: String::from("the"), | ||
1049 | }], | ||
1050 | ); | ||
1051 | assert_eq!(text, "the"); | ||
1052 | run(&mut text, c![0, 3; 0, 3 => " quick"]); | ||
1053 | assert_eq!(text, "the quick"); | ||
1054 | run(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); | ||
1055 | assert_eq!(text, "quick foxes"); | ||
1056 | run(&mut text, c![0, 11; 0, 11 => "\ndream"]); | ||
1057 | assert_eq!(text, "quick foxes\ndream"); | ||
1058 | run(&mut text, c![1, 0; 1, 0 => "have "]); | ||
1059 | assert_eq!(text, "quick foxes\nhave dream"); | ||
1060 | run(&mut text, c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"]); | ||
1061 | assert_eq!(text, "the quick foxes\nhave quiet dreams\n"); | ||
1062 | run(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); | ||
1063 | assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n"); | ||
1064 | run( | ||
1065 | &mut text, | ||
1066 | c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"], | ||
1067 | ); | ||
1068 | assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); | ||
1069 | run(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); | ||
1070 | assert_eq!(text, "the quick \nthey have quiet dreams\n"); | ||
1071 | } | ||
1072 | } | ||