Detailed changes
@@ -3,7 +3,7 @@ use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as
use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task};
use language::{
Capability, Diff, DiffOptions, File, Language, LanguageName, LanguageRegistry,
- language_settings::language_settings, word_diff_ranges,
+ language_settings::language_settings, text_diff, word_diff_ranges,
};
use rope::Rope;
use std::{
@@ -108,12 +108,17 @@ pub struct DiffHunk {
pub buffer_word_diffs: Vec<Range<Anchor>>,
// Offsets relative to the start of the deleted diff that represent word diff locations
pub base_word_diffs: Vec<Range<usize>>,
+ // These fields are nonempty only if the secondary status is OverlapsWithSecondaryHunk
+ pub buffer_staged_lines: Vec<Range<Anchor>>,
+ pub base_staged_lines: Vec<Range<Point>>,
}
/// We store [`InternalDiffHunk`]s internally so we don't need to store the additional row range.
#[derive(Debug, Clone, PartialEq, Eq)]
struct InternalDiffHunk {
+ // Range of text that has been added to the main buffer
buffer_range: Range<Anchor>,
+ // Range of text that has been deleted from the diff base
diff_base_byte_range: Range<usize>,
base_word_diffs: Vec<Range<usize>>,
buffer_word_diffs: Vec<Range<Anchor>>,
@@ -820,6 +825,9 @@ impl BufferDiffInner<language::BufferSnapshot> {
let base_word_diffs = hunk.base_word_diffs.clone();
let buffer_word_diffs = hunk.buffer_word_diffs.clone();
+ let mut buffer_staged_lines = Vec::new();
+ let mut base_staged_lines = Vec::new();
+
if !start_anchor.is_valid(buffer) {
continue;
}
@@ -879,6 +887,17 @@ impl BufferDiffInner<language::BufferSnapshot> {
} else if secondary_range == (start_point..end_point) {
secondary_status = DiffHunkSecondaryStatus::HasSecondaryHunk;
} else if secondary_range.start <= end_point {
+ // FIXME this should be a background computation that only happens when either the diff or the secondary diff changes
+ let (buffer, base) = compute_staged_lines(
+ &hunk,
+ &secondary_hunk,
+ buffer,
+ &self.base_text,
+ &secondary.unwrap().base_text,
+ );
+ buffer_staged_lines = buffer;
+ base_staged_lines = base;
+
secondary_status = DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk;
}
}
@@ -891,6 +910,8 @@ impl BufferDiffInner<language::BufferSnapshot> {
base_word_diffs,
buffer_word_diffs,
secondary_status,
+ buffer_staged_lines,
+ base_staged_lines,
});
}
})
@@ -917,11 +938,66 @@ impl BufferDiffInner<language::BufferSnapshot> {
secondary_status: DiffHunkSecondaryStatus::NoSecondaryHunk,
base_word_diffs: hunk.base_word_diffs.clone(),
buffer_word_diffs: hunk.buffer_word_diffs.clone(),
+ base_staged_lines: Vec::new(),
+ buffer_staged_lines: Vec::new(),
})
})
}
}
+fn compute_staged_lines(
+ hunk: &InternalDiffHunk,
+ secondary_hunk: &InternalDiffHunk,
+ buffer: &text::BufferSnapshot,
+ base_text: &language::BufferSnapshot,
+ secondary_base_text: &language::BufferSnapshot,
+) -> (Vec<Range<Anchor>>, Vec<Range<Point>>) {
+ let primary_hunk_buffer_text = buffer
+ .text_for_range(hunk.buffer_range.clone())
+ .collect::<String>();
+ let secondary_hunk_buffer_text = buffer
+ .text_for_range(secondary_hunk.buffer_range.clone())
+ .collect::<String>();
+ let primary_hunk_base_text = base_text
+ .text_for_range(hunk.diff_base_byte_range.clone())
+ .collect::<String>();
+ let secondary_hunk_base_text = secondary_base_text
+ .text_for_range(secondary_hunk.diff_base_byte_range.clone())
+ .collect::<String>();
+
+ let primary_hunk_start_in_buffer = hunk.buffer_range.start.to_offset(buffer);
+ // No word diffs
+ let buffer_staged_lines = language::text_diff_with_options_internal(
+ dbg!(&secondary_hunk_buffer_text),
+ dbg!(&primary_hunk_buffer_text),
+ DiffOptions {
+ language_scope: None,
+ max_word_diff_len: 0,
+ max_word_diff_line_count: 0,
+ },
+ )
+ .into_iter()
+ .map(|edit| {
+ buffer.anchor_before(edit.new.start + primary_hunk_start_in_buffer)
+ ..buffer.anchor_after(edit.new.end + primary_hunk_start_in_buffer)
+ })
+ .collect::<Vec<_>>();
+
+ // FIXME
+ if !buffer_staged_lines.is_empty() {
+ eprintln!(
+ "staged lines: {:?}",
+ buffer_staged_lines
+ .iter()
+ .map(|range| buffer.text_for_range(range.clone()).collect::<String>())
+ .collect::<Vec<_>>()
+ );
+ }
+
+ // FIXME
+ (buffer_staged_lines, Vec::new())
+}
+
fn build_diff_options(
file: Option<&Arc<dyn File>>,
language: Option<LanguageName>,
@@ -20622,6 +20622,8 @@ impl Editor {
..hunk.diff_base_byte_range.end.0,
secondary_status: hunk.status.secondary,
range: Point::zero()..Point::zero(), // unused
+ buffer_staged_lines: Vec::new(), // unused
+ base_staged_lines: Vec::new(), // unused
})
.collect::<Vec<_>>(),
&buffer_snapshot,
@@ -9682,6 +9682,20 @@ impl Element for EditorElement {
.row_infos(start_row)
.take((start_row..end_row).len())
.collect::<Vec<RowInfo>>();
+
+ // FIXME
+ let all_staged_lines = snapshot
+ .buffer_snapshot()
+ .diff_hunks_in_range(Anchor::min()..Anchor::max())
+ .flat_map(|hunk| hunk.staged_rows)
+ .map(|row| {
+ snapshot
+ .display_snapshot
+ .point_to_display_point(Point::new(row.0, 0), Bias::Left)
+ .row()
+ })
+ .collect::<Vec<_>>();
+
let is_row_soft_wrapped = |row: usize| {
row_infos
.get(row)
@@ -9761,15 +9775,17 @@ impl Element for EditorElement {
type_id: None,
};
- let background = if Self::diff_hunk_hollow(diff_status, cx) {
+ let base_display_point =
+ DisplayPoint::new(start_row + DisplayRow(ix as u32), 0);
+
+ let background = if Self::diff_hunk_hollow(diff_status, cx)
+ || all_staged_lines.contains(&base_display_point.row())
+ {
hollow_highlight
} else {
filled_highlight
};
- let base_display_point =
- DisplayPoint::new(start_row + DisplayRow(ix as u32), 0);
-
highlighted_rows
.entry(base_display_point.row())
.or_insert(background);
@@ -67,8 +67,8 @@ use task::RunnableTag;
pub use task_context::{ContextLocation, ContextProvider, RunnableRange};
pub use text_diff::{
DiffOptions, apply_diff_patch, apply_reversed_diff_patch, line_diff, text_diff,
- text_diff_with_options, unified_diff, unified_diff_with_context, unified_diff_with_offsets,
- word_diff_ranges,
+ text_diff_with_options, text_diff_with_options_internal, unified_diff,
+ unified_diff_with_context, unified_diff_with_offsets, word_diff_ranges,
};
use theme::SyntaxTheme;
pub use toolchain::{
@@ -237,12 +237,11 @@ impl Default for DiffOptions {
/// Computes a diff between two strings, using a specific language scope's
/// word characters for word-level diffing.
-pub fn text_diff_with_options(
+pub fn text_diff_with_options_internal(
old_text: &str,
new_text: &str,
options: DiffOptions,
-) -> Vec<(Range<usize>, Arc<str>)> {
- let empty: Arc<str> = Arc::default();
+) -> Vec<text::Edit<usize>> {
let mut edits = Vec::new();
let mut hunk_input = InternedInput::default();
let input = InternedInput::new(
@@ -275,26 +274,33 @@ pub fn text_diff_with_options(
old_offset + old_byte_range.start..old_offset + old_byte_range.end;
let new_byte_range =
new_offset + new_byte_range.start..new_offset + new_byte_range.end;
- let replacement_text = if new_byte_range.is_empty() {
- empty.clone()
- } else {
- new_text[new_byte_range].into()
- };
- edits.push((old_byte_range, replacement_text));
+ edits.push(text::Edit {
+ old: old_byte_range,
+ new: new_byte_range,
+ });
});
} else {
- let replacement_text = if new_byte_range.is_empty() {
- empty.clone()
- } else {
- new_text[new_byte_range].into()
- };
- edits.push((old_byte_range, replacement_text));
+ edits.push(text::Edit {
+ old: old_byte_range,
+ new: new_byte_range,
+ });
}
},
);
edits
}
+pub fn text_diff_with_options(
+ old_text: &str,
+ new_text: &str,
+ options: DiffOptions,
+) -> Vec<(Range<usize>, Arc<str>)> {
+ text_diff_with_options_internal(old_text, new_text, options)
+ .into_iter()
+ .map(|edit| (edit.old, new_text[edit.new].into()))
+ .collect()
+}
+
pub fn apply_diff_patch(base_text: &str, patch: &str) -> Result<String, anyhow::Error> {
let patch = diffy::Patch::from_str(patch).context("Failed to parse patch")?;
let result = diffy::apply(base_text, &patch);
@@ -150,6 +150,7 @@ pub struct MultiBufferDiffHunk {
pub status: DiffHunkStatus,
/// The word diffs for this hunk.
pub word_diffs: Vec<Range<MultiBufferOffset>>,
+ pub staged_rows: Vec<MultiBufferRow>,
}
impl MultiBufferDiffHunk {
@@ -3933,6 +3934,18 @@ impl MultiBufferSnapshot {
})
.unwrap_or_default();
+ let staged_rows = (!hunk.buffer_staged_lines.is_empty())
+ .then(|| {
+ let mut rows = Vec::new();
+ for range in &hunk.buffer_staged_lines {
+ let range =
+ Anchor::range_in_buffer(excerpt.id, range.clone()).to_point(self);
+ rows.extend((range.start.row..range.end.row).map(MultiBufferRow));
+ }
+ rows
+ })
+ .unwrap_or_default();
+
let buffer_range = if is_inverted {
excerpt.buffer.anchor_after(hunk.diff_base_byte_range.start)
..excerpt.buffer.anchor_before(hunk.diff_base_byte_range.end)
@@ -3958,6 +3971,7 @@ impl MultiBufferSnapshot {
kind: status_kind,
secondary: hunk.secondary_status,
},
+ staged_rows,
})
})
}