Introduce diff crate to unite BufferDiff and BufferChangeSet (#24392)
This is a refactoring PR that does three things: - First, it introduces a new `diff` crate that holds the previous contents of the `git::diff` module, plus the `BufferChangeSet` type formerly of `project::buffer_store`. The new crate is necessary since simply moving `BufferChangeSet` into `git::diff` results in a dependency cycle due to the use of `language::Buffer` to represent the diff base in `BufferChangeSet`. - Second, it renames the two main types in the new diff crate: `BufferDiff` becomes `BufferDiffSnapshot`, and `BufferChangeSet` becomes `BufferDiff`. This reflects that the relationship between these two types (immutable cheaply-cloneable "value" type + stateful "resource type" with subscriptions) mirrors existing pairs like `Buffer`/`BufferSnapshot`. References to "change sets" throughout the codebase are updated to refer to "diffs" instead. - Finally, it moves the base_text field of the new BufferDiff type to BufferDiffSnapshot. Release Notes: - N/A --------- Co-authored-by: maxbrunsfeld <max@zed.dev>
This commit is contained in:
parent
ffcad71bfa
commit
73c487c222
31 changed files with 922 additions and 875 deletions
|
@ -14,9 +14,10 @@ doctest = false
|
|||
|
||||
[features]
|
||||
test-support = [
|
||||
"text/test-support",
|
||||
"language/test-support",
|
||||
"diff/test-support",
|
||||
"gpui/test-support",
|
||||
"language/test-support",
|
||||
"text/test-support",
|
||||
"util/test-support",
|
||||
]
|
||||
|
||||
|
@ -25,15 +26,14 @@ anyhow.workspace = true
|
|||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
ctor.workspace = true
|
||||
diff.workspace = true
|
||||
env_logger.workspace = true
|
||||
futures.workspace = true
|
||||
git.workspace = true
|
||||
gpui.workspace = true
|
||||
itertools.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
project.workspace = true
|
||||
rand.workspace = true
|
||||
rope.workspace = true
|
||||
smol.workspace = true
|
||||
|
@ -47,12 +47,13 @@ tree-sitter.workspace = true
|
|||
util.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
diff = { workspace = true, features = ["test-support"] }
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
indoc.workspace = true
|
||||
language = { workspace = true, features = ["test-support"] }
|
||||
pretty_assertions.workspace = true
|
||||
project = { workspace = true, features = ["test-support"] }
|
||||
rand.workspace = true
|
||||
settings = { workspace = true, features = ["test-support"] }
|
||||
text = { workspace = true, features = ["test-support"] }
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
pretty_assertions.workspace = true
|
||||
indoc.workspace = true
|
||||
|
|
|
@ -70,15 +70,15 @@ impl Anchor {
|
|||
return text_cmp;
|
||||
}
|
||||
if self.diff_base_anchor.is_some() || other.diff_base_anchor.is_some() {
|
||||
if let Some(diff_base) = snapshot.diffs.get(&excerpt.buffer_id) {
|
||||
let self_anchor = self
|
||||
.diff_base_anchor
|
||||
.filter(|a| diff_base.base_text.can_resolve(a));
|
||||
let other_anchor = other
|
||||
.diff_base_anchor
|
||||
.filter(|a| diff_base.base_text.can_resolve(a));
|
||||
if let Some(base_text) = snapshot
|
||||
.diffs
|
||||
.get(&excerpt.buffer_id)
|
||||
.and_then(|diff| diff.base_text.as_ref())
|
||||
{
|
||||
let self_anchor = self.diff_base_anchor.filter(|a| base_text.can_resolve(a));
|
||||
let other_anchor = other.diff_base_anchor.filter(|a| base_text.can_resolve(a));
|
||||
return match (self_anchor, other_anchor) {
|
||||
(Some(a), Some(b)) => a.cmp(&b, &diff_base.base_text),
|
||||
(Some(a), Some(b)) => a.cmp(&b, base_text),
|
||||
(Some(_), None) => match other.text_anchor.bias {
|
||||
Bias::Left => Ordering::Greater,
|
||||
Bias::Right => Ordering::Less,
|
||||
|
@ -107,9 +107,13 @@ impl Anchor {
|
|||
excerpt_id: self.excerpt_id,
|
||||
text_anchor: self.text_anchor.bias_left(&excerpt.buffer),
|
||||
diff_base_anchor: self.diff_base_anchor.map(|a| {
|
||||
if let Some(base) = snapshot.diffs.get(&excerpt.buffer_id) {
|
||||
if a.buffer_id == Some(base.base_text.remote_id()) {
|
||||
return a.bias_left(&base.base_text);
|
||||
if let Some(base_text) = snapshot
|
||||
.diffs
|
||||
.get(&excerpt.buffer_id)
|
||||
.and_then(|diff| diff.base_text.as_ref())
|
||||
{
|
||||
if a.buffer_id == Some(base_text.remote_id()) {
|
||||
return a.bias_left(base_text);
|
||||
}
|
||||
}
|
||||
a
|
||||
|
@ -128,9 +132,13 @@ impl Anchor {
|
|||
excerpt_id: self.excerpt_id,
|
||||
text_anchor: self.text_anchor.bias_right(&excerpt.buffer),
|
||||
diff_base_anchor: self.diff_base_anchor.map(|a| {
|
||||
if let Some(base) = snapshot.diffs.get(&excerpt.buffer_id) {
|
||||
if a.buffer_id == Some(base.base_text.remote_id()) {
|
||||
return a.bias_right(&base.base_text);
|
||||
if let Some(base_text) = snapshot
|
||||
.diffs
|
||||
.get(&excerpt.buffer_id)
|
||||
.and_then(|diff| diff.base_text.as_ref())
|
||||
{
|
||||
if a.buffer_id == Some(base_text.remote_id()) {
|
||||
return a.bias_right(&base_text);
|
||||
}
|
||||
}
|
||||
a
|
||||
|
|
|
@ -9,8 +9,8 @@ pub use position::{TypedOffset, TypedPoint, TypedRow};
|
|||
use anyhow::{anyhow, Result};
|
||||
use clock::ReplicaId;
|
||||
use collections::{BTreeMap, Bound, HashMap, HashSet};
|
||||
use diff::{BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffHunkStatus};
|
||||
use futures::{channel::mpsc, SinkExt};
|
||||
use git::diff::DiffHunkStatus;
|
||||
use gpui::{App, Context, Entity, EntityId, EventEmitter, Task};
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
|
@ -21,7 +21,7 @@ use language::{
|
|||
TextDimension, TextObject, ToOffset as _, ToPoint as _, TransactionId, TreeSitterOptions,
|
||||
Unclipped,
|
||||
};
|
||||
use project::buffer_store::{BufferChangeSet, BufferChangeSetEvent};
|
||||
|
||||
use rope::DimensionPair;
|
||||
use smallvec::SmallVec;
|
||||
use smol::future::yield_now;
|
||||
|
@ -68,7 +68,7 @@ pub struct MultiBuffer {
|
|||
buffers: RefCell<HashMap<BufferId, BufferState>>,
|
||||
// only used by consumers using `set_excerpts_for_buffer`
|
||||
buffers_by_path: BTreeMap<PathKey, Vec<ExcerptId>>,
|
||||
diff_bases: HashMap<BufferId, ChangeSetState>,
|
||||
diffs: HashMap<BufferId, DiffState>,
|
||||
all_diff_hunks_expanded: bool,
|
||||
subscriptions: Topic,
|
||||
/// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`]
|
||||
|
@ -215,23 +215,21 @@ struct BufferState {
|
|||
_subscriptions: [gpui::Subscription; 2],
|
||||
}
|
||||
|
||||
struct ChangeSetState {
|
||||
change_set: Entity<BufferChangeSet>,
|
||||
struct DiffState {
|
||||
diff: Entity<BufferDiff>,
|
||||
_subscription: gpui::Subscription,
|
||||
}
|
||||
|
||||
impl ChangeSetState {
|
||||
fn new(change_set: Entity<BufferChangeSet>, cx: &mut Context<MultiBuffer>) -> Self {
|
||||
ChangeSetState {
|
||||
_subscription: cx.subscribe(&change_set, |this, change_set, event, cx| match event {
|
||||
BufferChangeSetEvent::DiffChanged { changed_range } => {
|
||||
this.buffer_diff_changed(change_set, changed_range.clone(), cx)
|
||||
}
|
||||
BufferChangeSetEvent::LanguageChanged => {
|
||||
this.buffer_diff_language_changed(change_set, cx)
|
||||
impl DiffState {
|
||||
fn new(diff: Entity<BufferDiff>, cx: &mut Context<MultiBuffer>) -> Self {
|
||||
DiffState {
|
||||
_subscription: cx.subscribe(&diff, |this, diff, event, cx| match event {
|
||||
BufferDiffEvent::DiffChanged { changed_range } => {
|
||||
this.buffer_diff_changed(diff, changed_range.clone(), cx)
|
||||
}
|
||||
BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
|
||||
}),
|
||||
change_set,
|
||||
diff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +240,7 @@ pub struct MultiBufferSnapshot {
|
|||
singleton: bool,
|
||||
excerpts: SumTree<Excerpt>,
|
||||
excerpt_ids: SumTree<ExcerptIdMapping>,
|
||||
diffs: TreeMap<BufferId, DiffSnapshot>,
|
||||
diffs: TreeMap<BufferId, BufferDiffSnapshot>,
|
||||
pub diff_transforms: SumTree<DiffTransform>,
|
||||
trailing_excerpt_update_count: usize,
|
||||
non_text_state_update_count: usize,
|
||||
|
@ -268,12 +266,6 @@ pub enum DiffTransform {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DiffSnapshot {
|
||||
diff: git::diff::BufferDiff,
|
||||
base_text: language::BufferSnapshot,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExcerptInfo {
|
||||
pub id: ExcerptId,
|
||||
|
@ -318,7 +310,7 @@ pub struct RowInfo {
|
|||
pub buffer_id: Option<BufferId>,
|
||||
pub buffer_row: Option<u32>,
|
||||
pub multibuffer_row: Option<MultiBufferRow>,
|
||||
pub diff_status: Option<git::diff::DiffHunkStatus>,
|
||||
pub diff_status: Option<diff::DiffHunkStatus>,
|
||||
}
|
||||
|
||||
/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].
|
||||
|
@ -397,7 +389,7 @@ pub struct MultiBufferRows<'a> {
|
|||
pub struct MultiBufferChunks<'a> {
|
||||
excerpts: Cursor<'a, Excerpt, ExcerptOffset>,
|
||||
diff_transforms: Cursor<'a, DiffTransform, (usize, ExcerptOffset)>,
|
||||
diffs: &'a TreeMap<BufferId, DiffSnapshot>,
|
||||
diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>,
|
||||
diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>,
|
||||
buffer_chunk: Option<Chunk<'a>>,
|
||||
range: Range<usize>,
|
||||
|
@ -431,7 +423,7 @@ pub struct ReversedMultiBufferBytes<'a> {
|
|||
struct MultiBufferCursor<'a, D: TextDimension> {
|
||||
excerpts: Cursor<'a, Excerpt, ExcerptDimension<D>>,
|
||||
diff_transforms: Cursor<'a, DiffTransform, (OutputDimension<D>, ExcerptDimension<D>)>,
|
||||
diffs: &'a TreeMap<BufferId, DiffSnapshot>,
|
||||
diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>,
|
||||
cached_region: Option<MultiBufferRegion<'a, D>>,
|
||||
}
|
||||
|
||||
|
@ -517,7 +509,7 @@ impl MultiBuffer {
|
|||
..MultiBufferSnapshot::default()
|
||||
}),
|
||||
buffers: RefCell::default(),
|
||||
diff_bases: HashMap::default(),
|
||||
diffs: HashMap::default(),
|
||||
all_diff_hunks_expanded: false,
|
||||
subscriptions: Topic::default(),
|
||||
singleton: false,
|
||||
|
@ -539,7 +531,7 @@ impl MultiBuffer {
|
|||
snapshot: Default::default(),
|
||||
buffers: Default::default(),
|
||||
buffers_by_path: Default::default(),
|
||||
diff_bases: HashMap::default(),
|
||||
diffs: HashMap::default(),
|
||||
all_diff_hunks_expanded: false,
|
||||
subscriptions: Default::default(),
|
||||
singleton: false,
|
||||
|
@ -573,17 +565,14 @@ impl MultiBuffer {
|
|||
);
|
||||
}
|
||||
let mut diff_bases = HashMap::default();
|
||||
for (buffer_id, change_set_state) in self.diff_bases.iter() {
|
||||
diff_bases.insert(
|
||||
*buffer_id,
|
||||
ChangeSetState::new(change_set_state.change_set.clone(), new_cx),
|
||||
);
|
||||
for (buffer_id, diff) in self.diffs.iter() {
|
||||
diff_bases.insert(*buffer_id, DiffState::new(diff.diff.clone(), new_cx));
|
||||
}
|
||||
Self {
|
||||
snapshot: RefCell::new(self.snapshot.borrow().clone()),
|
||||
buffers: RefCell::new(buffers),
|
||||
buffers_by_path: Default::default(),
|
||||
diff_bases,
|
||||
diffs: diff_bases,
|
||||
all_diff_hunks_expanded: self.all_diff_hunks_expanded,
|
||||
subscriptions: Default::default(),
|
||||
singleton: self.singleton,
|
||||
|
@ -2152,71 +2141,49 @@ impl MultiBuffer {
|
|||
});
|
||||
}
|
||||
|
||||
fn buffer_diff_language_changed(
|
||||
&mut self,
|
||||
change_set: Entity<BufferChangeSet>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
fn buffer_diff_language_changed(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
|
||||
self.sync(cx);
|
||||
let mut snapshot = self.snapshot.borrow_mut();
|
||||
let change_set = change_set.read(cx);
|
||||
let buffer_id = change_set.buffer_id;
|
||||
let base_text = change_set.base_text.clone();
|
||||
let diff = change_set.diff_to_buffer.clone();
|
||||
if let Some(base_text) = base_text {
|
||||
snapshot.diffs.insert(
|
||||
buffer_id,
|
||||
DiffSnapshot {
|
||||
diff: diff.clone(),
|
||||
base_text,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
snapshot.diffs.remove(&buffer_id);
|
||||
}
|
||||
let diff = diff.read(cx);
|
||||
let buffer_id = diff.buffer_id;
|
||||
let diff = diff.snapshot.clone();
|
||||
snapshot.diffs.insert(buffer_id, diff);
|
||||
}
|
||||
|
||||
fn buffer_diff_changed(
|
||||
&mut self,
|
||||
change_set: Entity<BufferChangeSet>,
|
||||
diff: Entity<BufferDiff>,
|
||||
range: Range<text::Anchor>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let change_set = change_set.read(cx);
|
||||
let buffer_id = change_set.buffer_id;
|
||||
let diff = change_set.diff_to_buffer.clone();
|
||||
let base_text = change_set.base_text.clone();
|
||||
self.sync(cx);
|
||||
let mut snapshot = self.snapshot.borrow_mut();
|
||||
let base_text_changed = snapshot
|
||||
.diffs
|
||||
.get(&buffer_id)
|
||||
.map_or(true, |diff_snapshot| {
|
||||
change_set.base_text.as_ref().map_or(true, |base_text| {
|
||||
base_text.remote_id() != diff_snapshot.base_text.remote_id()
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(base_text) = base_text {
|
||||
snapshot.diffs.insert(
|
||||
buffer_id,
|
||||
DiffSnapshot {
|
||||
diff: diff.clone(),
|
||||
base_text,
|
||||
},
|
||||
);
|
||||
} else if self.all_diff_hunks_expanded {
|
||||
let base_text = Buffer::build_empty_snapshot(cx);
|
||||
snapshot.diffs.insert(
|
||||
buffer_id,
|
||||
DiffSnapshot {
|
||||
diff: git::diff::BufferDiff::new_with_single_insertion(&base_text),
|
||||
base_text,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
snapshot.diffs.remove(&buffer_id);
|
||||
let diff = diff.read(cx);
|
||||
let buffer_id = diff.buffer_id;
|
||||
let mut diff = diff.snapshot.clone();
|
||||
if diff.base_text.is_none() && self.all_diff_hunks_expanded {
|
||||
diff = BufferDiffSnapshot::new_with_single_insertion(cx);
|
||||
}
|
||||
|
||||
let mut snapshot = self.snapshot.borrow_mut();
|
||||
let base_text_changed =
|
||||
snapshot
|
||||
.diffs
|
||||
.get(&buffer_id)
|
||||
.map_or(true, |diff_snapshot| {
|
||||
match (&diff_snapshot.base_text, &diff.base_text) {
|
||||
(None, None) => false,
|
||||
(None, Some(_)) => true,
|
||||
(Some(_), None) => true,
|
||||
(Some(old), Some(new)) => {
|
||||
let (old_id, old_empty) = (old.remote_id(), old.is_empty());
|
||||
let (new_id, new_empty) = (new.remote_id(), new.is_empty());
|
||||
new_id != old_id && (!new_empty || !old_empty)
|
||||
}
|
||||
}
|
||||
});
|
||||
snapshot.diffs.insert(buffer_id, diff);
|
||||
|
||||
let buffers = self.buffers.borrow();
|
||||
let Some(buffer_state) = buffers.get(&buffer_id) else {
|
||||
return;
|
||||
|
@ -2352,17 +2319,14 @@ impl MultiBuffer {
|
|||
self.as_singleton().unwrap().read(cx).is_parsing()
|
||||
}
|
||||
|
||||
pub fn add_change_set(&mut self, change_set: Entity<BufferChangeSet>, cx: &mut Context<Self>) {
|
||||
let buffer_id = change_set.read(cx).buffer_id;
|
||||
self.buffer_diff_changed(change_set.clone(), text::Anchor::MIN..text::Anchor::MAX, cx);
|
||||
self.diff_bases
|
||||
.insert(buffer_id, ChangeSetState::new(change_set, cx));
|
||||
pub fn add_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
|
||||
let buffer_id = diff.read(cx).buffer_id;
|
||||
self.buffer_diff_changed(diff.clone(), text::Anchor::MIN..text::Anchor::MAX, cx);
|
||||
self.diffs.insert(buffer_id, DiffState::new(diff, cx));
|
||||
}
|
||||
|
||||
pub fn change_set_for(&self, buffer_id: BufferId) -> Option<Entity<BufferChangeSet>> {
|
||||
self.diff_bases
|
||||
.get(&buffer_id)
|
||||
.map(|state| state.change_set.clone())
|
||||
pub fn diff_for(&self, buffer_id: BufferId) -> Option<Entity<BufferDiff>> {
|
||||
self.diffs.get(&buffer_id).map(|state| state.diff.clone())
|
||||
}
|
||||
|
||||
pub fn expand_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
|
||||
|
@ -2920,9 +2884,11 @@ impl MultiBuffer {
|
|||
while let Some(excerpt) = excerpts.item() {
|
||||
// Recompute the expanded hunks in the portion of the excerpt that
|
||||
// intersects the edit.
|
||||
if let Some(diff_state) = snapshot.diffs.get(&excerpt.buffer_id) {
|
||||
let diff = &diff_state.diff;
|
||||
let base_text = &diff_state.base_text;
|
||||
if let Some((diff, base_text)) = snapshot
|
||||
.diffs
|
||||
.get(&excerpt.buffer_id)
|
||||
.and_then(|diff| Some((diff, diff.base_text.as_ref()?)))
|
||||
{
|
||||
let buffer = &excerpt.buffer;
|
||||
let excerpt_start = *excerpts.start();
|
||||
let excerpt_end = excerpt_start + ExcerptOffset::new(excerpt.text_summary.len);
|
||||
|
@ -3445,8 +3411,7 @@ impl MultiBufferSnapshot {
|
|||
let buffer_start = buffer.anchor_before(buffer_range.start);
|
||||
let buffer_end = buffer.anchor_after(buffer_range.end);
|
||||
Some(
|
||||
diff.diff
|
||||
.hunks_intersecting_range(buffer_start..buffer_end, buffer)
|
||||
diff.hunks_intersecting_range(buffer_start..buffer_end, buffer)
|
||||
.map(|hunk| {
|
||||
(
|
||||
Point::new(hunk.row_range.start, 0)..Point::new(hunk.row_range.end, 0),
|
||||
|
@ -3782,8 +3747,8 @@ impl MultiBufferSnapshot {
|
|||
let buffer_end = excerpt.buffer.anchor_before(buffer_offset);
|
||||
let buffer_end_row = buffer_end.to_point(&excerpt.buffer).row;
|
||||
|
||||
if let Some(diff_state) = self.diffs.get(&excerpt.buffer_id) {
|
||||
for hunk in diff_state.diff.hunks_intersecting_range_rev(
|
||||
if let Some(diff) = self.diffs.get(&excerpt.buffer_id) {
|
||||
for hunk in diff.hunks_intersecting_range_rev(
|
||||
excerpt.range.context.start..buffer_end,
|
||||
&excerpt.buffer,
|
||||
) {
|
||||
|
@ -3851,7 +3816,7 @@ impl MultiBufferSnapshot {
|
|||
}
|
||||
|
||||
pub fn has_diff_hunks(&self) -> bool {
|
||||
self.diffs.values().any(|diff| !diff.diff.is_empty())
|
||||
self.diffs.values().any(|diff| !diff.is_empty())
|
||||
}
|
||||
|
||||
pub fn surrounding_word<T: ToOffset>(
|
||||
|
@ -4313,7 +4278,11 @@ impl MultiBufferSnapshot {
|
|||
} => {
|
||||
let buffer_start = base_text_byte_range.start + start_overshoot;
|
||||
let mut buffer_end = base_text_byte_range.start + end_overshoot;
|
||||
let Some(buffer_diff) = self.diffs.get(buffer_id) else {
|
||||
let Some(base_text) = self
|
||||
.diffs
|
||||
.get(buffer_id)
|
||||
.and_then(|diff| diff.base_text.as_ref())
|
||||
else {
|
||||
panic!("{:?} is in non-existent deleted hunk", range.start)
|
||||
};
|
||||
|
||||
|
@ -4323,9 +4292,8 @@ impl MultiBufferSnapshot {
|
|||
buffer_end -= 1;
|
||||
}
|
||||
|
||||
let mut summary = buffer_diff
|
||||
.base_text
|
||||
.text_summary_for_range::<D, _>(buffer_start..buffer_end);
|
||||
let mut summary =
|
||||
base_text.text_summary_for_range::<D, _>(buffer_start..buffer_end);
|
||||
|
||||
if include_trailing_newline {
|
||||
summary.add_assign(&D::from_text_summary(&TextSummary::newline()))
|
||||
|
@ -4362,12 +4330,15 @@ impl MultiBufferSnapshot {
|
|||
..
|
||||
} => {
|
||||
let buffer_end = base_text_byte_range.start + overshoot;
|
||||
let Some(buffer_diff) = self.diffs.get(buffer_id) else {
|
||||
panic!("{:?} is in non-extant deleted hunk", range.end)
|
||||
let Some(base_text) = self
|
||||
.diffs
|
||||
.get(buffer_id)
|
||||
.and_then(|diff| diff.base_text.as_ref())
|
||||
else {
|
||||
panic!("{:?} is in non-existent deleted hunk", range.end)
|
||||
};
|
||||
|
||||
let mut suffix = buffer_diff
|
||||
.base_text
|
||||
let mut suffix = base_text
|
||||
.text_summary_for_range::<D, _>(base_text_byte_range.start..buffer_end);
|
||||
if *has_trailing_newline && buffer_end == base_text_byte_range.end + 1 {
|
||||
suffix.add_assign(&D::from_text_summary(&TextSummary::newline()))
|
||||
|
@ -4467,14 +4438,18 @@ impl MultiBufferSnapshot {
|
|||
}) => {
|
||||
let mut in_deleted_hunk = false;
|
||||
if let Some(diff_base_anchor) = &anchor.diff_base_anchor {
|
||||
if let Some(diff) = self.diffs.get(buffer_id) {
|
||||
if diff.base_text.can_resolve(&diff_base_anchor) {
|
||||
let base_text_offset = diff_base_anchor.to_offset(&diff.base_text);
|
||||
if let Some(base_text) = self
|
||||
.diffs
|
||||
.get(buffer_id)
|
||||
.and_then(|diff| diff.base_text.as_ref())
|
||||
{
|
||||
if base_text.can_resolve(&diff_base_anchor) {
|
||||
let base_text_offset = diff_base_anchor.to_offset(&base_text);
|
||||
if base_text_offset >= base_text_byte_range.start
|
||||
&& base_text_offset <= base_text_byte_range.end
|
||||
{
|
||||
let position_in_hunk =
|
||||
diff.base_text.text_summary_for_range::<D, _>(
|
||||
let position_in_hunk = base_text
|
||||
.text_summary_for_range::<D, _>(
|
||||
base_text_byte_range.start..base_text_offset,
|
||||
);
|
||||
position.add_assign(&position_in_hunk);
|
||||
|
@ -4800,15 +4775,17 @@ impl MultiBufferSnapshot {
|
|||
..
|
||||
}) = diff_transforms.item()
|
||||
{
|
||||
let diff_base = self.diffs.get(buffer_id).expect("missing diff base");
|
||||
let base_text = self
|
||||
.diffs
|
||||
.get(buffer_id)
|
||||
.and_then(|diff| diff.base_text.as_ref())
|
||||
.expect("missing diff base");
|
||||
if offset_in_transform > base_text_byte_range.len() {
|
||||
debug_assert!(*has_trailing_newline);
|
||||
bias = Bias::Right;
|
||||
} else {
|
||||
diff_base_anchor = Some(
|
||||
diff_base
|
||||
.base_text
|
||||
.anchor_at(base_text_byte_range.start + offset_in_transform, bias),
|
||||
base_text.anchor_at(base_text_byte_range.start + offset_in_transform, bias),
|
||||
);
|
||||
bias = Bias::Left;
|
||||
}
|
||||
|
@ -6144,7 +6121,7 @@ where
|
|||
..
|
||||
} => {
|
||||
let diff = self.diffs.get(&buffer_id)?;
|
||||
let buffer = &diff.base_text;
|
||||
let buffer = diff.base_text.as_ref()?;
|
||||
let mut rope_cursor = buffer.as_rope().cursor(0);
|
||||
let buffer_start = rope_cursor.summary::<D>(base_text_byte_range.start);
|
||||
let buffer_range_len = rope_cursor.summary::<D>(base_text_byte_range.end);
|
||||
|
@ -7186,7 +7163,7 @@ impl<'a> Iterator for MultiBufferChunks<'a> {
|
|||
}
|
||||
chunks
|
||||
} else {
|
||||
let base_buffer = &self.diffs.get(&buffer_id)?.base_text;
|
||||
let base_buffer = &self.diffs.get(&buffer_id)?.base_text.as_ref()?;
|
||||
base_buffer.chunks(base_text_start..base_text_end, self.language_aware)
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::*;
|
||||
use git::diff::DiffHunkStatus;
|
||||
use diff::DiffHunkStatus;
|
||||
use gpui::{App, TestAppContext};
|
||||
use indoc::indoc;
|
||||
use language::{Buffer, Rope};
|
||||
|
@ -361,11 +361,9 @@ fn test_diff_boundary_anchors(cx: &mut TestAppContext) {
|
|||
let base_text = "one\ntwo\nthree\n";
|
||||
let text = "one\nthree\n";
|
||||
let buffer = cx.new(|cx| Buffer::local(text, cx));
|
||||
let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(base_text, &buffer, cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
|
||||
let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.add_change_set(change_set, cx)
|
||||
});
|
||||
multibuffer.update(cx, |multibuffer, cx| multibuffer.add_diff(diff, cx));
|
||||
|
||||
let (before, after) = multibuffer.update(cx, |multibuffer, cx| {
|
||||
let before = multibuffer.snapshot(cx).anchor_before(Point::new(1, 0));
|
||||
|
@ -405,14 +403,14 @@ fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
|
|||
let base_text = "one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\n";
|
||||
let text = "one\nfour\nseven\n";
|
||||
let buffer = cx.new(|cx| Buffer::local(text, cx));
|
||||
let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(base_text, &buffer, cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
|
||||
let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
||||
let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
|
||||
(multibuffer.snapshot(cx), multibuffer.subscribe())
|
||||
});
|
||||
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.add_change_set(change_set, cx);
|
||||
multibuffer.add_diff(diff, cx);
|
||||
multibuffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
|
||||
});
|
||||
|
||||
|
@ -498,11 +496,11 @@ fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
|
|||
let base_text = "one\ntwo\nfour\nfive\nsix\nseven\n";
|
||||
let text = "one\ntwo\nTHREE\nfour\nfive\nseven\n";
|
||||
let buffer = cx.new(|cx| Buffer::local(text, cx));
|
||||
let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(&base_text, &buffer, cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(&base_text, &buffer, cx));
|
||||
let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
|
||||
|
||||
let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.add_change_set(change_set.clone(), cx);
|
||||
multibuffer.add_diff(diff.clone(), cx);
|
||||
(multibuffer.snapshot(cx), multibuffer.subscribe())
|
||||
});
|
||||
|
||||
|
@ -979,10 +977,10 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
|
|||
let buffer = cx.new(|cx| Buffer::local("", cx));
|
||||
let base_text = "a\nb\nc";
|
||||
|
||||
let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(base_text, &buffer, cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.set_all_diff_hunks_expanded(cx);
|
||||
multibuffer.add_change_set(change_set.clone(), cx);
|
||||
multibuffer.add_diff(diff.clone(), cx);
|
||||
multibuffer.push_excerpts(
|
||||
buffer.clone(),
|
||||
[ExcerptRange {
|
||||
|
@ -1018,8 +1016,8 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
|
|||
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit([(0..0, "a\nb\nc")], None, cx);
|
||||
change_set.update(cx, |change_set, cx| {
|
||||
change_set.recalculate_diff_sync(buffer.snapshot().text, cx);
|
||||
diff.update(cx, |diff, cx| {
|
||||
diff.recalculate_diff_sync(buffer.snapshot().text, cx);
|
||||
});
|
||||
assert_eq!(buffer.text(), "a\nb\nc")
|
||||
});
|
||||
|
@ -1030,8 +1028,8 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
|
|||
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.undo(cx);
|
||||
change_set.update(cx, |change_set, cx| {
|
||||
change_set.recalculate_diff_sync(buffer.snapshot().text, cx);
|
||||
diff.update(cx, |diff, cx| {
|
||||
diff.recalculate_diff_sync(buffer.snapshot().text, cx);
|
||||
});
|
||||
assert_eq!(buffer.text(), "")
|
||||
});
|
||||
|
@ -1273,12 +1271,12 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
|
|||
);
|
||||
|
||||
let buffer = cx.new(|cx| Buffer::local(text, cx));
|
||||
let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(base_text, &buffer, cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
let multibuffer = cx.new(|cx| {
|
||||
let mut multibuffer = MultiBuffer::singleton(buffer.clone(), cx);
|
||||
multibuffer.add_change_set(change_set.clone(), cx);
|
||||
multibuffer.add_diff(diff.clone(), cx);
|
||||
multibuffer
|
||||
});
|
||||
|
||||
|
@ -1463,8 +1461,8 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) {
|
|||
assert_line_indents(&snapshot);
|
||||
|
||||
// Recalculate the diff, changing the first diff hunk.
|
||||
change_set.update(cx, |change_set, cx| {
|
||||
change_set.recalculate_diff_sync(buffer.read(cx).text_snapshot(), cx);
|
||||
diff.update(cx, |diff, cx| {
|
||||
diff.recalculate_diff_sync(buffer.read(cx).text_snapshot(), cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
assert_new_snapshot(
|
||||
|
@ -1516,12 +1514,12 @@ fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) {
|
|||
);
|
||||
|
||||
let buffer = cx.new(|cx| Buffer::local(text, cx));
|
||||
let change_set = cx.new(|cx| BufferChangeSet::new_with_base_text(base_text, &buffer, cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
let multibuffer = cx.new(|cx| {
|
||||
let mut multibuffer = MultiBuffer::singleton(buffer.clone(), cx);
|
||||
multibuffer.add_change_set(change_set.clone(), cx);
|
||||
multibuffer.add_diff(diff.clone(), cx);
|
||||
multibuffer
|
||||
});
|
||||
|
||||
|
@ -1918,8 +1916,8 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
|
|||
|
||||
let buffer_1 = cx.new(|cx| Buffer::local(text_1, cx));
|
||||
let buffer_2 = cx.new(|cx| Buffer::local(text_2, cx));
|
||||
let change_set_1 = cx.new(|cx| BufferChangeSet::new_with_base_text(base_text_1, &buffer_1, cx));
|
||||
let change_set_2 = cx.new(|cx| BufferChangeSet::new_with_base_text(base_text_2, &buffer_2, cx));
|
||||
let diff_1 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_1, &buffer_1, cx));
|
||||
let diff_2 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_2, &buffer_2, cx));
|
||||
cx.run_until_parked();
|
||||
|
||||
let multibuffer = cx.new(|cx| {
|
||||
|
@ -1940,8 +1938,8 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
|
|||
}],
|
||||
cx,
|
||||
);
|
||||
multibuffer.add_change_set(change_set_1.clone(), cx);
|
||||
multibuffer.add_change_set(change_set_2.clone(), cx);
|
||||
multibuffer.add_diff(diff_1.clone(), cx);
|
||||
multibuffer.add_diff(diff_2.clone(), cx);
|
||||
multibuffer
|
||||
});
|
||||
|
||||
|
@ -2001,11 +1999,11 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
|
|||
|
||||
let id_1 = buffer_1.read_with(cx, |buffer, _| buffer.remote_id());
|
||||
let id_2 = buffer_2.read_with(cx, |buffer, _| buffer.remote_id());
|
||||
let base_id_1 = change_set_1.read_with(cx, |change_set, _| {
|
||||
change_set.base_text.as_ref().unwrap().remote_id()
|
||||
let base_id_1 = diff_1.read_with(cx, |diff, _| {
|
||||
diff.snapshot.base_text.as_ref().unwrap().remote_id()
|
||||
});
|
||||
let base_id_2 = change_set_2.read_with(cx, |change_set, _| {
|
||||
change_set.base_text.as_ref().unwrap().remote_id()
|
||||
let base_id_2 = diff_2.read_with(cx, |diff, _| {
|
||||
diff.snapshot.base_text.as_ref().unwrap().remote_id()
|
||||
});
|
||||
|
||||
let buffer_lines = (0..=snapshot.max_row().0)
|
||||
|
@ -2101,7 +2099,7 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
|
|||
#[derive(Default)]
|
||||
struct ReferenceMultibuffer {
|
||||
excerpts: Vec<ReferenceExcerpt>,
|
||||
change_sets: HashMap<BufferId, Entity<BufferChangeSet>>,
|
||||
diffs: HashMap<BufferId, Entity<BufferDiff>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -2190,10 +2188,10 @@ impl ReferenceMultibuffer {
|
|||
.unwrap();
|
||||
let buffer = excerpt.buffer.read(cx).snapshot();
|
||||
let buffer_id = buffer.remote_id();
|
||||
let Some(change_set) = self.change_sets.get(&buffer_id) else {
|
||||
let Some(diff) = self.diffs.get(&buffer_id) else {
|
||||
return;
|
||||
};
|
||||
let diff = change_set.read(cx).diff_to_buffer.clone();
|
||||
let diff = diff.read(cx).snapshot.clone();
|
||||
let excerpt_range = excerpt.range.to_offset(&buffer);
|
||||
for hunk in diff.hunks_intersecting_range(range, &buffer) {
|
||||
let hunk_range = hunk.buffer_range.to_offset(&buffer);
|
||||
|
@ -2227,9 +2225,9 @@ impl ReferenceMultibuffer {
|
|||
excerpt_boundary_rows.insert(MultiBufferRow(text.matches('\n').count() as u32));
|
||||
let buffer = excerpt.buffer.read(cx);
|
||||
let buffer_range = excerpt.range.to_offset(buffer);
|
||||
let change_set = self.change_sets.get(&buffer.remote_id()).unwrap().read(cx);
|
||||
let diff = change_set.diff_to_buffer.clone();
|
||||
let base_buffer = change_set.base_text.as_ref().unwrap();
|
||||
let diff = self.diffs.get(&buffer.remote_id()).unwrap().read(cx);
|
||||
let diff = diff.snapshot.clone();
|
||||
let base_buffer = diff.base_text.as_ref().unwrap();
|
||||
|
||||
let mut offset = buffer_range.start;
|
||||
let mut hunks = diff
|
||||
|
@ -2367,12 +2365,7 @@ impl ReferenceMultibuffer {
|
|||
let buffer = excerpt.buffer.read(cx).snapshot();
|
||||
let excerpt_range = excerpt.range.to_offset(&buffer);
|
||||
let buffer_id = buffer.remote_id();
|
||||
let diff = &self
|
||||
.change_sets
|
||||
.get(&buffer_id)
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.diff_to_buffer;
|
||||
let diff = &self.diffs.get(&buffer_id).unwrap().read(cx).snapshot;
|
||||
let mut hunks = diff.hunks_in_row_range(0..u32::MAX, &buffer).peekable();
|
||||
excerpt.expanded_diff_hunks.retain(|hunk_anchor| {
|
||||
if !hunk_anchor.is_valid(&buffer) {
|
||||
|
@ -2396,9 +2389,9 @@ impl ReferenceMultibuffer {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_change_set(&mut self, change_set: Entity<BufferChangeSet>, cx: &mut App) {
|
||||
let buffer_id = change_set.read(cx).buffer_id;
|
||||
self.change_sets.insert(buffer_id, change_set);
|
||||
fn add_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut App) {
|
||||
let buffer_id = diff.read(cx).buffer_id;
|
||||
self.diffs.insert(buffer_id, diff);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2528,16 +2521,16 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
|||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
for buffer in multibuffer.all_buffers() {
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let _ = multibuffer
|
||||
.change_set_for(snapshot.remote_id())
|
||||
.unwrap()
|
||||
.update(cx, |change_set, cx| {
|
||||
let _ = multibuffer.diff_for(snapshot.remote_id()).unwrap().update(
|
||||
cx,
|
||||
|diff, cx| {
|
||||
log::info!(
|
||||
"recalculating diff for buffer {:?}",
|
||||
snapshot.remote_id(),
|
||||
);
|
||||
change_set.recalculate_diff_sync(snapshot.text, cx);
|
||||
});
|
||||
diff.recalculate_diff_sync(snapshot.text, cx);
|
||||
},
|
||||
);
|
||||
}
|
||||
reference.diffs_updated(cx);
|
||||
needs_diff_calculation = false;
|
||||
|
@ -2550,12 +2543,11 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
|||
.collect::<String>();
|
||||
|
||||
let buffer = cx.new(|cx| Buffer::local(base_text.clone(), cx));
|
||||
let change_set =
|
||||
cx.new(|cx| BufferChangeSet::new_with_base_text(&base_text, &buffer, cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(&base_text, &buffer, cx));
|
||||
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
reference.add_change_set(change_set.clone(), cx);
|
||||
multibuffer.add_change_set(change_set, cx)
|
||||
reference.add_diff(diff.clone(), cx);
|
||||
multibuffer.add_diff(diff, cx)
|
||||
});
|
||||
buffers.push(buffer);
|
||||
buffers.last().unwrap()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue