mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-26 14:00:51 +00:00
files: make diff() return an iterator instead of using a callback
Iterators are generally nicer to work with. My immediate goal is to be able to propagate errors when failing to write to stdout.
This commit is contained in:
parent
f22514f7bb
commit
f634ff0e3f
2 changed files with 88 additions and 48 deletions
130
lib/src/files.rs
130
lib/src/files.rs
|
@ -12,10 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::{Debug, Error, Formatter};
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::diff;
|
||||
use crate::diff::SliceDiff;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum DiffHunk<'a> {
|
||||
|
@ -47,58 +49,96 @@ impl DiffLine<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn diff<'a>(left: &'a [u8], right: &'a [u8], callback: &mut impl FnMut(&DiffLine<'a>)) {
|
||||
// TODO: Should we attempt to interpret as utf-8 and otherwise break only at
|
||||
// newlines?
|
||||
let mut diff_line = DiffLine {
|
||||
left_line_number: 1,
|
||||
right_line_number: 1,
|
||||
has_left_content: false,
|
||||
has_right_content: false,
|
||||
hunks: vec![],
|
||||
};
|
||||
for hunk in diff::diff(left, right) {
|
||||
match hunk {
|
||||
diff::SliceDiff::Unchanged(text) => {
|
||||
let lines = text.split_inclusive(|b| *b == b'\n');
|
||||
for line in lines {
|
||||
diff_line.has_left_content = true;
|
||||
diff_line.has_right_content = true;
|
||||
diff_line.hunks.push(DiffHunk::Unmodified(line));
|
||||
if line.ends_with(b"\n") {
|
||||
callback(&diff_line);
|
||||
diff_line.left_line_number += 1;
|
||||
diff_line.right_line_number += 1;
|
||||
diff_line.reset_line();
|
||||
pub fn diff<'a>(left: &'a [u8], right: &'a [u8]) -> DiffLineIterator<'a> {
|
||||
let slice_diffs = diff::diff(left, right);
|
||||
DiffLineIterator::new(slice_diffs)
|
||||
}
|
||||
|
||||
pub struct DiffLineIterator<'a> {
|
||||
slice_diffs: Vec<SliceDiff<'a>>,
|
||||
current_pos: usize,
|
||||
current_line: DiffLine<'a>,
|
||||
queued_lines: VecDeque<DiffLine<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> DiffLineIterator<'a> {
|
||||
fn new(slice_diffs: Vec<SliceDiff<'a>>) -> Self {
|
||||
let current_line = DiffLine {
|
||||
left_line_number: 1,
|
||||
right_line_number: 1,
|
||||
has_left_content: false,
|
||||
has_right_content: false,
|
||||
hunks: vec![],
|
||||
};
|
||||
DiffLineIterator {
|
||||
slice_diffs,
|
||||
current_pos: 0,
|
||||
current_line,
|
||||
queued_lines: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DiffLineIterator<'a> {
|
||||
type Item = DiffLine<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// TODO: Should we attempt to interpret as utf-8 and otherwise break only at
|
||||
// newlines?
|
||||
while self.current_pos < self.slice_diffs.len() && self.queued_lines.is_empty() {
|
||||
let hunk = &self.slice_diffs[self.current_pos];
|
||||
self.current_pos += 1;
|
||||
match hunk {
|
||||
diff::SliceDiff::Unchanged(text) => {
|
||||
let lines = text.split_inclusive(|b| *b == b'\n');
|
||||
for line in lines {
|
||||
self.current_line.has_left_content = true;
|
||||
self.current_line.has_right_content = true;
|
||||
self.current_line.hunks.push(DiffHunk::Unmodified(line));
|
||||
if line.ends_with(b"\n") {
|
||||
self.queued_lines.push_back(self.current_line.clone());
|
||||
self.current_line.left_line_number += 1;
|
||||
self.current_line.right_line_number += 1;
|
||||
self.current_line.reset_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
diff::SliceDiff::Replaced(left, right) => {
|
||||
let left_lines = left.split_inclusive(|b| *b == b'\n');
|
||||
for left_line in left_lines {
|
||||
diff_line.has_left_content = true;
|
||||
diff_line.hunks.push(DiffHunk::Removed(left_line));
|
||||
if left_line.ends_with(b"\n") {
|
||||
callback(&diff_line);
|
||||
diff_line.left_line_number += 1;
|
||||
diff_line.reset_line();
|
||||
diff::SliceDiff::Replaced(left, right) => {
|
||||
let left_lines = left.split_inclusive(|b| *b == b'\n');
|
||||
for left_line in left_lines {
|
||||
self.current_line.has_left_content = true;
|
||||
self.current_line.hunks.push(DiffHunk::Removed(left_line));
|
||||
if left_line.ends_with(b"\n") {
|
||||
self.queued_lines.push_back(self.current_line.clone());
|
||||
self.current_line.left_line_number += 1;
|
||||
self.current_line.reset_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
let right_lines = right.split_inclusive(|b| *b == b'\n');
|
||||
for right_line in right_lines {
|
||||
diff_line.has_right_content = true;
|
||||
diff_line.hunks.push(DiffHunk::Added(right_line));
|
||||
if right_line.ends_with(b"\n") {
|
||||
callback(&diff_line);
|
||||
diff_line.right_line_number += 1;
|
||||
diff_line.reset_line();
|
||||
let right_lines = right.split_inclusive(|b| *b == b'\n');
|
||||
for right_line in right_lines {
|
||||
self.current_line.has_right_content = true;
|
||||
self.current_line.hunks.push(DiffHunk::Added(right_line));
|
||||
if right_line.ends_with(b"\n") {
|
||||
self.queued_lines.push_back(self.current_line.clone());
|
||||
self.current_line.right_line_number += 1;
|
||||
self.current_line.reset_line();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !diff_line.hunks.is_empty() {
|
||||
callback(&diff_line);
|
||||
|
||||
if let Some(line) = self.queued_lines.pop_front() {
|
||||
return Some(line);
|
||||
}
|
||||
|
||||
if !self.current_line.hunks.is_empty() {
|
||||
let line = self.current_line.clone();
|
||||
self.current_line.reset_line();
|
||||
return Some(line);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -687,7 +687,7 @@ fn print_diff(left: &[u8], right: &[u8], styler: &mut dyn Styler) {
|
|||
let mut skipped_context = false;
|
||||
// Are the lines in `context` to be printed before the next modified line?
|
||||
let mut context_before = true;
|
||||
files::diff(left, right, &mut |diff_line| {
|
||||
for diff_line in files::diff(left, right) {
|
||||
if diff_line.is_unmodified() {
|
||||
context.push_back(diff_line.clone());
|
||||
if context.len() > num_context_lines {
|
||||
|
@ -715,11 +715,11 @@ fn print_diff(left: &[u8], right: &[u8], styler: &mut dyn Styler) {
|
|||
}
|
||||
}
|
||||
context.clear();
|
||||
print_diff_line(styler, diff_line);
|
||||
print_diff_line(styler, &diff_line);
|
||||
context_before = false;
|
||||
skipped_context = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if !context_before {
|
||||
for line in &context {
|
||||
print_diff_line(styler, line);
|
||||
|
|
Loading…
Reference in a new issue