diff options
-rw-r--r-- | Cargo.lock | 7 | ||||
-rw-r--r-- | crates/rust-analyzer/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diff.rs | 53 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 12 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/rust-analyzer/main.rs | 31 |
6 files changed, 82 insertions, 23 deletions
diff --git a/Cargo.lock b/Cargo.lock index 9ddbeac47..1aa0c072d 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -349,6 +349,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
349 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" | 349 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" |
350 | 350 | ||
351 | [[package]] | 351 | [[package]] |
352 | name = "dissimilar" | ||
353 | version = "1.0.2" | ||
354 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
355 | checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb" | ||
356 | |||
357 | [[package]] | ||
352 | name = "drop_bomb" | 358 | name = "drop_bomb" |
353 | version = "0.1.5" | 359 | version = "0.1.5" |
354 | source = "registry+https://github.com/rust-lang/crates.io-index" | 360 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1343,6 +1349,7 @@ dependencies = [ | |||
1343 | "anyhow", | 1349 | "anyhow", |
1344 | "cfg", | 1350 | "cfg", |
1345 | "crossbeam-channel 0.5.0", | 1351 | "crossbeam-channel 0.5.0", |
1352 | "dissimilar", | ||
1346 | "env_logger", | 1353 | "env_logger", |
1347 | "expect-test", | 1354 | "expect-test", |
1348 | "flycheck", | 1355 | "flycheck", |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 0a002337b..0a63593fb 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -17,6 +17,7 @@ path = "src/bin/main.rs" | |||
17 | [dependencies] | 17 | [dependencies] |
18 | anyhow = "1.0.26" | 18 | anyhow = "1.0.26" |
19 | crossbeam-channel = "0.5.0" | 19 | crossbeam-channel = "0.5.0" |
20 | dissimilar = "1.0.2" | ||
20 | env_logger = { version = "0.8.1", default-features = false } | 21 | env_logger = { version = "0.8.1", default-features = false } |
21 | itertools = "0.10.0" | 22 | itertools = "0.10.0" |
22 | jod-thread = "0.1.0" | 23 | jod-thread = "0.1.0" |
diff --git a/crates/rust-analyzer/src/diff.rs b/crates/rust-analyzer/src/diff.rs new file mode 100644 index 000000000..231be5807 --- /dev/null +++ b/crates/rust-analyzer/src/diff.rs | |||
@@ -0,0 +1,53 @@ | |||
1 | //! Generate minimal `TextEdit`s from different text versions | ||
2 | use dissimilar::Chunk; | ||
3 | use ide::{TextEdit, TextRange, TextSize}; | ||
4 | |||
5 | pub(crate) fn diff(left: &str, right: &str) -> TextEdit { | ||
6 | let chunks = dissimilar::diff(left, right); | ||
7 | textedit_from_chunks(chunks) | ||
8 | } | ||
9 | |||
10 | fn textedit_from_chunks(chunks: Vec<dissimilar::Chunk>) -> TextEdit { | ||
11 | let mut builder = TextEdit::builder(); | ||
12 | let mut pos = TextSize::default(); | ||
13 | |||
14 | let mut chunks = chunks.into_iter().peekable(); | ||
15 | while let Some(chunk) = chunks.next() { | ||
16 | if let (Chunk::Delete(deleted), Some(&Chunk::Insert(inserted))) = (chunk, chunks.peek()) { | ||
17 | chunks.next().unwrap(); | ||
18 | let deleted_len = TextSize::of(deleted); | ||
19 | builder.replace(TextRange::at(pos, deleted_len), inserted.into()); | ||
20 | pos += deleted_len; | ||
21 | continue; | ||
22 | } | ||
23 | |||
24 | match chunk { | ||
25 | Chunk::Equal(text) => { | ||
26 | pos += TextSize::of(text); | ||
27 | } | ||
28 | Chunk::Delete(deleted) => { | ||
29 | let deleted_len = TextSize::of(deleted); | ||
30 | builder.delete(TextRange::at(pos, deleted_len)); | ||
31 | pos += deleted_len; | ||
32 | } | ||
33 | Chunk::Insert(inserted) => { | ||
34 | builder.insert(pos, inserted.into()); | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | builder.finish() | ||
39 | } | ||
40 | |||
41 | #[cfg(test)] | ||
42 | mod tests { | ||
43 | use super::*; | ||
44 | |||
45 | #[test] | ||
46 | fn diff_applies() { | ||
47 | let mut original = String::from("fn foo(a:u32){\n}"); | ||
48 | let result = "fn foo(a: u32) {}"; | ||
49 | let edit = diff(&original, result); | ||
50 | edit.apply(&mut original); | ||
51 | assert_eq!(original, result); | ||
52 | } | ||
53 | } | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 78411f6c0..948cfc17c 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -31,6 +31,7 @@ use serde_json::to_value; | |||
31 | use stdx::{format_to, split_once}; | 31 | use stdx::{format_to, split_once}; |
32 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; | 32 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; |
33 | 33 | ||
34 | use crate::diff::diff; | ||
34 | use crate::{ | 35 | use crate::{ |
35 | cargo_target_spec::CargoTargetSpec, | 36 | cargo_target_spec::CargoTargetSpec, |
36 | config::RustfmtConfig, | 37 | config::RustfmtConfig, |
@@ -840,7 +841,7 @@ pub(crate) fn handle_formatting( | |||
840 | let crate_ids = snap.analysis.crate_for(file_id)?; | 841 | let crate_ids = snap.analysis.crate_for(file_id)?; |
841 | 842 | ||
842 | let file_line_index = snap.analysis.file_line_index(file_id)?; | 843 | let file_line_index = snap.analysis.file_line_index(file_id)?; |
843 | let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); | 844 | let file_line_endings = snap.file_line_endings(file_id); |
844 | 845 | ||
845 | let mut rustfmt = match &snap.config.rustfmt { | 846 | let mut rustfmt = match &snap.config.rustfmt { |
846 | RustfmtConfig::Rustfmt { extra_args } => { | 847 | RustfmtConfig::Rustfmt { extra_args } => { |
@@ -902,10 +903,11 @@ pub(crate) fn handle_formatting( | |||
902 | // The document is already formatted correctly -- no edits needed. | 903 | // The document is already formatted correctly -- no edits needed. |
903 | Ok(None) | 904 | Ok(None) |
904 | } else { | 905 | } else { |
905 | Ok(Some(vec![lsp_types::TextEdit { | 906 | Ok(Some(to_proto::text_edit_vec( |
906 | range: Range::new(Position::new(0, 0), end_position), | 907 | &file_line_index, |
907 | new_text: captured_stdout, | 908 | file_line_endings, |
908 | }])) | 909 | diff(&file, &captured_stdout), |
910 | ))) | ||
909 | } | 911 | } |
910 | } | 912 | } |
911 | 913 | ||
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index d538ad69a..c9494e300 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -34,6 +34,7 @@ mod request_metrics; | |||
34 | mod lsp_utils; | 34 | mod lsp_utils; |
35 | mod thread_pool; | 35 | mod thread_pool; |
36 | mod document; | 36 | mod document; |
37 | mod diff; | ||
37 | pub mod lsp_ext; | 38 | pub mod lsp_ext; |
38 | pub mod config; | 39 | pub mod config; |
39 | 40 | ||
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs index e51eb2626..84db0856d 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/main.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs | |||
@@ -190,15 +190,10 @@ pub use std::collections::HashMap; | |||
190 | }, | 190 | }, |
191 | json!([ | 191 | json!([ |
192 | { | 192 | { |
193 | "newText": r#"mod bar; | 193 | "newText": "", |
194 | |||
195 | fn main() {} | ||
196 | |||
197 | pub use std::collections::HashMap; | ||
198 | "#, | ||
199 | "range": { | 194 | "range": { |
200 | "end": { "character": 0, "line": 6 }, | 195 | "end": { "character": 0, "line": 3 }, |
201 | "start": { "character": 0, "line": 0 } | 196 | "start": { "character": 11, "line": 2 } |
202 | } | 197 | } |
203 | } | 198 | } |
204 | ]), | 199 | ]), |
@@ -248,17 +243,17 @@ pub use std::collections::HashMap; | |||
248 | }, | 243 | }, |
249 | json!([ | 244 | json!([ |
250 | { | 245 | { |
251 | "newText": r#"mod bar; | 246 | "newText": "", |
252 | 247 | "range": { | |
253 | async fn test() {} | 248 | "end": { "character": 0, "line": 3 }, |
254 | 249 | "start": { "character": 17, "line": 2 } | |
255 | fn main() {} | 250 | } |
256 | 251 | }, | |
257 | pub use std::collections::HashMap; | 252 | { |
258 | "#, | 253 | "newText": "", |
259 | "range": { | 254 | "range": { |
260 | "end": { "character": 0, "line": 9 }, | 255 | "end": { "character": 0, "line": 6 }, |
261 | "start": { "character": 0, "line": 0 } | 256 | "start": { "character": 11, "line": 5 } |
262 | } | 257 | } |
263 | } | 258 | } |
264 | ]), | 259 | ]), |