Detailed changes
@@ -74,6 +74,18 @@ impl BufferDiff {
}
}
+ pub fn new_with_single_insertion(buffer: &BufferSnapshot) -> Self {
+ Self {
+ tree: SumTree::from_item(
+ InternalDiffHunk {
+ buffer_range: Anchor::MIN..Anchor::MAX,
+ diff_base_byte_range: 0..0,
+ },
+ buffer,
+ ),
+ }
+ }
+
pub fn build(diff_base: Option<&str>, buffer: &text::BufferSnapshot) -> Self {
let mut tree = SumTree::new(buffer);
@@ -9,7 +9,7 @@ use gpui::{
actions, AnyElement, AnyView, App, AppContext, AsyncWindowContext, Entity, EventEmitter,
FocusHandle, Focusable, Render, Subscription, Task, WeakEntity,
};
-use language::{Anchor, Buffer, Capability, OffsetRangeExt};
+use language::{Anchor, Buffer, Capability, OffsetRangeExt, Point};
use multi_buffer::{MultiBuffer, PathKey};
use project::{buffer_store::BufferChangeSet, git::GitState, Project, ProjectPath};
use theme::ActiveTheme;
@@ -293,11 +293,15 @@ impl ProjectDiff {
let change_set = diff_buffer.change_set;
let snapshot = buffer.read(cx).snapshot();
- let diff_hunk_ranges = change_set
- .read(cx)
- .diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot)
- .map(|diff_hunk| diff_hunk.buffer_range.to_point(&snapshot))
- .collect::<Vec<_>>();
+ let change_set = change_set.read(cx);
+ let diff_hunk_ranges = if change_set.base_text.is_none() {
+ vec![Point::zero()..snapshot.max_point()]
+ } else {
+ change_set
+ .diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot)
+ .map(|diff_hunk| diff_hunk.buffer_range.to_point(&snapshot))
+ .collect::<Vec<_>>()
+ };
self.multibuffer.update(cx, |multibuffer, cx| {
multibuffer.set_excerpts_for_path(
@@ -1001,6 +1001,23 @@ impl Buffer {
}
}
+ pub fn build_empty_snapshot(cx: &mut App) -> BufferSnapshot {
+ let entity_id = cx.reserve_entity::<Self>().entity_id();
+ let buffer_id = entity_id.as_non_zero_u64().into();
+ let text =
+ TextBuffer::new_normalized(0, buffer_id, Default::default(), Rope::new()).snapshot();
+ let syntax = SyntaxMap::new(&text).snapshot();
+ BufferSnapshot {
+ text,
+ syntax,
+ file: None,
+ diagnostics: Default::default(),
+ remote_selections: Default::default(),
+ language: None,
+ non_text_state_update_count: 0,
+ }
+ }
+
#[cfg(any(test, feature = "test-support"))]
pub fn build_snapshot_sync(
text: Rope,
@@ -220,6 +220,22 @@ struct ChangeSetState {
_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)
+ }
+ }),
+ change_set,
+ }
+ }
+}
+
/// The contents of a [`MultiBuffer`] at a single point in time.
#[derive(Clone, Default)]
pub struct MultiBufferSnapshot {
@@ -560,17 +576,7 @@ impl MultiBuffer {
for (buffer_id, change_set_state) in self.diff_bases.iter() {
diff_bases.insert(
*buffer_id,
- ChangeSetState {
- _subscription: new_cx.subscribe(
- &change_set_state.change_set,
- |this, change_set, event, cx| match event {
- BufferChangeSetEvent::DiffChanged { changed_range } => {
- this.buffer_diff_changed(change_set, changed_range.clone(), cx)
- }
- },
- ),
- change_set: change_set_state.change_set.clone(),
- },
+ ChangeSetState::new(change_set_state.change_set.clone(), new_cx),
);
}
Self {
@@ -2146,6 +2152,30 @@ impl MultiBuffer {
});
}
+ fn buffer_diff_language_changed(
+ &mut self,
+ change_set: Entity<BufferChangeSet>,
+ 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);
+ }
+ }
+
fn buffer_diff_changed(
&mut self,
change_set: Entity<BufferChangeSet>,
@@ -2175,6 +2205,15 @@ impl MultiBuffer {
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);
}
@@ -2316,20 +2355,8 @@ impl MultiBuffer {
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 {
- _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);
- }
- },
- ),
- change_set,
- },
- );
+ self.diff_bases
+ .insert(buffer_id, ChangeSetState::new(change_set, cx));
}
pub fn change_set_for(&self, buffer_id: BufferId) -> Option<Entity<BufferChangeSet>> {
@@ -85,6 +85,7 @@ struct BufferChangeSetState {
index_text: Option<Arc<String>>,
head_changed: bool,
index_changed: bool,
+ language_changed: bool,
}
#[derive(Clone, Debug)]
@@ -101,8 +102,7 @@ enum DiffBasesChange {
impl BufferChangeSetState {
fn buffer_language_changed(&mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>) {
self.language = buffer.read(cx).language().cloned();
- self.index_changed = self.index_text.is_some();
- self.head_changed = self.head_text.is_some();
+ self.language_changed = true;
let _ = self.recalculate_diffs(buffer.read(cx).text_snapshot(), cx);
}
@@ -149,34 +149,40 @@ impl BufferChangeSetState {
) -> oneshot::Receiver<()> {
match diff_bases_change {
DiffBasesChange::SetIndex(index) => {
- let mut index = index.unwrap_or_default();
- text::LineEnding::normalize(&mut index);
- self.index_text = Some(Arc::new(index));
+ self.index_text = index.map(|mut index| {
+ text::LineEnding::normalize(&mut index);
+ Arc::new(index)
+ });
self.index_changed = true;
}
DiffBasesChange::SetHead(head) => {
- let mut head = head.unwrap_or_default();
- text::LineEnding::normalize(&mut head);
- self.head_text = Some(Arc::new(head));
+ self.head_text = head.map(|mut head| {
+ text::LineEnding::normalize(&mut head);
+ Arc::new(head)
+ });
self.head_changed = true;
}
DiffBasesChange::SetBoth(text) => {
- let mut text = text.unwrap_or_default();
- text::LineEnding::normalize(&mut text);
- self.head_text = Some(Arc::new(text));
- self.index_text = self.head_text.clone();
+ let text = text.map(|mut text| {
+ text::LineEnding::normalize(&mut text);
+ Arc::new(text)
+ });
+ self.head_text = text.clone();
+ self.index_text = text;
self.head_changed = true;
self.index_changed = true;
}
DiffBasesChange::SetEach { index, head } => {
- let mut index = index.unwrap_or_default();
- text::LineEnding::normalize(&mut index);
- let mut head = head.unwrap_or_default();
- text::LineEnding::normalize(&mut head);
- self.index_text = Some(Arc::new(index));
- self.head_text = Some(Arc::new(head));
- self.head_changed = true;
+ self.index_text = index.map(|mut index| {
+ text::LineEnding::normalize(&mut index);
+ Arc::new(index)
+ });
self.index_changed = true;
+ self.head_text = head.map(|mut head| {
+ text::LineEnding::normalize(&mut head);
+ Arc::new(head)
+ });
+ self.head_changed = true;
}
}
@@ -199,6 +205,7 @@ impl BufferChangeSetState {
let index = self.index_text.clone();
let index_changed = self.index_changed;
let head_changed = self.head_changed;
+ let language_changed = self.language_changed;
let index_matches_head = match (self.index_text.as_ref(), self.head_text.as_ref()) {
(Some(index), Some(head)) => Arc::ptr_eq(index, head),
(None, None) => true,
@@ -206,7 +213,7 @@ impl BufferChangeSetState {
};
self.recalculate_diff_task = Some(cx.spawn(|this, mut cx| async move {
if let Some(unstaged_changes) = &unstaged_changes {
- let staged_snapshot = if index_changed {
+ let staged_snapshot = if index_changed || language_changed {
let staged_snapshot = cx.update(|cx| {
index.as_ref().map(|head| {
language::Buffer::build_snapshot(
@@ -238,6 +245,9 @@ impl BufferChangeSetState {
unstaged_changes.update(&mut cx, |unstaged_changes, cx| {
unstaged_changes.set_state(staged_snapshot.clone(), diff, &buffer, cx);
+ if language_changed {
+ cx.emit(BufferChangeSetEvent::LanguageChanged);
+ }
})?;
}
@@ -252,7 +262,7 @@ impl BufferChangeSetState {
)
})?
} else {
- let committed_snapshot = if head_changed {
+ let committed_snapshot = if head_changed || language_changed {
let committed_snapshot = cx.update(|cx| {
head.as_ref().map(|head| {
language::Buffer::build_snapshot(
@@ -284,6 +294,9 @@ impl BufferChangeSetState {
uncommitted_changes.update(&mut cx, |change_set, cx| {
change_set.set_state(snapshot, diff, &buffer, cx);
+ if language_changed {
+ cx.emit(BufferChangeSetEvent::LanguageChanged);
+ }
})?;
}
@@ -323,6 +336,7 @@ impl std::fmt::Debug for BufferChangeSet {
pub enum BufferChangeSetEvent {
DiffChanged { changed_range: Range<text::Anchor> },
+ LanguageChanged,
}
enum BufferStoreState {