Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-core/src/diff.rs')
-rw-r--r--helix-core/src/diff.rs86
1 files changed, 48 insertions, 38 deletions
diff --git a/helix-core/src/diff.rs b/helix-core/src/diff.rs
index 5937f91c..a5d6d722 100644
--- a/helix-core/src/diff.rs
+++ b/helix-core/src/diff.rs
@@ -1,22 +1,51 @@
use std::ops::Range;
use std::time::Instant;
-use imara_diff::{Algorithm, Diff, Hunk, IndentHeuristic, IndentLevel, InternedInput};
+use imara_diff::intern::InternedInput;
+use imara_diff::Algorithm;
use ropey::RopeSlice;
use crate::{ChangeSet, Rope, Tendril, Transaction};
-struct ChangeSetBuilder<'a> {
+/// A `imara_diff::Sink` that builds a `ChangeSet` for a character diff of a hunk
+struct CharChangeSetBuilder<'a> {
+ res: &'a mut ChangeSet,
+ hunk: &'a InternedInput<char>,
+ pos: u32,
+}
+
+impl imara_diff::Sink for CharChangeSetBuilder<'_> {
+ type Out = ();
+ fn process_change(&mut self, before: Range<u32>, after: Range<u32>) {
+ self.res.retain((before.start - self.pos) as usize);
+ self.res.delete(before.len());
+ self.pos = before.end;
+
+ let res = self.hunk.after[after.start as usize..after.end as usize]
+ .iter()
+ .map(|&token| self.hunk.interner[token])
+ .collect();
+
+ self.res.insert(res);
+ }
+
+ fn finish(self) -> Self::Out {
+ self.res.retain(self.hunk.before.len() - self.pos as usize);
+ }
+}
+
+struct LineChangeSetBuilder<'a> {
res: ChangeSet,
after: RopeSlice<'a>,
file: &'a InternedInput<RopeSlice<'a>>,
current_hunk: InternedInput<char>,
- char_diff: Diff,
pos: u32,
}
-impl ChangeSetBuilder<'_> {
- fn process_hunk(&mut self, before: Range<u32>, after: Range<u32>) {
+impl imara_diff::Sink for LineChangeSetBuilder<'_> {
+ type Out = ChangeSet;
+
+ fn process_change(&mut self, before: Range<u32>, after: Range<u32>) {
let len = self.file.before[self.pos as usize..before.start as usize]
.iter()
.map(|&it| self.file.interner[it].len_chars())
@@ -80,36 +109,25 @@ impl ChangeSetBuilder<'_> {
.flat_map(|&it| self.file.interner[it].chars());
self.current_hunk.update_before(hunk_before);
self.current_hunk.update_after(hunk_after);
+
// the histogram heuristic does not work as well
// for characters because the same characters often reoccur
// use myer diff instead
- self.char_diff.compute_with(
+ imara_diff::diff(
Algorithm::Myers,
- &self.current_hunk.before,
- &self.current_hunk.after,
- self.current_hunk.interner.num_tokens(),
+ &self.current_hunk,
+ CharChangeSetBuilder {
+ res: &mut self.res,
+ hunk: &self.current_hunk,
+ pos: 0,
+ },
);
- let mut pos = 0;
- for Hunk { before, after } in self.char_diff.hunks() {
- self.res.retain((before.start - pos) as usize);
- self.res.delete(before.len());
- pos = before.end;
-
- let res = self.current_hunk.after[after.start as usize..after.end as usize]
- .iter()
- .map(|&token| self.current_hunk.interner[token])
- .collect();
-
- self.res.insert(res);
- }
- self.res
- .retain(self.current_hunk.before.len() - pos as usize);
- // reuse allocations
+
self.current_hunk.clear();
}
}
- fn finish(mut self) -> ChangeSet {
+ fn finish(mut self) -> Self::Out {
let len = self.file.before[self.pos as usize..]
.iter()
.map(|&it| self.file.interner[it].len_chars())
@@ -122,7 +140,7 @@ impl ChangeSetBuilder<'_> {
struct RopeLines<'a>(RopeSlice<'a>);
-impl<'a> imara_diff::TokenSource for RopeLines<'a> {
+impl<'a> imara_diff::intern::TokenSource for RopeLines<'a> {
type Token = RopeSlice<'a>;
type Tokenizer = ropey::iter::Lines<'a>;
@@ -143,23 +161,15 @@ pub fn compare_ropes(before: &Rope, after: &Rope) -> Transaction {
let res = ChangeSet::with_capacity(32);
let after = after.slice(..);
let file = InternedInput::new(RopeLines(before.slice(..)), RopeLines(after));
- let mut builder = ChangeSetBuilder {
+ let builder = LineChangeSetBuilder {
res,
file: &file,
after,
pos: 0,
current_hunk: InternedInput::default(),
- char_diff: Diff::default(),
};
- let mut diff = Diff::compute(Algorithm::Histogram, &file);
- diff.postprocess_with_heuristic(
- &file,
- IndentHeuristic::new(|token| IndentLevel::for_ascii_line(file.interner[token].bytes(), 4)),
- );
- for hunk in diff.hunks() {
- builder.process_hunk(hunk.before, hunk.after)
- }
- let res = builder.finish().into();
+
+ let res = imara_diff::diff(Algorithm::Histogram, &file, builder).into();
log::debug!(
"rope diff took {}s",