aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorJesse Bakker <[email protected]>2020-12-31 12:05:19 +0000
committerJesse Bakker <[email protected]>2020-12-31 14:33:20 +0000
commitf355a6d8319408a3cc349be399d9e69985737c42 (patch)
treee527a3297191ab0a5d9450ecfb12f71f7a51e5ee /crates
parent9bb9fbab3ab603150990ef8f2df12bddc7104058 (diff)
Split textDocument/formatting TextEdit with diff
Diffstat (limited to 'crates')
-rw-r--r--crates/rust-analyzer/Cargo.toml1
-rw-r--r--crates/rust-analyzer/src/diff.rs53
-rw-r--r--crates/rust-analyzer/src/handlers.rs12
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs31
5 files changed, 75 insertions, 23 deletions
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]
18anyhow = "1.0.26" 18anyhow = "1.0.26"
19crossbeam-channel = "0.5.0" 19crossbeam-channel = "0.5.0"
20dissimilar = "1.0.2"
20env_logger = { version = "0.8.1", default-features = false } 21env_logger = { version = "0.8.1", default-features = false }
21itertools = "0.9.0" 22itertools = "0.9.0"
22jod-thread = "0.1.0" 23jod-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
2use dissimilar::Chunk;
3use ide::{TextEdit, TextRange, TextSize};
4
5pub(crate) fn diff(left: &str, right: &str) -> TextEdit {
6 let chunks = dissimilar::diff(left, right);
7 textedit_from_chunks(chunks)
8}
9
10fn 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)]
42mod 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;
29use stdx::{format_to, split_once}; 29use stdx::{format_to, split_once};
30use syntax::{algo, ast, AstNode, TextRange, TextSize}; 30use syntax::{algo, ast, AstNode, TextRange, TextSize};
31 31
32use crate::diff::diff;
32use crate::{ 33use 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;
34mod lsp_utils; 34mod lsp_utils;
35mod thread_pool; 35mod thread_pool;
36mod document; 36mod document;
37mod diff;
37pub mod lsp_ext; 38pub mod lsp_ext;
38pub mod config; 39pub 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
195fn main() {}
196
197pub 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": {
253async fn test() {} 248 "end": { "character": 0, "line": 3 },
254 249 "start": { "character": 17, "line": 2 }
255fn main() {} 250 }
256 251 },
257pub 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 ]),