Compare commits

...

1 Commits

Author SHA1 Message Date
Cole Miller
bc2ceb0f1a WIP 2024-12-09 23:20:10 -05:00
4 changed files with 327 additions and 125 deletions

View File

@@ -18,9 +18,15 @@ use gpui::{
InteractiveElement, Model, Render, Subscription, Task, View, WeakView, InteractiveElement, Model, Render, Subscription, Task, View, WeakView,
}; };
use language::{Buffer, BufferRow}; use language::{Buffer, BufferRow};
use multi_buffer::{ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer}; use multi_buffer::{
Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, MultiBufferPoint,
};
use project::{Project, ProjectEntryId, ProjectPath, WorktreeId}; use project::{Project, ProjectEntryId, ProjectPath, WorktreeId};
use text::{OffsetRangeExt, ToPoint}; use rand::{
distributions::{DistString, Standard},
prelude::*,
};
use text::{Edit, OffsetRangeExt, ToPoint};
use theme::ActiveTheme; use theme::ActiveTheme;
use ui::{ use ui::{
div, h_flex, Color, Context, FluentBuilder, Icon, IconName, IntoElement, Label, LabelCommon, div, h_flex, Color, Context, FluentBuilder, Icon, IconName, IntoElement, Label, LabelCommon,
@@ -331,6 +337,7 @@ impl ProjectDiffEditor {
new_entry_order: Vec<(ProjectPath, ProjectEntryId)>, new_entry_order: Vec<(ProjectPath, ProjectEntryId)>,
cx: &mut ViewContext<ProjectDiffEditor>, cx: &mut ViewContext<ProjectDiffEditor>,
) { ) {
println!("update_excerpts.................");
if let Some(current_order) = self.entry_order.get(&worktree_id) { if let Some(current_order) = self.entry_order.get(&worktree_id) {
let current_entries = self.buffer_changes.entry(worktree_id).or_default(); let current_entries = self.buffer_changes.entry(worktree_id).or_default();
let mut new_order_entries = new_entry_order.iter().fuse().peekable(); let mut new_order_entries = new_entry_order.iter().fuse().peekable();
@@ -1099,8 +1106,11 @@ impl Render for ProjectDiffEditor {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use anyhow::anyhow;
use futures::{prelude::*, stream::FuturesUnordered};
use gpui::{SemanticVersion, TestAppContext, VisualTestContext}; use gpui::{SemanticVersion, TestAppContext, VisualTestContext};
use project::buffer_store::BufferChangeSet; use project::buffer_store::BufferChangeSet;
use rand::distributions::Alphanumeric;
use serde_json::json; use serde_json::json;
use settings::SettingsStore; use settings::SettingsStore;
use std::{ use std::{
@@ -1108,127 +1118,275 @@ mod tests {
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use crate::hunks_for_ranges;
use super::*; use super::*;
// TODO finish #[gpui::test(iterations = 100)]
// #[gpui::test] async fn random_edits(cx: &mut TestAppContext, mut rng: StdRng) {
// async fn randomized_tests(cx: &mut TestAppContext) { // TODO switch to RandomCharIter from util or the random_edits thing
// // Create a new project (how?? temp fs?), fn line(rng: &mut StdRng) -> String {
// let fs = FakeFs::new(cx.executor()); let len = rng.gen_range(0..20);
// let project = Project::test(fs, [], cx).await; let mut s = Alphanumeric.sample_string(rng, len);
s.push('\n');
s
}
// // create random files with random content fn original_file(rng: &mut StdRng) -> String {
let line_count = rng.gen_range(0..10);
(0..line_count).map(|_| line(rng)).collect()
}
// // Commit it into git somehow (technically can do with "real" fs in a temp dir) fn edit_file(rng: &mut StdRng, old: &str) -> Vec<(Range<usize>, Range<usize>, String)> {
// // let mut old_lines = old.lines().collect::<Vec<_>>().into_iter();
// // Apply randomized changes to the project: select a random file, random change and apply to buffers let mut edits = Vec::new();
// } let u = rng.gen_range(0..=old_lines.len());
let mut old_offset = old_lines
.by_ref()
.take(u)
.map(|line| line.len() + 1)
.sum::<usize>();
let mut new_offset = old_offset;
while old_lines.len() > 0 {
let d = rng.gen_range(0..=old_lines.len());
let advance = old_lines
.by_ref()
.take(d)
.map(|line| line.len() + 1)
.sum::<usize>();
let d_range = old_offset..old_offset + advance;
old_offset += advance;
let a_min = if d == 0 { 1 } else { 0 };
let a = rng.gen_range(a_min..=5);
let piece = (0..a).map(|_| line(rng)).collect::<String>();
let a_range = new_offset..new_offset + piece.len();
new_offset += piece.len();
edits.push((d_range, a_range, piece));
if old_lines.len() > 0 {
let u = rng.gen_range(1..=old_lines.len());
let advance = old_lines
.by_ref()
.take(u)
.map(|line| line.len() + 1)
.sum::<usize>();
old_offset += advance;
new_offset += advance;
}
}
edits
}
#[gpui::test(iterations = 30)]
async fn simple_edit_test(cx: &mut TestAppContext) {
cx.executor().allow_parking(); cx.executor().allow_parking();
init_test(cx); init_test(cx);
let rng = &mut rng;
let originals = HashMap::from_iter([
("file0", original_file(rng)),
// ("file1", original_file(rng)),
// ("file2", original_file(rng)),
]);
let fs = fs::FakeFs::new(cx.executor().clone()); let fs = fs::FakeFs::new(cx.executor().clone());
fs.insert_tree( let mut files = json!(originals);
"/root", files
json!({ .as_object_mut()
".git": {}, .unwrap()
"file_a": "This is file_a", .insert(".git".to_owned(), json!({}));
"file_b": "This is file_b", fs.insert_tree("/project", files).await;
}), let project = Project::test(fs.clone(), [Path::new("/project")], cx).await;
)
.await;
let project = Project::test(fs.clone(), [Path::new("/root")], cx).await;
let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx); let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
let file_a_editor = workspace let (file_editors, project_diff_editor) = workspace
.update(cx, |workspace, cx| { .update(cx, |workspace, cx| {
let file_a_editor = let file_editors = originals
workspace.open_abs_path(PathBuf::from("/root/file_a"), true, cx); .keys()
.map(|name| {
workspace.open_abs_path(
PathBuf::from(format!("/project/{}", name)),
true,
cx,
)
})
.collect::<Vec<_>>();
ProjectDiffEditor::deploy(workspace, &Deploy, cx); ProjectDiffEditor::deploy(workspace, &Deploy, cx);
file_a_editor let project_diff_editor = workspace
})
.unwrap()
.await
.expect("did not open an item at all")
.downcast::<Editor>()
.expect("did not open an editor for file_a");
let project_diff_editor = workspace
.update(cx, |workspace, cx| {
workspace
.active_pane() .active_pane()
.read(cx) .read(cx)
.items() .items()
.find_map(|item| item.downcast::<ProjectDiffEditor>()) .find_map(|item| item.downcast::<ProjectDiffEditor>())
.expect("Didn't open project diff editor");
(file_editors, project_diff_editor)
}) })
.unwrap() .unwrap();
.expect("did not find a ProjectDiffEditor"); let file_editors = file_editors
.into_iter()
.collect::<FuturesUnordered<_>>()
.map(|result| result?.downcast::<Editor>().ok_or(anyhow!("downcast")))
.try_collect::<Vec<_>>()
.await
.expect("Didn't open file editors");
project_diff_editor.update(cx, |project_diff_editor, cx| { project_diff_editor.update(cx, |project_diff_editor, cx| {
assert!( assert!(
project_diff_editor.editor.read(cx).text(cx).is_empty(), project_diff_editor.editor.read(cx).text(cx).is_empty(),
"Should have no changes after opening the diff on no git changes" "Should have no diff before files are edited"
); );
}); });
let old_text = file_a_editor.update(cx, |editor, cx| editor.text(cx)); let mut all_edits = Vec::new();
let change = "an edit after git add"; for editor in &file_editors {
file_a_editor let (mut old_text, mut edits) = (String::new(), Vec::new());
.update(cx, |file_a_editor, cx| { editor
file_a_editor.insert(change, cx); .update(cx, |editor, cx| {
file_a_editor.save(false, project.clone(), cx) old_text = dbg!(editor.text(cx));
}) edits = dbg!(edit_file(rng, &old_text));
.await editor.edit(
.expect("failed to save a file"); edits
file_a_editor.update(cx, |file_a_editor, cx| { .clone()
let change_set = cx.new_model(|cx| { .into_iter()
BufferChangeSet::new_with_base_text( .map(|(old, _new, content)| (old, content)),
old_text.clone(), cx,
file_a_editor
.buffer()
.read(cx)
.as_singleton()
.unwrap()
.read(cx)
.text_snapshot(),
cx,
)
});
file_a_editor
.diff_map
.add_change_set(change_set.clone(), cx);
project.update(cx, |project, cx| {
project.buffer_store().update(cx, |buffer_store, cx| {
buffer_store.set_change_set(
file_a_editor
.buffer()
.read(cx)
.as_singleton()
.unwrap()
.read(cx)
.remote_id(),
change_set,
); );
}); editor.save(false, project.clone(), cx)
})
.await
.expect("Failed to save edits");
let buffer_id = editor.update(cx, |editor, cx| {
let buffer = editor.buffer().read(cx).as_singleton().unwrap().read(cx);
let snapshot = buffer.text_snapshot();
let id = buffer.remote_id();
let change_set =
cx.new_model(|cx| BufferChangeSet::new_with_base_text(old_text, snapshot, cx));
editor
.diff_map
.add_change_set_with_project(project.clone(), change_set, cx);
id
}); });
}); all_edits.extend(edits.into_iter().map(|(old, new, _)| (buffer_id, old, new)));
}
fs.set_status_for_repo_via_git_operation( fs.set_status_for_repo_via_git_operation(
Path::new("/root/.git"), Path::new("/project/.git"),
&[(Path::new("file_a"), GitFileStatus::Modified)], &originals
.keys()
.map(|name| (Path::new(name), GitFileStatus::Modified))
.collect::<Vec<_>>(),
); );
cx.executor() cx.executor()
.advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(100)); .advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(100));
cx.run_until_parked(); cx.run_until_parked();
project_diff_editor.update(cx, |project_diff_editor, cx| { project_diff_editor.update(cx, |project_diff_editor, cx| {
assert_eq!( let mut hunks: Vec<_> = project_diff_editor.editor.update(cx, |editor, cx| {
// TODO assert it better: extract added text (based on the background changes) and deleted text (based on the deleted blocks added) let snapshot = editor.snapshot(cx);
project_diff_editor.editor.read(cx).text(cx), hunks_for_ranges(
format!("{change}{old_text}"), [MultiBufferPoint::zero()..snapshot.buffer_snapshot.max_point()].into_iter(),
"Should have a new change shown in the beginning, and the old text shown as deleted text afterwards" &snapshot,
); )
.into_iter()
.map(|hunk| {
let point = MultiBufferPoint::new(hunk.row_range.start.0, 0);
let buffer_snapshot = snapshot
.buffer_snapshot
.excerpt_containing(point..point)
.unwrap()
.buffer();
(
hunk.buffer_id,
hunk.diff_base_byte_range,
hunk.buffer_range.to_offset(buffer_snapshot),
)
})
.collect()
});
hunks.sort_by_key(|(buffer_id, old, _)| (*buffer_id, old.start));
all_edits.sort_by_key(|(buffer_id, old, _)| (*buffer_id, old.start));
pretty_assertions::assert_eq!(hunks, all_edits);
});
}
#[gpui::test]
async fn repro(cx: &mut TestAppContext) {
let old_text = "r4zU3hQFgVh74o\n";
let edit = (0..15, "");
let new_text = "";
cx.executor().allow_parking();
init_test(cx);
let fs = fs::FakeFs::new(cx.executor().clone());
fs.insert_tree("/project", json!({".git": {}, "file": old_text}))
.await;
let project = Project::test(fs.clone(), [Path::new("/project")], cx).await;
let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
let (editor, project_diff_editor) = workspace
.update(cx, |workspace, cx| {
let editor = workspace.open_abs_path("/project/file".into(), true, cx);
ProjectDiffEditor::deploy(workspace, &Deploy, cx);
let project_diff_editor = workspace
.active_pane()
.read(cx)
.items()
.find_map(|item| item.downcast::<ProjectDiffEditor>())
.expect("Didn't open project diff editor");
(editor, project_diff_editor)
})
.unwrap();
let editor = editor
.await
.and_then(|item| item.downcast::<Editor>().ok_or(anyhow!("downcast")))
.unwrap();
editor
.update(cx, |editor, cx| {
editor.edit([edit.clone()], cx);
editor.save(false, project.clone(), cx)
})
.await
.expect("failed to save a file");
editor.update(cx, |editor, cx| {
let change_set = cx.new_model(|cx| {
let snapshot = editor
.buffer()
.read(cx)
.as_singleton()
.unwrap()
.read(cx)
.text_snapshot();
assert_eq!(snapshot.text(), new_text);
BufferChangeSet::new_with_base_text(old_text.to_owned(), snapshot, cx)
});
editor
.diff_map
.add_change_set_with_project(project.clone(), change_set, cx);
});
fs.set_status_for_repo_via_git_operation(
Path::new("/project/.git"),
&[(Path::new("file"), GitFileStatus::Modified)],
);
cx.executor()
.advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(1000));
cx.run_until_parked();
project_diff_editor.update(cx, |project_diff_editor, cx| {
let mut hunks: Vec<_> = project_diff_editor.editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
hunks_for_ranges(
[MultiBufferPoint::zero()..snapshot.buffer_snapshot.max_point()].into_iter(),
&snapshot,
)
.into_iter()
.map(|hunk| {
let point = MultiBufferPoint::new(hunk.row_range.start.0, 0);
let buffer_snapshot = snapshot
.buffer_snapshot
.excerpt_containing(point..point)
.unwrap()
.buffer();
hunk.diff_base_byte_range
})
.collect()
});
pretty_assertions::assert_eq!(hunks, [edit.0]);
}); });
} }

View File

@@ -9,7 +9,7 @@ use multi_buffer::{
Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow, Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow,
MultiBufferSnapshot, ToOffset, ToPoint, MultiBufferSnapshot, ToOffset, ToPoint,
}; };
use project::buffer_store::BufferChangeSet; use project::{buffer_store::BufferChangeSet, Project};
use std::{ops::Range, sync::Arc}; use std::{ops::Range, sync::Arc};
use sum_tree::TreeMap; use sum_tree::TreeMap;
use text::OffsetRangeExt; use text::OffsetRangeExt;
@@ -18,7 +18,7 @@ use ui::{
ParentElement, PopoverMenu, Styled, Tooltip, ViewContext, VisualContext, ParentElement, PopoverMenu, Styled, Tooltip, ViewContext, VisualContext,
}; };
use util::RangeExt; use util::RangeExt;
use workspace::Item; use workspace::{Item, ItemHandle};
use crate::{ use crate::{
editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyAllDiffHunks, editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyAllDiffHunks,
@@ -80,6 +80,40 @@ impl DiffMap {
self.snapshot.clone() self.snapshot.clone()
} }
#[cfg(any(test, feature = "test-support"))]
pub fn add_change_set_with_project(
&mut self,
project: Model<Project>,
change_set: Model<BufferChangeSet>,
cx: &mut ViewContext<Editor>,
) {
let buffer_id = change_set.read(cx).buffer_id;
self.snapshot
.0
.insert(buffer_id, change_set.read(cx).diff_to_buffer.clone());
Editor::sync_expanded_diff_hunks(self, buffer_id, cx);
self.diff_bases.insert(
buffer_id,
DiffBaseState {
last_version: None,
_subscription: cx.observe(&change_set, move |editor, change_set, cx| {
editor
.diff_map
.snapshot
.0
.insert(buffer_id, change_set.read(cx).diff_to_buffer.clone());
Editor::sync_expanded_diff_hunks(&mut editor.diff_map, buffer_id, cx);
}),
change_set: change_set.clone(),
},
);
project.update(cx, |project, cx| {
project.buffer_store().update(cx, |buffer_store, cx| {
buffer_store.set_change_set(buffer_id, change_set);
});
});
}
pub fn add_change_set( pub fn add_change_set(
&mut self, &mut self,
change_set: Model<BufferChangeSet>, change_set: Model<BufferChangeSet>,
@@ -137,10 +171,15 @@ impl DiffMapSnapshot {
.filter_map(move |excerpt| { .filter_map(move |excerpt| {
let buffer = excerpt.buffer(); let buffer = excerpt.buffer();
let buffer_id = buffer.remote_id(); let buffer_id = buffer.remote_id();
let diff = self.0.get(&buffer_id)?; let Some(diff) = self.0.get(&buffer_id) else {
eprintln!("boom");
dbg!(&self.0);
return None;
};
let buffer_range = excerpt.map_range_to_buffer(range.clone()); let buffer_range = excerpt.map_range_to_buffer(range.clone());
let buffer_range = let buffer_range =
buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end); buffer.anchor_before(buffer_range.start)..buffer.anchor_after(buffer_range.end);
dbg!("some hunks");
Some( Some(
diff.hunks_intersecting_range(buffer_range, excerpt.buffer()) diff.hunks_intersecting_range(buffer_range, excerpt.buffer())
.map(move |hunk| { .map(move |hunk| {

View File

@@ -69,12 +69,14 @@ pub struct BufferDiff {
impl BufferDiff { impl BufferDiff {
pub fn new(buffer: &BufferSnapshot) -> BufferDiff { pub fn new(buffer: &BufferSnapshot) -> BufferDiff {
dbg!("BufferDiff::new");
BufferDiff { BufferDiff {
tree: SumTree::new(buffer), tree: SumTree::new(buffer),
} }
} }
pub async fn build(diff_base: &str, buffer: &text::BufferSnapshot) -> Self { pub async fn build(diff_base: &str, buffer: &text::BufferSnapshot) -> Self {
dbg!("BufferDiff::build");
let mut tree = SumTree::new(buffer); let mut tree = SumTree::new(buffer);
let buffer_text = buffer.as_rope().to_string(); let buffer_text = buffer.as_rope().to_string();
@@ -84,7 +86,7 @@ impl BufferDiff {
let mut divergence = 0; let mut divergence = 0;
for hunk_index in 0..patch.num_hunks() { for hunk_index in 0..patch.num_hunks() {
let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer, &mut divergence); let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer, &mut divergence);
tree.push(hunk, buffer); tree.push(dbg!(hunk), buffer);
} }
} }
@@ -141,11 +143,12 @@ impl BufferDiff {
end_point.column = 0; end_point.column = 0;
} }
Some(DiffHunk { let hunk = DiffHunk {
row_range: start_point.row..end_point.row, row_range: start_point.row..end_point.row,
diff_base_byte_range: start_base..end_base, diff_base_byte_range: start_base..end_base,
buffer_range: buffer.anchor_before(start_point)..buffer.anchor_after(end_point), buffer_range: buffer.anchor_before(start_point)..buffer.anchor_after(end_point),
}) };
Some(dbg!(hunk))
}) })
} }
@@ -187,11 +190,12 @@ impl BufferDiff {
} }
pub async fn update(&mut self, diff_base: &Rope, buffer: &text::BufferSnapshot) { pub async fn update(&mut self, diff_base: &Rope, buffer: &text::BufferSnapshot) {
dbg!("BufferDiff::update");
*self = Self::build(&diff_base.to_string(), buffer).await; *self = Self::build(&diff_base.to_string(), buffer).await;
} }
#[cfg(test)] #[cfg(any(test, feature = "test-support"))]
fn hunks<'a>(&'a self, text: &'a BufferSnapshot) -> impl 'a + Iterator<Item = DiffHunk> { pub fn hunks<'a>(&'a self, text: &'a BufferSnapshot) -> impl 'a + Iterator<Item = DiffHunk> {
let start = text.anchor_before(Point::new(0, 0)); let start = text.anchor_before(Point::new(0, 0));
let end = text.anchor_after(Point::new(u32::MAX, u32::MAX)); let end = text.anchor_after(Point::new(u32::MAX, u32::MAX));
self.hunks_intersecting_range(start..end, text) self.hunks_intersecting_range(start..end, text)
@@ -225,60 +229,60 @@ impl BufferDiff {
buffer: &text::BufferSnapshot, buffer: &text::BufferSnapshot,
buffer_row_divergence: &mut i64, buffer_row_divergence: &mut i64,
) -> InternalDiffHunk { ) -> InternalDiffHunk {
dbg!("BufferDiff::process_patch_hunk");
let line_item_count = patch.num_lines_in_hunk(hunk_index).unwrap(); let line_item_count = patch.num_lines_in_hunk(hunk_index).unwrap();
assert!(line_item_count > 0); assert!(line_item_count > 0);
let mut first_deletion_buffer_row: Option<u32> = None; let mut first_deletion_buffer_row: Option<u32> = None;
let mut buffer_row_range: Option<Range<u32>> = None; let mut buffer_row_range: Option<Range<u32>> = None;
let mut diff_base_byte_range: Option<Range<usize>> = None; let mut diff_base_byte_range: Option<Range<usize>> = None;
let fallback_offset = patch.line_in_hunk(hunk_index, 0).unwrap().content_offset() as usize;
let fallback_range = fallback_offset..fallback_offset;
for line_index in 0..line_item_count { for line_index in 0..line_item_count {
let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); let line = patch.line_in_hunk(hunk_index, line_index).unwrap();
let kind = line.origin_value(); let kind = line.origin_value();
let content_offset = line.content_offset() as isize; let content_offset = line.content_offset() as isize;
let content_len = line.content().len() as isize; let content_len = line.content().len() as isize;
match kind {
GitDiffLineType::Addition => {
*buffer_row_divergence += 1;
let row = line.new_lineno().unwrap().saturating_sub(1);
if kind == GitDiffLineType::Addition { match &mut buffer_row_range {
*buffer_row_divergence += 1; Some(buffer_row_range) => buffer_row_range.end = row + 1,
let row = line.new_lineno().unwrap().saturating_sub(1); None => buffer_row_range = Some(row..row + 1),
}
match &mut buffer_row_range {
Some(buffer_row_range) => buffer_row_range.end = row + 1,
None => buffer_row_range = Some(row..row + 1),
} }
} GitDiffLineType::Deletion => {
let end = content_offset + content_len;
if kind == GitDiffLineType::Deletion { match &mut diff_base_byte_range {
let end = content_offset + content_len; Some(head_byte_range) => head_byte_range.end = end as usize,
None => diff_base_byte_range = Some(content_offset as usize..end as usize),
}
match &mut diff_base_byte_range { if first_deletion_buffer_row.is_none() {
Some(head_byte_range) => head_byte_range.end = end as usize, let old_row = line.old_lineno().unwrap().saturating_sub(1);
None => diff_base_byte_range = Some(content_offset as usize..end as usize), let row = old_row as i64 + *buffer_row_divergence;
first_deletion_buffer_row = Some(row as u32);
}
*buffer_row_divergence -= 1;
} }
_ => {}
if first_deletion_buffer_row.is_none() {
let old_row = line.old_lineno().unwrap().saturating_sub(1);
let row = old_row as i64 + *buffer_row_divergence;
first_deletion_buffer_row = Some(row as u32);
}
*buffer_row_divergence -= 1;
} }
} }
//unwrap_or deletion without addition
let buffer_row_range = buffer_row_range.unwrap_or_else(|| { let buffer_row_range = buffer_row_range.unwrap_or_else(|| {
//we cannot have an addition-less hunk without deletion(s) or else there would be no hunk // Pure deletion hunk without addition.
let row = first_deletion_buffer_row.unwrap(); let row = first_deletion_buffer_row.unwrap();
row..row row..row
}); });
//unwrap_or addition without deletion
let diff_base_byte_range = diff_base_byte_range.unwrap_or(0..0);
let start = Point::new(buffer_row_range.start, 0); let start = Point::new(buffer_row_range.start, 0);
let end = Point::new(buffer_row_range.end, 0); let end = Point::new(buffer_row_range.end, 0);
let buffer_range = buffer.anchor_before(start)..buffer.anchor_before(end); let buffer_range = buffer.anchor_before(start)..buffer.anchor_before(end);
let diff_base_byte_range = diff_base_byte_range.unwrap_or(fallback_range);
InternalDiffHunk { InternalDiffHunk {
buffer_range, buffer_range,
diff_base_byte_range, diff_base_byte_range,

View File

@@ -2198,6 +2198,7 @@ impl BufferChangeSet {
range: Range<text::Anchor>, range: Range<text::Anchor>,
buffer_snapshot: &'a text::BufferSnapshot, buffer_snapshot: &'a text::BufferSnapshot,
) -> impl 'a + Iterator<Item = git::diff::DiffHunk> { ) -> impl 'a + Iterator<Item = git::diff::DiffHunk> {
dbg!("");
self.diff_to_buffer self.diff_to_buffer
.hunks_intersecting_range(range, buffer_snapshot) .hunks_intersecting_range(range, buffer_snapshot)
} }