diff options
author | Jesse Bakker <[email protected]> | 2020-12-31 12:05:19 +0000 |
---|---|---|
committer | Jesse Bakker <[email protected]> | 2020-12-31 14:33:20 +0000 |
commit | f355a6d8319408a3cc349be399d9e69985737c42 (patch) | |
tree | e527a3297191ab0a5d9450ecfb12f71f7a51e5ee | |
parent | 9bb9fbab3ab603150990ef8f2df12bddc7104058 (diff) |
Split textDocument/formatting TextEdit with diff
-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 fbb79e01f..1213e5762 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -348,6 +348,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
348 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" | 348 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" |
349 | 349 | ||
350 | [[package]] | 350 | [[package]] |
351 | name = "dissimilar" | ||
352 | version = "1.0.2" | ||
353 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
354 | checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb" | ||
355 | |||
356 | [[package]] | ||
351 | name = "drop_bomb" | 357 | name = "drop_bomb" |
352 | version = "0.1.5" | 358 | version = "0.1.5" |
353 | source = "registry+https://github.com/rust-lang/crates.io-index" | 359 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1333,6 +1339,7 @@ dependencies = [ | |||
1333 | "anyhow", | 1339 | "anyhow", |
1334 | "cfg", | 1340 | "cfg", |
1335 | "crossbeam-channel 0.5.0", | 1341 | "crossbeam-channel 0.5.0", |
1342 | "dissimilar", | ||
1336 | "env_logger", | 1343 | "env_logger", |
1337 | "expect-test", | 1344 | "expect-test", |
1338 | "flycheck", | 1345 | "flycheck", |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 0b4d3f4eb..470c1d458 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.9.0" | 22 | itertools = "0.9.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 66f8bee99..ec37fba04 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -29,6 +29,7 @@ use serde_json::to_value; | |||
29 | use stdx::{format_to, split_once}; | 29 | use stdx::{format_to, split_once}; |
30 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; | 30 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; |
31 | 31 | ||
32 | use crate::diff::diff; | ||
32 | use crate::{ | 33 | use crate::{ |
33 | cargo_target_spec::CargoTargetSpec, | 34 | cargo_target_spec::CargoTargetSpec, |
34 | config::RustfmtConfig, | 35 | config::RustfmtConfig, |
@@ -799,7 +800,7 @@ pub(crate) fn handle_formatting( | |||
799 | let crate_ids = snap.analysis.crate_for(file_id)?; | 800 | let crate_ids = snap.analysis.crate_for(file_id)?; |
800 | 801 | ||
801 | let file_line_index = snap.analysis.file_line_index(file_id)?; | 802 | let file_line_index = snap.analysis.file_line_index(file_id)?; |
802 | let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); | 803 | let file_line_endings = snap.file_line_endings(file_id); |
803 | 804 | ||
804 | let mut rustfmt = match &snap.config.rustfmt { | 805 | let mut rustfmt = match &snap.config.rustfmt { |
805 | RustfmtConfig::Rustfmt { extra_args } => { | 806 | RustfmtConfig::Rustfmt { extra_args } => { |
@@ -858,10 +859,11 @@ pub(crate) fn handle_formatting( | |||
858 | // The document is already formatted correctly -- no edits needed. | 859 | // The document is already formatted correctly -- no edits needed. |
859 | Ok(None) | 860 | Ok(None) |
860 | } else { | 861 | } else { |
861 | Ok(Some(vec![lsp_types::TextEdit { | 862 | Ok(Some(to_proto::text_edit_vec( |
862 | range: Range::new(Position::new(0, 0), end_position), | 863 | &file_line_index, |
863 | new_text: captured_stdout, | 864 | file_line_endings, |
864 | }])) | 865 | diff(&file, &captured_stdout), |
866 | ))) | ||
865 | } | 867 | } |
866 | } | 868 | } |
867 | 869 | ||
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 79fe30e53..682fa5f7f 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 | ]), |