mirror of https://github.com/helix-editor/helix
`:reload` (#374)
* reloading functionality * fn with_newline_eof() * fmt * wip * wip * wip * wip * moved to core, added simd feature for encoding_rs * wip * rm * .gitignore * wip * local wip * wip * wip * no features * wip * nit * remove simd * doc * clippy * clippy * address comments * add indentation & line ending changepull/403/head
parent
e177b27baf
commit
c5b2973739
@ -0,0 +1,70 @@
|
||||
use ropey::Rope;
|
||||
|
||||
use crate::{Change, Transaction};
|
||||
|
||||
/// Compares `old` and `new` to generate a [`Transaction`] describing
|
||||
/// the steps required to get from `old` to `new`.
|
||||
pub fn compare_ropes(old: &Rope, new: &Rope) -> Transaction {
|
||||
// `similar` only works on contiguous data, so a `Rope` has
|
||||
// to be temporarily converted into a `String`.
|
||||
let old_converted = old.to_string();
|
||||
let new_converted = new.to_string();
|
||||
|
||||
// A timeout is set so after 1 seconds, the algorithm will start
|
||||
// approximating. This is especially important for big `Rope`s or
|
||||
// `Rope`s that are extremely dissimilar to each other.
|
||||
//
|
||||
// Note: Ignore the clippy warning, as the trait bounds of
|
||||
// `Transaction::change()` require an iterator implementing
|
||||
// `ExactIterator`.
|
||||
let mut config = similar::TextDiff::configure();
|
||||
config.timeout(std::time::Duration::from_secs(1));
|
||||
|
||||
let diff = config.diff_chars(&old_converted, &new_converted);
|
||||
|
||||
// The current position of the change needs to be tracked to
|
||||
// construct the `Change`s.
|
||||
let mut pos = 0;
|
||||
let changes: Vec<Change> = diff
|
||||
.ops()
|
||||
.iter()
|
||||
.map(|op| op.as_tag_tuple())
|
||||
.filter_map(|(tag, old_range, new_range)| {
|
||||
// `old_pos..pos` is equivalent to `start..end` for where
|
||||
// the change should be applied.
|
||||
let old_pos = pos;
|
||||
pos += old_range.end - old_range.start;
|
||||
|
||||
match tag {
|
||||
// Semantically, inserts and replacements are the same thing.
|
||||
similar::DiffTag::Insert | similar::DiffTag::Replace => {
|
||||
// This is the text from the `new` rope that should be
|
||||
// inserted into `old`.
|
||||
let text: &str = {
|
||||
let start = new.char_to_byte(new_range.start);
|
||||
let end = new.char_to_byte(new_range.end);
|
||||
&new_converted[start..end]
|
||||
};
|
||||
Some((old_pos, pos, Some(text.into())))
|
||||
}
|
||||
similar::DiffTag::Delete => Some((old_pos, pos, None)),
|
||||
similar::DiffTag::Equal => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Transaction::change(old, changes.into_iter())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
quickcheck::quickcheck! {
|
||||
fn test_compare_ropes(a: String, b: String) -> bool {
|
||||
let mut old = Rope::from(a);
|
||||
let new = Rope::from(b);
|
||||
compare_ropes(&old, &new).apply(&mut old);
|
||||
old.to_string() == new.to_string()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue