Fix two issues with diff highlights (#24309)
* fix syntax highlighting of deleted text when buffer language changes * do not highlight entire untracked files as created, except in the project diff view Release Notes: - N/A Co-authored-by: ConradIrwin <conrad.irwin@gmail.com> Co-authored-by: cole-miller <m@cole-miller.net>
This commit is contained in:
parent
9114ca973c
commit
ca01a8b9cb
5 changed files with 126 additions and 52 deletions
|
@ -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 {
|
pub fn build(diff_base: Option<&str>, buffer: &text::BufferSnapshot) -> Self {
|
||||||
let mut tree = SumTree::new(buffer);
|
let mut tree = SumTree::new(buffer);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use gpui::{
|
||||||
actions, AnyElement, AnyView, App, AppContext, AsyncWindowContext, Entity, EventEmitter,
|
actions, AnyElement, AnyView, App, AppContext, AsyncWindowContext, Entity, EventEmitter,
|
||||||
FocusHandle, Focusable, Render, Subscription, Task, WeakEntity,
|
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 multi_buffer::{MultiBuffer, PathKey};
|
||||||
use project::{buffer_store::BufferChangeSet, git::GitState, Project, ProjectPath};
|
use project::{buffer_store::BufferChangeSet, git::GitState, Project, ProjectPath};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
@ -293,11 +293,15 @@ impl ProjectDiff {
|
||||||
let change_set = diff_buffer.change_set;
|
let change_set = diff_buffer.change_set;
|
||||||
|
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
let diff_hunk_ranges = change_set
|
let change_set = change_set.read(cx);
|
||||||
.read(cx)
|
let diff_hunk_ranges = if change_set.base_text.is_none() {
|
||||||
.diff_hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot)
|
vec![Point::zero()..snapshot.max_point()]
|
||||||
.map(|diff_hunk| diff_hunk.buffer_range.to_point(&snapshot))
|
} else {
|
||||||
.collect::<Vec<_>>();
|
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| {
|
self.multibuffer.update(cx, |multibuffer, cx| {
|
||||||
multibuffer.set_excerpts_for_path(
|
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"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub fn build_snapshot_sync(
|
pub fn build_snapshot_sync(
|
||||||
text: Rope,
|
text: Rope,
|
||||||
|
|
|
@ -220,6 +220,22 @@ struct ChangeSetState {
|
||||||
_subscription: gpui::Subscription,
|
_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.
|
/// The contents of a [`MultiBuffer`] at a single point in time.
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct MultiBufferSnapshot {
|
pub struct MultiBufferSnapshot {
|
||||||
|
@ -560,17 +576,7 @@ impl MultiBuffer {
|
||||||
for (buffer_id, change_set_state) in self.diff_bases.iter() {
|
for (buffer_id, change_set_state) in self.diff_bases.iter() {
|
||||||
diff_bases.insert(
|
diff_bases.insert(
|
||||||
*buffer_id,
|
*buffer_id,
|
||||||
ChangeSetState {
|
ChangeSetState::new(change_set_state.change_set.clone(), new_cx),
|
||||||
_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(),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Self {
|
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(
|
fn buffer_diff_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
change_set: Entity<BufferChangeSet>,
|
change_set: Entity<BufferChangeSet>,
|
||||||
|
@ -2175,6 +2205,15 @@ impl MultiBuffer {
|
||||||
base_text,
|
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 {
|
} else {
|
||||||
snapshot.diffs.remove(&buffer_id);
|
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>) {
|
pub fn add_change_set(&mut self, change_set: Entity<BufferChangeSet>, cx: &mut Context<Self>) {
|
||||||
let buffer_id = change_set.read(cx).buffer_id;
|
let buffer_id = change_set.read(cx).buffer_id;
|
||||||
self.buffer_diff_changed(change_set.clone(), text::Anchor::MIN..text::Anchor::MAX, cx);
|
self.buffer_diff_changed(change_set.clone(), text::Anchor::MIN..text::Anchor::MAX, cx);
|
||||||
self.diff_bases.insert(
|
self.diff_bases
|
||||||
buffer_id,
|
.insert(buffer_id, ChangeSetState::new(change_set, cx));
|
||||||
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,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_set_for(&self, buffer_id: BufferId) -> Option<Entity<BufferChangeSet>> {
|
pub fn change_set_for(&self, buffer_id: BufferId) -> Option<Entity<BufferChangeSet>> {
|
||||||
|
|
|
@ -85,6 +85,7 @@ struct BufferChangeSetState {
|
||||||
index_text: Option<Arc<String>>,
|
index_text: Option<Arc<String>>,
|
||||||
head_changed: bool,
|
head_changed: bool,
|
||||||
index_changed: bool,
|
index_changed: bool,
|
||||||
|
language_changed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -101,8 +102,7 @@ enum DiffBasesChange {
|
||||||
impl BufferChangeSetState {
|
impl BufferChangeSetState {
|
||||||
fn buffer_language_changed(&mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>) {
|
fn buffer_language_changed(&mut self, buffer: Entity<Buffer>, cx: &mut Context<Self>) {
|
||||||
self.language = buffer.read(cx).language().cloned();
|
self.language = buffer.read(cx).language().cloned();
|
||||||
self.index_changed = self.index_text.is_some();
|
self.language_changed = true;
|
||||||
self.head_changed = self.head_text.is_some();
|
|
||||||
let _ = self.recalculate_diffs(buffer.read(cx).text_snapshot(), cx);
|
let _ = self.recalculate_diffs(buffer.read(cx).text_snapshot(), cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,34 +149,40 @@ impl BufferChangeSetState {
|
||||||
) -> oneshot::Receiver<()> {
|
) -> oneshot::Receiver<()> {
|
||||||
match diff_bases_change {
|
match diff_bases_change {
|
||||||
DiffBasesChange::SetIndex(index) => {
|
DiffBasesChange::SetIndex(index) => {
|
||||||
let mut index = index.unwrap_or_default();
|
self.index_text = index.map(|mut index| {
|
||||||
text::LineEnding::normalize(&mut index);
|
text::LineEnding::normalize(&mut index);
|
||||||
self.index_text = Some(Arc::new(index));
|
Arc::new(index)
|
||||||
|
});
|
||||||
self.index_changed = true;
|
self.index_changed = true;
|
||||||
}
|
}
|
||||||
DiffBasesChange::SetHead(head) => {
|
DiffBasesChange::SetHead(head) => {
|
||||||
let mut head = head.unwrap_or_default();
|
self.head_text = head.map(|mut head| {
|
||||||
text::LineEnding::normalize(&mut head);
|
text::LineEnding::normalize(&mut head);
|
||||||
self.head_text = Some(Arc::new(head));
|
Arc::new(head)
|
||||||
|
});
|
||||||
self.head_changed = true;
|
self.head_changed = true;
|
||||||
}
|
}
|
||||||
DiffBasesChange::SetBoth(text) => {
|
DiffBasesChange::SetBoth(text) => {
|
||||||
let mut text = text.unwrap_or_default();
|
let text = text.map(|mut text| {
|
||||||
text::LineEnding::normalize(&mut text);
|
text::LineEnding::normalize(&mut text);
|
||||||
self.head_text = Some(Arc::new(text));
|
Arc::new(text)
|
||||||
self.index_text = self.head_text.clone();
|
});
|
||||||
|
self.head_text = text.clone();
|
||||||
|
self.index_text = text;
|
||||||
self.head_changed = true;
|
self.head_changed = true;
|
||||||
self.index_changed = true;
|
self.index_changed = true;
|
||||||
}
|
}
|
||||||
DiffBasesChange::SetEach { index, head } => {
|
DiffBasesChange::SetEach { index, head } => {
|
||||||
let mut index = index.unwrap_or_default();
|
self.index_text = index.map(|mut index| {
|
||||||
text::LineEnding::normalize(&mut index);
|
text::LineEnding::normalize(&mut index);
|
||||||
let mut head = head.unwrap_or_default();
|
Arc::new(index)
|
||||||
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_changed = true;
|
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 = self.index_text.clone();
|
||||||
let index_changed = self.index_changed;
|
let index_changed = self.index_changed;
|
||||||
let head_changed = self.head_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()) {
|
let index_matches_head = match (self.index_text.as_ref(), self.head_text.as_ref()) {
|
||||||
(Some(index), Some(head)) => Arc::ptr_eq(index, head),
|
(Some(index), Some(head)) => Arc::ptr_eq(index, head),
|
||||||
(None, None) => true,
|
(None, None) => true,
|
||||||
|
@ -206,7 +213,7 @@ impl BufferChangeSetState {
|
||||||
};
|
};
|
||||||
self.recalculate_diff_task = Some(cx.spawn(|this, mut cx| async move {
|
self.recalculate_diff_task = Some(cx.spawn(|this, mut cx| async move {
|
||||||
if let Some(unstaged_changes) = &unstaged_changes {
|
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| {
|
let staged_snapshot = cx.update(|cx| {
|
||||||
index.as_ref().map(|head| {
|
index.as_ref().map(|head| {
|
||||||
language::Buffer::build_snapshot(
|
language::Buffer::build_snapshot(
|
||||||
|
@ -238,6 +245,9 @@ impl BufferChangeSetState {
|
||||||
|
|
||||||
unstaged_changes.update(&mut cx, |unstaged_changes, cx| {
|
unstaged_changes.update(&mut cx, |unstaged_changes, cx| {
|
||||||
unstaged_changes.set_state(staged_snapshot.clone(), diff, &buffer, 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 {
|
} else {
|
||||||
let committed_snapshot = if head_changed {
|
let committed_snapshot = if head_changed || language_changed {
|
||||||
let committed_snapshot = cx.update(|cx| {
|
let committed_snapshot = cx.update(|cx| {
|
||||||
head.as_ref().map(|head| {
|
head.as_ref().map(|head| {
|
||||||
language::Buffer::build_snapshot(
|
language::Buffer::build_snapshot(
|
||||||
|
@ -284,6 +294,9 @@ impl BufferChangeSetState {
|
||||||
|
|
||||||
uncommitted_changes.update(&mut cx, |change_set, cx| {
|
uncommitted_changes.update(&mut cx, |change_set, cx| {
|
||||||
change_set.set_state(snapshot, diff, &buffer, 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 {
|
pub enum BufferChangeSetEvent {
|
||||||
DiffChanged { changed_range: Range<text::Anchor> },
|
DiffChanged { changed_range: Range<text::Anchor> },
|
||||||
|
LanguageChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum BufferStoreState {
|
enum BufferStoreState {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue