aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_fmt/src/lib.rs23
-rw-r--r--crates/ra_ide/src/join_lines.rs66
-rw-r--r--crates/ra_parser/src/grammar/items/use_item.rs2
-rw-r--r--crates/ra_syntax/src/ast/expr_extensions.rs5
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs1
-rw-r--r--crates/ra_syntax/src/validation.rs29
-rw-r--r--crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rast113
-rw-r--r--crates/ra_syntax/test_data/parser/err/0040_illegal_crate_kw_location.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rast49
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0002_use_tree_list.rs2
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/caps.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs130
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 {
131fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> { 131fn 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"
685fn foo() {
686 <|>if true {
687 92
688 }
689}
690 ",
691 r"
692fn foo() {
693 <|>if true { 92
694 }
695}
696 ",
697 );
698
699 check_join_lines(
700 r"
701fn foo() {
702 <|>loop {
703 92
704 }
705}
706 ",
707 r"
708fn foo() {
709 <|>loop { 92
710 }
711}
712 ",
713 );
714
715 check_join_lines(
716 r"
717fn foo() {
718 <|>unsafe {
719 92
720 }
721}
722 ",
723 r"
724fn 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 {}
554impl BlockExpr { 554impl 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 1SOURCE_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"
73error 6..11: The `crate` keyword is only allowed as the first segment of a path 88error 6..11: The `crate` keyword is only allowed as the first segment of a path
74error 31..36: The `crate` keyword is only allowed as the first segment of a path 89error 31..36: The `crate` keyword is only allowed as the first segment of a path
75error 51..56: The `crate` keyword is only allowed as the first segment of a path 90error 66..71: The `crate` keyword is only allowed as the first segment of a path
76error 69..74: The `crate` keyword is only allowed as the first segment of a path 91error 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 @@
1use ::crate; 1use ::crate;
2use {crate, foo::{crate}}; 2use {crate, foo::{crate::foo::bar::baz}};
3use hello::crate; 3use hello::crate;
4use hello::crate::there; 4use 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 @@
1use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) 1use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
2use {path::from::root}; // Rust 2015 2use {path::from::root}; // Rust 2015
3use ::{some::arbritrary::path}; // Rust 2015 3use ::{some::arbritrary::path}; // Rust 2015
4use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig 4use ::{{{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" }
39ra_project_model = { path = "../ra_project_model" } 39ra_project_model = { path = "../ra_project_model" }
40ra_syntax = { path = "../ra_syntax" } 40ra_syntax = { path = "../ra_syntax" }
41ra_text_edit = { path = "../ra_text_edit" } 41ra_text_edit = { path = "../ra_text_edit" }
42ra_vfs = "0.5.2" 42ra_vfs = "0.6.0"
43 43
44# This should only be used in CLI 44# This should only be used in CLI
45ra_db = { path = "../ra_db" } 45ra_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;
6pub(crate) mod pending_requests; 6pub(crate) mod pending_requests;
7 7
8use std::{ 8use 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};
18use itertools::Itertools; 21use itertools::Itertools;
19use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 22use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
20use lsp_types::{ 23use lsp_types::{
21 NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, 24 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress,
22 WorkDoneProgressEnd, WorkDoneProgressReport, 25 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
26 WorkDoneProgressReport,
23}; 27};
24use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask}; 28use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask};
25use ra_ide::{Canceled, FileId, LibraryData, SourceRootId}; 29use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId};
26use ra_prof::profile; 30use ra_prof::profile;
27use ra_project_model::{PackageRoot, ProjectWorkspace}; 31use ra_project_model::{PackageRoot, ProjectWorkspace};
28use ra_vfs::{VfsFile, VfsTask, Watch}; 32use ra_vfs::{VfsFile, VfsTask, Watch};
@@ -33,6 +37,7 @@ use threadpool::ThreadPool;
33 37
34use crate::{ 38use 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
665fn 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
656fn on_check_task( 707fn 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)]
1014mod 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}