]> git.lizzy.rs Git - rust.git/commitdiff
Split textDocument/formatting TextEdit with diff
authorJesse Bakker <github@jessebakker.com>
Thu, 31 Dec 2020 12:05:19 +0000 (13:05 +0100)
committerJesse Bakker <github@jessebakker.com>
Thu, 31 Dec 2020 14:33:20 +0000 (15:33 +0100)
Cargo.lock
crates/rust-analyzer/Cargo.toml
crates/rust-analyzer/src/diff.rs [new file with mode: 0644]
crates/rust-analyzer/src/handlers.rs
crates/rust-analyzer/src/lib.rs
crates/rust-analyzer/tests/rust-analyzer/main.rs

index fbb79e01fc2f7d032d867736f290dc4e4e5e12ae..1213e5762246f02767a7607c88cd1f6f8199e255 100644 (file)
@@ -347,6 +347,12 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
 
+[[package]]
+name = "dissimilar"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb"
+
 [[package]]
 name = "drop_bomb"
 version = "0.1.5"
@@ -1333,6 +1339,7 @@ dependencies = [
  "anyhow",
  "cfg",
  "crossbeam-channel 0.5.0",
+ "dissimilar",
  "env_logger",
  "expect-test",
  "flycheck",
index 0b4d3f4ebbe70f60834d1f8d8fb55f698beb82ab..470c1d45898b1cdc99ecca0a528c2de51ccddbdf 100644 (file)
@@ -17,6 +17,7 @@ path = "src/bin/main.rs"
 [dependencies]
 anyhow = "1.0.26"
 crossbeam-channel = "0.5.0"
+dissimilar = "1.0.2"
 env_logger = { version = "0.8.1", default-features = false }
 itertools = "0.9.0"
 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 (file)
index 0000000..231be58
--- /dev/null
@@ -0,0 +1,53 @@
+//! Generate minimal `TextEdit`s from different text versions
+use dissimilar::Chunk;
+use ide::{TextEdit, TextRange, TextSize};
+
+pub(crate) fn diff(left: &str, right: &str) -> TextEdit {
+    let chunks = dissimilar::diff(left, right);
+    textedit_from_chunks(chunks)
+}
+
+fn textedit_from_chunks(chunks: Vec<dissimilar::Chunk>) -> TextEdit {
+    let mut builder = TextEdit::builder();
+    let mut pos = TextSize::default();
+
+    let mut chunks = chunks.into_iter().peekable();
+    while let Some(chunk) = chunks.next() {
+        if let (Chunk::Delete(deleted), Some(&Chunk::Insert(inserted))) = (chunk, chunks.peek()) {
+            chunks.next().unwrap();
+            let deleted_len = TextSize::of(deleted);
+            builder.replace(TextRange::at(pos, deleted_len), inserted.into());
+            pos += deleted_len;
+            continue;
+        }
+
+        match chunk {
+            Chunk::Equal(text) => {
+                pos += TextSize::of(text);
+            }
+            Chunk::Delete(deleted) => {
+                let deleted_len = TextSize::of(deleted);
+                builder.delete(TextRange::at(pos, deleted_len));
+                pos += deleted_len;
+            }
+            Chunk::Insert(inserted) => {
+                builder.insert(pos, inserted.into());
+            }
+        }
+    }
+    builder.finish()
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn diff_applies() {
+        let mut original = String::from("fn foo(a:u32){\n}");
+        let result = "fn foo(a: u32) {}";
+        let edit = diff(&original, result);
+        edit.apply(&mut original);
+        assert_eq!(original, result);
+    }
+}
index 66f8bee991579feea8218dca29cc620d8a67e44b..ec37fba04fe1f365dca5d43c49220ebf5acf9a4d 100644 (file)
@@ -29,6 +29,7 @@
 use stdx::{format_to, split_once};
 use syntax::{algo, ast, AstNode, TextRange, TextSize};
 
+use crate::diff::diff;
 use crate::{
     cargo_target_spec::CargoTargetSpec,
     config::RustfmtConfig,
@@ -799,7 +800,7 @@ pub(crate) fn handle_formatting(
     let crate_ids = snap.analysis.crate_for(file_id)?;
 
     let file_line_index = snap.analysis.file_line_index(file_id)?;
-    let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str()));
+    let file_line_endings = snap.file_line_endings(file_id);
 
     let mut rustfmt = match &snap.config.rustfmt {
         RustfmtConfig::Rustfmt { extra_args } => {
@@ -858,10 +859,11 @@ pub(crate) fn handle_formatting(
         // The document is already formatted correctly -- no edits needed.
         Ok(None)
     } else {
-        Ok(Some(vec![lsp_types::TextEdit {
-            range: Range::new(Position::new(0, 0), end_position),
-            new_text: captured_stdout,
-        }]))
+        Ok(Some(to_proto::text_edit_vec(
+            &file_line_index,
+            file_line_endings,
+            diff(&file, &captured_stdout),
+        )))
     }
 }
 
index 79fe30e5301ee4e80991e8546163e25025d95e0c..682fa5f7f220bf0dbff4a5b530fc1ded73250733 100644 (file)
@@ -34,6 +34,7 @@ macro_rules! eprintln {
 mod lsp_utils;
 mod thread_pool;
 mod document;
+mod diff;
 pub mod lsp_ext;
 pub mod config;
 
index e51eb26261586927984c8f3ba12d23ee3370c352..84db0856d48ce4218c5bc120801182ed8020f42b 100644 (file)
@@ -190,15 +190,10 @@ fn main() {
         },
         json!([
             {
-                "newText": r#"mod bar;
-
-fn main() {}
-
-pub use std::collections::HashMap;
-"#,
+                "newText": "",
                 "range": {
-                    "end": { "character": 0, "line": 6 },
-                    "start": { "character": 0, "line": 0 }
+                    "end": { "character": 0, "line": 3 },
+                    "start": { "character": 11, "line": 2 }
                 }
             }
         ]),
@@ -248,17 +243,17 @@ fn main() {
         },
         json!([
             {
-                "newText": r#"mod bar;
-
-async fn test() {}
-
-fn main() {}
-
-pub use std::collections::HashMap;
-"#,
+                "newText": "",
+                "range": {
+                    "end": { "character": 0, "line": 3 },
+                    "start": { "character": 17, "line": 2 }
+                }
+            },
+            {
+                "newText": "",
                 "range": {
-                    "end": { "character": 0, "line": 9 },
-                    "start": { "character": 0, "line": 0 }
+                    "end": { "character": 0, "line": 6 },
+                    "start": { "character": 11, "line": 5 }
                 }
             }
         ]),