mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 07:04:22 +00:00
Split textDocument/formatting TextEdit with diff
This commit is contained in:
parent
9bb9fbab3a
commit
f355a6d831
6 changed files with 82 additions and 23 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -347,6 +347,12 @@ version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dissimilar"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "drop_bomb"
|
name = "drop_bomb"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -1333,6 +1339,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg",
|
"cfg",
|
||||||
"crossbeam-channel 0.5.0",
|
"crossbeam-channel 0.5.0",
|
||||||
|
"dissimilar",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"expect-test",
|
"expect-test",
|
||||||
"flycheck",
|
"flycheck",
|
||||||
|
|
|
@ -17,6 +17,7 @@ path = "src/bin/main.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.26"
|
anyhow = "1.0.26"
|
||||||
crossbeam-channel = "0.5.0"
|
crossbeam-channel = "0.5.0"
|
||||||
|
dissimilar = "1.0.2"
|
||||||
env_logger = { version = "0.8.1", default-features = false }
|
env_logger = { version = "0.8.1", default-features = false }
|
||||||
itertools = "0.9.0"
|
itertools = "0.9.0"
|
||||||
jod-thread = "0.1.0"
|
jod-thread = "0.1.0"
|
||||||
|
|
53
crates/rust-analyzer/src/diff.rs
Normal file
53
crates/rust-analyzer/src/diff.rs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ use serde_json::to_value;
|
||||||
use stdx::{format_to, split_once};
|
use stdx::{format_to, split_once};
|
||||||
use syntax::{algo, ast, AstNode, TextRange, TextSize};
|
use syntax::{algo, ast, AstNode, TextRange, TextSize};
|
||||||
|
|
||||||
|
use crate::diff::diff;
|
||||||
use crate::{
|
use crate::{
|
||||||
cargo_target_spec::CargoTargetSpec,
|
cargo_target_spec::CargoTargetSpec,
|
||||||
config::RustfmtConfig,
|
config::RustfmtConfig,
|
||||||
|
@ -799,7 +800,7 @@ pub(crate) fn handle_formatting(
|
||||||
let crate_ids = snap.analysis.crate_for(file_id)?;
|
let crate_ids = snap.analysis.crate_for(file_id)?;
|
||||||
|
|
||||||
let file_line_index = snap.analysis.file_line_index(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 {
|
let mut rustfmt = match &snap.config.rustfmt {
|
||||||
RustfmtConfig::Rustfmt { extra_args } => {
|
RustfmtConfig::Rustfmt { extra_args } => {
|
||||||
|
@ -858,10 +859,11 @@ pub(crate) fn handle_formatting(
|
||||||
// The document is already formatted correctly -- no edits needed.
|
// The document is already formatted correctly -- no edits needed.
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(vec![lsp_types::TextEdit {
|
Ok(Some(to_proto::text_edit_vec(
|
||||||
range: Range::new(Position::new(0, 0), end_position),
|
&file_line_index,
|
||||||
new_text: captured_stdout,
|
file_line_endings,
|
||||||
}]))
|
diff(&file, &captured_stdout),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ mod request_metrics;
|
||||||
mod lsp_utils;
|
mod lsp_utils;
|
||||||
mod thread_pool;
|
mod thread_pool;
|
||||||
mod document;
|
mod document;
|
||||||
|
mod diff;
|
||||||
pub mod lsp_ext;
|
pub mod lsp_ext;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
|
|
|
@ -190,15 +190,10 @@ pub use std::collections::HashMap;
|
||||||
},
|
},
|
||||||
json!([
|
json!([
|
||||||
{
|
{
|
||||||
"newText": r#"mod bar;
|
"newText": "",
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
pub use std::collections::HashMap;
|
|
||||||
"#,
|
|
||||||
"range": {
|
"range": {
|
||||||
"end": { "character": 0, "line": 6 },
|
"end": { "character": 0, "line": 3 },
|
||||||
"start": { "character": 0, "line": 0 }
|
"start": { "character": 11, "line": 2 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]),
|
]),
|
||||||
|
@ -248,17 +243,17 @@ pub use std::collections::HashMap;
|
||||||
},
|
},
|
||||||
json!([
|
json!([
|
||||||
{
|
{
|
||||||
"newText": r#"mod bar;
|
"newText": "",
|
||||||
|
|
||||||
async fn test() {}
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
pub use std::collections::HashMap;
|
|
||||||
"#,
|
|
||||||
"range": {
|
"range": {
|
||||||
"end": { "character": 0, "line": 9 },
|
"end": { "character": 0, "line": 3 },
|
||||||
"start": { "character": 0, "line": 0 }
|
"start": { "character": 17, "line": 2 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"newText": "",
|
||||||
|
"range": {
|
||||||
|
"end": { "character": 0, "line": 6 },
|
||||||
|
"start": { "character": 11, "line": 5 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]),
|
]),
|
||||||
|
|
Loading…
Reference in a new issue