Merge pull request #1800 from zed-industries/go-to-diff-hunk
Add action to go to next/previous git diff in editor
This commit is contained in:
commit
259a758849
13 changed files with 374 additions and 129 deletions
|
@ -402,7 +402,9 @@
|
||||||
{
|
{
|
||||||
"context": "Editor",
|
"context": "Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"alt-enter": "editor::OpenExcerpts"
|
"alt-enter": "editor::OpenExcerpts",
|
||||||
|
"cmd-f8": "editor::GoToHunk",
|
||||||
|
"cmd-shift-f8": "editor::GoToPrevHunk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1483,7 +1483,7 @@ async fn test_git_diff_base_change(
|
||||||
buffer_local_a.read_with(cx_a, |buffer, _| {
|
buffer_local_a.read_with(cx_a, |buffer, _| {
|
||||||
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
||||||
git::diff::assert_hunks(
|
git::diff::assert_hunks(
|
||||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
buffer.snapshot().git_diff_hunks_in_range(0..4, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[(1..2, "", "two\n")],
|
&[(1..2, "", "two\n")],
|
||||||
|
@ -1503,7 +1503,7 @@ async fn test_git_diff_base_change(
|
||||||
buffer_remote_a.read_with(cx_b, |buffer, _| {
|
buffer_remote_a.read_with(cx_b, |buffer, _| {
|
||||||
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
||||||
git::diff::assert_hunks(
|
git::diff::assert_hunks(
|
||||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
buffer.snapshot().git_diff_hunks_in_range(0..4, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[(1..2, "", "two\n")],
|
&[(1..2, "", "two\n")],
|
||||||
|
@ -1527,7 +1527,7 @@ async fn test_git_diff_base_change(
|
||||||
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
||||||
|
|
||||||
git::diff::assert_hunks(
|
git::diff::assert_hunks(
|
||||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
buffer.snapshot().git_diff_hunks_in_range(0..4, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[(2..3, "", "three\n")],
|
&[(2..3, "", "three\n")],
|
||||||
|
@ -1538,7 +1538,7 @@ async fn test_git_diff_base_change(
|
||||||
buffer_remote_a.read_with(cx_b, |buffer, _| {
|
buffer_remote_a.read_with(cx_b, |buffer, _| {
|
||||||
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
||||||
git::diff::assert_hunks(
|
git::diff::assert_hunks(
|
||||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
buffer.snapshot().git_diff_hunks_in_range(0..4, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[(2..3, "", "three\n")],
|
&[(2..3, "", "three\n")],
|
||||||
|
@ -1581,7 +1581,7 @@ async fn test_git_diff_base_change(
|
||||||
buffer_local_b.read_with(cx_a, |buffer, _| {
|
buffer_local_b.read_with(cx_a, |buffer, _| {
|
||||||
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
||||||
git::diff::assert_hunks(
|
git::diff::assert_hunks(
|
||||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
buffer.snapshot().git_diff_hunks_in_range(0..4, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[(1..2, "", "two\n")],
|
&[(1..2, "", "two\n")],
|
||||||
|
@ -1601,7 +1601,7 @@ async fn test_git_diff_base_change(
|
||||||
buffer_remote_b.read_with(cx_b, |buffer, _| {
|
buffer_remote_b.read_with(cx_b, |buffer, _| {
|
||||||
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
assert_eq!(buffer.diff_base(), Some(diff_base.as_ref()));
|
||||||
git::diff::assert_hunks(
|
git::diff::assert_hunks(
|
||||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
buffer.snapshot().git_diff_hunks_in_range(0..4, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[(1..2, "", "two\n")],
|
&[(1..2, "", "two\n")],
|
||||||
|
@ -1629,12 +1629,12 @@ async fn test_git_diff_base_change(
|
||||||
"{:?}",
|
"{:?}",
|
||||||
buffer
|
buffer
|
||||||
.snapshot()
|
.snapshot()
|
||||||
.git_diff_hunks_in_range(0..4)
|
.git_diff_hunks_in_range(0..4, false)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
|
|
||||||
git::diff::assert_hunks(
|
git::diff::assert_hunks(
|
||||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
buffer.snapshot().git_diff_hunks_in_range(0..4, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[(2..3, "", "three\n")],
|
&[(2..3, "", "three\n")],
|
||||||
|
@ -1645,7 +1645,7 @@ async fn test_git_diff_base_change(
|
||||||
buffer_remote_b.read_with(cx_b, |buffer, _| {
|
buffer_remote_b.read_with(cx_b, |buffer, _| {
|
||||||
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref()));
|
||||||
git::diff::assert_hunks(
|
git::diff::assert_hunks(
|
||||||
buffer.snapshot().git_diff_hunks_in_range(0..4),
|
buffer.snapshot().git_diff_hunks_in_range(0..4, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[(2..3, "", "three\n")],
|
&[(2..3, "", "three\n")],
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod blink_manager;
|
mod blink_manager;
|
||||||
pub mod display_map;
|
pub mod display_map;
|
||||||
mod element;
|
mod element;
|
||||||
|
mod git;
|
||||||
mod highlight_matching_bracket;
|
mod highlight_matching_bracket;
|
||||||
mod hover_popover;
|
mod hover_popover;
|
||||||
pub mod items;
|
pub mod items;
|
||||||
|
@ -42,6 +43,7 @@ use gpui::{
|
||||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||||
use hover_popover::{hide_hover, HoverState};
|
use hover_popover::{hide_hover, HoverState};
|
||||||
pub use items::MAX_TAB_TITLE_LEN;
|
pub use items::MAX_TAB_TITLE_LEN;
|
||||||
|
use itertools::Itertools;
|
||||||
pub use language::{char_kind, CharKind};
|
pub use language::{char_kind, CharKind};
|
||||||
use language::{
|
use language::{
|
||||||
AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
|
AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
|
||||||
|
@ -79,6 +81,8 @@ use theme::{DiagnosticStyle, Theme};
|
||||||
use util::{post_inc, ResultExt, TryFutureExt};
|
use util::{post_inc, ResultExt, TryFutureExt};
|
||||||
use workspace::{ItemNavHistory, Workspace};
|
use workspace::{ItemNavHistory, Workspace};
|
||||||
|
|
||||||
|
use crate::git::diff_hunk_to_display;
|
||||||
|
|
||||||
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
|
const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
|
||||||
const MAX_LINE_LEN: usize = 1024;
|
const MAX_LINE_LEN: usize = 1024;
|
||||||
|
@ -162,6 +166,8 @@ actions!(
|
||||||
NewlineBelow,
|
NewlineBelow,
|
||||||
GoToDiagnostic,
|
GoToDiagnostic,
|
||||||
GoToPrevDiagnostic,
|
GoToPrevDiagnostic,
|
||||||
|
GoToHunk,
|
||||||
|
GoToPrevHunk,
|
||||||
Indent,
|
Indent,
|
||||||
Outdent,
|
Outdent,
|
||||||
DeleteLine,
|
DeleteLine,
|
||||||
|
@ -338,6 +344,8 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(Editor::redo_selection);
|
cx.add_action(Editor::redo_selection);
|
||||||
cx.add_action(Editor::go_to_diagnostic);
|
cx.add_action(Editor::go_to_diagnostic);
|
||||||
cx.add_action(Editor::go_to_prev_diagnostic);
|
cx.add_action(Editor::go_to_prev_diagnostic);
|
||||||
|
cx.add_action(Editor::go_to_hunk);
|
||||||
|
cx.add_action(Editor::go_to_prev_hunk);
|
||||||
cx.add_action(Editor::go_to_definition);
|
cx.add_action(Editor::go_to_definition);
|
||||||
cx.add_action(Editor::go_to_type_definition);
|
cx.add_action(Editor::go_to_type_definition);
|
||||||
cx.add_action(Editor::fold);
|
cx.add_action(Editor::fold);
|
||||||
|
@ -5232,6 +5240,72 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
|
||||||
|
self.go_to_hunk_impl(Direction::Next, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
|
||||||
|
self.go_to_hunk_impl(Direction::Prev, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
||||||
|
let snapshot = self
|
||||||
|
.display_map
|
||||||
|
.update(cx, |display_map, cx| display_map.snapshot(cx));
|
||||||
|
let selection = self.selections.newest::<Point>(cx);
|
||||||
|
|
||||||
|
fn seek_in_direction(
|
||||||
|
this: &mut Editor,
|
||||||
|
snapshot: &DisplaySnapshot,
|
||||||
|
initial_point: Point,
|
||||||
|
is_wrapped: bool,
|
||||||
|
direction: Direction,
|
||||||
|
cx: &mut ViewContext<Editor>,
|
||||||
|
) -> bool {
|
||||||
|
let hunks = if direction == Direction::Next {
|
||||||
|
snapshot
|
||||||
|
.buffer_snapshot
|
||||||
|
.git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
|
||||||
|
} else {
|
||||||
|
snapshot
|
||||||
|
.buffer_snapshot
|
||||||
|
.git_diff_hunks_in_range(0..initial_point.row, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_point = initial_point.to_display_point(snapshot);
|
||||||
|
let mut hunks = hunks
|
||||||
|
.map(|hunk| diff_hunk_to_display(hunk, &snapshot))
|
||||||
|
.skip_while(|hunk| {
|
||||||
|
if is_wrapped {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
hunk.contains_display_row(display_point.row())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.dedup();
|
||||||
|
|
||||||
|
if let Some(hunk) = hunks.next() {
|
||||||
|
this.change_selections(Some(Autoscroll::Center), cx, |s| {
|
||||||
|
let row = hunk.start_display_row();
|
||||||
|
let point = DisplayPoint::new(row, 0);
|
||||||
|
s.select_display_ranges([point..point]);
|
||||||
|
});
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
|
||||||
|
let wrapped_point = match direction {
|
||||||
|
Direction::Next => Point::zero(),
|
||||||
|
Direction::Prev => snapshot.buffer_snapshot.max_point(),
|
||||||
|
};
|
||||||
|
seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn go_to_definition(
|
pub fn go_to_definition(
|
||||||
workspace: &mut Workspace,
|
workspace: &mut Workspace,
|
||||||
_: &GoToDefinition,
|
_: &GoToDefinition,
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::test::{
|
||||||
editor_test_context::EditorTestContext, select_ranges,
|
editor_test_context::EditorTestContext, select_ranges,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
executor::Deterministic,
|
||||||
geometry::rect::RectF,
|
geometry::rect::RectF,
|
||||||
platform::{WindowBounds, WindowOptions},
|
platform::{WindowBounds, WindowOptions},
|
||||||
};
|
};
|
||||||
|
@ -5116,6 +5117,111 @@ fn test_combine_syntax_and_fuzzy_match_highlights() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = EditorTestContext::new(cx);
|
||||||
|
|
||||||
|
let diff_base = r#"
|
||||||
|
use some::mod;
|
||||||
|
|
||||||
|
const A: u32 = 42;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("hello");
|
||||||
|
|
||||||
|
println!("world");
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
.unindent();
|
||||||
|
|
||||||
|
// Edits are modified, removed, modified, added
|
||||||
|
cx.set_state(
|
||||||
|
&r#"
|
||||||
|
use some::modified;
|
||||||
|
|
||||||
|
ˇ
|
||||||
|
fn main() {
|
||||||
|
println!("hello there");
|
||||||
|
|
||||||
|
println!("around the");
|
||||||
|
println!("world");
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.set_diff_base(Some(&diff_base));
|
||||||
|
deterministic.run_until_parked();
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
//Wrap around the bottom of the buffer
|
||||||
|
for _ in 0..3 {
|
||||||
|
editor.go_to_hunk(&GoToHunk, cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.assert_editor_state(
|
||||||
|
&r#"
|
||||||
|
ˇuse some::modified;
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("hello there");
|
||||||
|
|
||||||
|
println!("around the");
|
||||||
|
println!("world");
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
//Wrap around the top of the buffer
|
||||||
|
for _ in 0..2 {
|
||||||
|
editor.go_to_prev_hunk(&GoToPrevHunk, cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.assert_editor_state(
|
||||||
|
&r#"
|
||||||
|
use some::modified;
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
ˇ println!("hello there");
|
||||||
|
|
||||||
|
println!("around the");
|
||||||
|
println!("world");
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.update_editor(|editor, cx| {
|
||||||
|
editor.fold(&Fold, cx);
|
||||||
|
|
||||||
|
//Make sure that the fold only gets one hunk
|
||||||
|
for _ in 0..4 {
|
||||||
|
editor.go_to_hunk(&GoToHunk, cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.assert_editor_state(
|
||||||
|
&r#"
|
||||||
|
ˇuse some::modified;
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("hello there");
|
||||||
|
|
||||||
|
println!("around the");
|
||||||
|
println!("world");
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
.unindent(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
|
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
|
||||||
let point = DisplayPoint::new(row as u32, column as u32);
|
let point = DisplayPoint::new(row as u32, column as u32);
|
||||||
point..point
|
point..point
|
||||||
|
|
|
@ -5,6 +5,7 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::{BlockStyle, DisplaySnapshot, TransformBlock},
|
display_map::{BlockStyle, DisplaySnapshot, TransformBlock},
|
||||||
|
git::{diff_hunk_to_display, DisplayDiffHunk},
|
||||||
hover_popover::{
|
hover_popover::{
|
||||||
HoverAt, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
|
HoverAt, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
|
@ -12,7 +13,7 @@ use crate::{
|
||||||
GoToFetchedDefinition, GoToFetchedTypeDefinition, UpdateGoToDefinitionLink,
|
GoToFetchedDefinition, GoToFetchedTypeDefinition, UpdateGoToDefinitionLink,
|
||||||
},
|
},
|
||||||
mouse_context_menu::DeployMouseContextMenu,
|
mouse_context_menu::DeployMouseContextMenu,
|
||||||
AnchorRangeExt, EditorStyle,
|
EditorStyle,
|
||||||
};
|
};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::{BTreeMap, HashMap};
|
use collections::{BTreeMap, HashMap};
|
||||||
|
@ -33,8 +34,9 @@ use gpui::{
|
||||||
Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent, MouseRegion, MutableAppContext,
|
Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent, MouseRegion, MutableAppContext,
|
||||||
PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WeakViewHandle,
|
PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WeakViewHandle,
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use json::json;
|
use json::json;
|
||||||
use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Point, Selection};
|
use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection};
|
||||||
use project::ProjectPath;
|
use project::ProjectPath;
|
||||||
use settings::{GitGutter, Settings};
|
use settings::{GitGutter, Settings};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -46,13 +48,6 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct DiffHunkLayout {
|
|
||||||
visual_range: Range<u32>,
|
|
||||||
status: DiffHunkStatus,
|
|
||||||
is_folded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SelectionLayout {
|
struct SelectionLayout {
|
||||||
head: DisplayPoint,
|
head: DisplayPoint,
|
||||||
cursor_shape: CursorShape,
|
cursor_shape: CursorShape,
|
||||||
|
@ -576,14 +571,41 @@ impl EditorElement {
|
||||||
let scroll_position = layout.position_map.snapshot.scroll_position();
|
let scroll_position = layout.position_map.snapshot.scroll_position();
|
||||||
let scroll_top = scroll_position.y() * line_height;
|
let scroll_top = scroll_position.y() * line_height;
|
||||||
|
|
||||||
for hunk in &layout.hunk_layouts {
|
for hunk in &layout.display_hunks {
|
||||||
let color = match (hunk.status, hunk.is_folded) {
|
let (display_row_range, status) = match hunk {
|
||||||
(DiffHunkStatus::Added, false) => diff_style.inserted,
|
//TODO: This rendering is entirely a horrible hack
|
||||||
(DiffHunkStatus::Modified, false) => diff_style.modified,
|
&DisplayDiffHunk::Folded { display_row: row } => {
|
||||||
|
let start_y = row as f32 * line_height - scroll_top;
|
||||||
|
let end_y = start_y + line_height;
|
||||||
|
|
||||||
|
let width = diff_style.removed_width_em * line_height;
|
||||||
|
let highlight_origin = bounds.origin() + vec2f(-width, start_y);
|
||||||
|
let highlight_size = vec2f(width * 2., end_y - start_y);
|
||||||
|
let highlight_bounds = RectF::new(highlight_origin, highlight_size);
|
||||||
|
|
||||||
|
cx.scene.push_quad(Quad {
|
||||||
|
bounds: highlight_bounds,
|
||||||
|
background: Some(diff_style.modified),
|
||||||
|
border: Border::new(0., Color::transparent_black()),
|
||||||
|
corner_radius: 1. * line_height,
|
||||||
|
});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayDiffHunk::Unfolded {
|
||||||
|
display_row_range,
|
||||||
|
status,
|
||||||
|
} => (display_row_range, status),
|
||||||
|
};
|
||||||
|
|
||||||
|
let color = match status {
|
||||||
|
DiffHunkStatus::Added => diff_style.inserted,
|
||||||
|
DiffHunkStatus::Modified => diff_style.modified,
|
||||||
|
|
||||||
//TODO: This rendering is entirely a horrible hack
|
//TODO: This rendering is entirely a horrible hack
|
||||||
(DiffHunkStatus::Removed, false) => {
|
DiffHunkStatus::Removed => {
|
||||||
let row = hunk.visual_range.start;
|
let row = *display_row_range.start();
|
||||||
|
|
||||||
let offset = line_height / 2.;
|
let offset = line_height / 2.;
|
||||||
let start_y = row as f32 * line_height - offset - scroll_top;
|
let start_y = row as f32 * line_height - offset - scroll_top;
|
||||||
|
@ -603,33 +625,13 @@ impl EditorElement {
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, true) => {
|
|
||||||
let row = hunk.visual_range.start;
|
|
||||||
let start_y = row as f32 * line_height - scroll_top;
|
|
||||||
let end_y = start_y + line_height;
|
|
||||||
|
|
||||||
let width = diff_style.removed_width_em * line_height;
|
|
||||||
let highlight_origin = bounds.origin() + vec2f(-width, start_y);
|
|
||||||
let highlight_size = vec2f(width * 2., end_y - start_y);
|
|
||||||
let highlight_bounds = RectF::new(highlight_origin, highlight_size);
|
|
||||||
|
|
||||||
cx.scene.push_quad(Quad {
|
|
||||||
bounds: highlight_bounds,
|
|
||||||
background: Some(diff_style.modified),
|
|
||||||
border: Border::new(0., Color::transparent_black()),
|
|
||||||
corner_radius: 1. * line_height,
|
|
||||||
});
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let start_row = hunk.visual_range.start;
|
let start_row = *display_row_range.start();
|
||||||
let end_row = hunk.visual_range.end;
|
let end_row = *display_row_range.end();
|
||||||
|
|
||||||
let start_y = start_row as f32 * line_height - scroll_top;
|
let start_y = start_row as f32 * line_height - scroll_top;
|
||||||
let end_y = end_row as f32 * line_height - scroll_top;
|
let end_y = end_row as f32 * line_height - scroll_top + line_height;
|
||||||
|
|
||||||
let width = diff_style.width_em * line_height;
|
let width = diff_style.width_em * line_height;
|
||||||
let highlight_origin = bounds.origin() + vec2f(-width, start_y);
|
let highlight_origin = bounds.origin() + vec2f(-width, start_y);
|
||||||
|
@ -1107,69 +1109,23 @@ impl EditorElement {
|
||||||
//If a fold contains any hunks then that fold line is marked as modified
|
//If a fold contains any hunks then that fold line is marked as modified
|
||||||
fn layout_git_gutters(
|
fn layout_git_gutters(
|
||||||
&self,
|
&self,
|
||||||
rows: Range<u32>,
|
display_rows: Range<u32>,
|
||||||
snapshot: &EditorSnapshot,
|
snapshot: &EditorSnapshot,
|
||||||
) -> Vec<DiffHunkLayout> {
|
) -> Vec<DisplayDiffHunk> {
|
||||||
let buffer_snapshot = &snapshot.buffer_snapshot;
|
let buffer_snapshot = &snapshot.buffer_snapshot;
|
||||||
let visual_start = DisplayPoint::new(rows.start, 0).to_point(snapshot).row;
|
|
||||||
let visual_end = DisplayPoint::new(rows.end, 0).to_point(snapshot).row;
|
|
||||||
let hunks = buffer_snapshot.git_diff_hunks_in_range(visual_start..visual_end);
|
|
||||||
|
|
||||||
let mut layouts = Vec::<DiffHunkLayout>::new();
|
let buffer_start_row = DisplayPoint::new(display_rows.start, 0)
|
||||||
|
.to_point(snapshot)
|
||||||
|
.row;
|
||||||
|
let buffer_end_row = DisplayPoint::new(display_rows.end, 0)
|
||||||
|
.to_point(snapshot)
|
||||||
|
.row;
|
||||||
|
|
||||||
for hunk in hunks {
|
buffer_snapshot
|
||||||
let hunk_start_point = Point::new(hunk.buffer_range.start, 0);
|
.git_diff_hunks_in_range(buffer_start_row..buffer_end_row, false)
|
||||||
let hunk_end_point = Point::new(hunk.buffer_range.end, 0);
|
.map(|hunk| diff_hunk_to_display(hunk, snapshot))
|
||||||
let hunk_start_point_sub = Point::new(hunk.buffer_range.start.saturating_sub(1), 0);
|
.dedup()
|
||||||
let hunk_end_point_sub = Point::new(
|
.collect()
|
||||||
hunk.buffer_range
|
|
||||||
.end
|
|
||||||
.saturating_sub(1)
|
|
||||||
.max(hunk.buffer_range.start),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
let is_removal = hunk.status() == DiffHunkStatus::Removed;
|
|
||||||
|
|
||||||
let folds_start = Point::new(hunk.buffer_range.start.saturating_sub(1), 0);
|
|
||||||
let folds_end = Point::new(hunk.buffer_range.end + 1, 0);
|
|
||||||
let folds_range = folds_start..folds_end;
|
|
||||||
|
|
||||||
let containing_fold = snapshot.folds_in_range(folds_range).find(|fold_range| {
|
|
||||||
let fold_point_range = fold_range.to_point(buffer_snapshot);
|
|
||||||
let fold_point_range = fold_point_range.start..=fold_point_range.end;
|
|
||||||
|
|
||||||
let folded_start = fold_point_range.contains(&hunk_start_point);
|
|
||||||
let folded_end = fold_point_range.contains(&hunk_end_point_sub);
|
|
||||||
let folded_start_sub = fold_point_range.contains(&hunk_start_point_sub);
|
|
||||||
|
|
||||||
(folded_start && folded_end) || (is_removal && folded_start_sub)
|
|
||||||
});
|
|
||||||
|
|
||||||
let visual_range = if let Some(fold) = containing_fold {
|
|
||||||
let row = fold.start.to_display_point(snapshot).row();
|
|
||||||
row..row
|
|
||||||
} else {
|
|
||||||
let start = hunk_start_point.to_display_point(snapshot).row();
|
|
||||||
let end = hunk_end_point.to_display_point(snapshot).row();
|
|
||||||
start..end
|
|
||||||
};
|
|
||||||
|
|
||||||
let has_existing_layout = match layouts.last() {
|
|
||||||
Some(e) => visual_range == e.visual_range && e.status == hunk.status(),
|
|
||||||
None => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if !has_existing_layout {
|
|
||||||
layouts.push(DiffHunkLayout {
|
|
||||||
visual_range,
|
|
||||||
status: hunk.status(),
|
|
||||||
is_folded: containing_fold.is_some(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
layouts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_line_numbers(
|
fn layout_line_numbers(
|
||||||
|
@ -1721,7 +1677,7 @@ impl Element for EditorElement {
|
||||||
let line_number_layouts =
|
let line_number_layouts =
|
||||||
self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx);
|
self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx);
|
||||||
|
|
||||||
let hunk_layouts = self.layout_git_gutters(start_row..end_row, &snapshot);
|
let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot);
|
||||||
|
|
||||||
let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines);
|
let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines);
|
||||||
|
|
||||||
|
@ -1880,7 +1836,7 @@ impl Element for EditorElement {
|
||||||
highlighted_rows,
|
highlighted_rows,
|
||||||
highlighted_ranges,
|
highlighted_ranges,
|
||||||
line_number_layouts,
|
line_number_layouts,
|
||||||
hunk_layouts,
|
display_hunks,
|
||||||
blocks,
|
blocks,
|
||||||
selections,
|
selections,
|
||||||
context_menu,
|
context_menu,
|
||||||
|
@ -2002,7 +1958,7 @@ pub struct LayoutState {
|
||||||
active_rows: BTreeMap<u32, bool>,
|
active_rows: BTreeMap<u32, bool>,
|
||||||
highlighted_rows: Option<Range<u32>>,
|
highlighted_rows: Option<Range<u32>>,
|
||||||
line_number_layouts: Vec<Option<text_layout::Line>>,
|
line_number_layouts: Vec<Option<text_layout::Line>>,
|
||||||
hunk_layouts: Vec<DiffHunkLayout>,
|
display_hunks: Vec<DisplayDiffHunk>,
|
||||||
blocks: Vec<BlockLayout>,
|
blocks: Vec<BlockLayout>,
|
||||||
highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
|
highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
|
||||||
selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
|
selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
|
||||||
|
|
93
crates/editor/src/git.rs
Normal file
93
crates/editor/src/git.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
use git::diff::{DiffHunk, DiffHunkStatus};
|
||||||
|
use language::Point;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||||
|
AnchorRangeExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum DisplayDiffHunk {
|
||||||
|
Folded {
|
||||||
|
display_row: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
Unfolded {
|
||||||
|
display_row_range: RangeInclusive<u32>,
|
||||||
|
status: DiffHunkStatus,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplayDiffHunk {
|
||||||
|
pub fn start_display_row(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
&DisplayDiffHunk::Folded { display_row } => display_row,
|
||||||
|
DisplayDiffHunk::Unfolded {
|
||||||
|
display_row_range, ..
|
||||||
|
} => *display_row_range.start(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_display_row(&self, display_row: u32) -> bool {
|
||||||
|
let range = match self {
|
||||||
|
&DisplayDiffHunk::Folded { display_row } => display_row..=display_row,
|
||||||
|
|
||||||
|
DisplayDiffHunk::Unfolded {
|
||||||
|
display_row_range, ..
|
||||||
|
} => display_row_range.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
range.contains(&display_row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diff_hunk_to_display(hunk: DiffHunk<u32>, snapshot: &DisplaySnapshot) -> DisplayDiffHunk {
|
||||||
|
let hunk_start_point = Point::new(hunk.buffer_range.start, 0);
|
||||||
|
let hunk_start_point_sub = Point::new(hunk.buffer_range.start.saturating_sub(1), 0);
|
||||||
|
let hunk_end_point_sub = Point::new(
|
||||||
|
hunk.buffer_range
|
||||||
|
.end
|
||||||
|
.saturating_sub(1)
|
||||||
|
.max(hunk.buffer_range.start),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let is_removal = hunk.status() == DiffHunkStatus::Removed;
|
||||||
|
|
||||||
|
let folds_start = Point::new(hunk.buffer_range.start.saturating_sub(2), 0);
|
||||||
|
let folds_end = Point::new(hunk.buffer_range.end + 2, 0);
|
||||||
|
let folds_range = folds_start..folds_end;
|
||||||
|
|
||||||
|
let containing_fold = snapshot.folds_in_range(folds_range).find(|fold_range| {
|
||||||
|
let fold_point_range = fold_range.to_point(&snapshot.buffer_snapshot);
|
||||||
|
let fold_point_range = fold_point_range.start..=fold_point_range.end;
|
||||||
|
|
||||||
|
let folded_start = fold_point_range.contains(&hunk_start_point);
|
||||||
|
let folded_end = fold_point_range.contains(&hunk_end_point_sub);
|
||||||
|
let folded_start_sub = fold_point_range.contains(&hunk_start_point_sub);
|
||||||
|
|
||||||
|
(folded_start && folded_end) || (is_removal && folded_start_sub)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(fold) = containing_fold {
|
||||||
|
let row = fold.start.to_display_point(snapshot).row();
|
||||||
|
DisplayDiffHunk::Folded { display_row: row }
|
||||||
|
} else {
|
||||||
|
let start = hunk_start_point.to_display_point(snapshot).row();
|
||||||
|
|
||||||
|
let hunk_end_row_inclusive = hunk
|
||||||
|
.buffer_range
|
||||||
|
.end
|
||||||
|
.saturating_sub(1)
|
||||||
|
.max(hunk.buffer_range.start);
|
||||||
|
let hunk_end_point = Point::new(hunk_end_row_inclusive, 0);
|
||||||
|
let end = hunk_end_point.to_display_point(snapshot).row();
|
||||||
|
|
||||||
|
DisplayDiffHunk::Unfolded {
|
||||||
|
display_row_range: start..=end,
|
||||||
|
status: hunk.status(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2592,10 +2592,13 @@ impl MultiBufferSnapshot {
|
||||||
pub fn git_diff_hunks_in_range<'a>(
|
pub fn git_diff_hunks_in_range<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
row_range: Range<u32>,
|
row_range: Range<u32>,
|
||||||
|
reversed: bool,
|
||||||
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
||||||
self.as_singleton()
|
self.as_singleton()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(move |(_, _, buffer)| buffer.git_diff_hunks_in_range(row_range.clone()))
|
.flat_map(move |(_, _, buffer)| {
|
||||||
|
buffer.git_diff_hunks_in_range(row_range.clone(), reversed)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
|
pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
|
||||||
|
|
|
@ -594,7 +594,6 @@ impl<'a> MutableSelectionsCollection<'a> {
|
||||||
self.select_anchors(selections)
|
self.select_anchors(selections)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
|
||||||
pub fn select_display_ranges<T>(&mut self, ranges: T)
|
pub fn select_display_ranges<T>(&mut self, ranges: T)
|
||||||
where
|
where
|
||||||
T: IntoIterator<Item = Range<DisplayPoint>>,
|
T: IntoIterator<Item = Range<DisplayPoint>>,
|
||||||
|
|
|
@ -151,6 +151,11 @@ impl<'a> EditorTestContext<'a> {
|
||||||
snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
|
snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_diff_base(&mut self, diff_base: Option<&str>) {
|
||||||
|
let diff_base = diff_base.map(String::from);
|
||||||
|
self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx));
|
||||||
|
}
|
||||||
|
|
||||||
/// Change the editor's text and selections using a string containing
|
/// Change the editor's text and selections using a string containing
|
||||||
/// embedded range markers that represent the ranges and directions of
|
/// embedded range markers that represent the ranges and directions of
|
||||||
/// each selection.
|
/// each selection.
|
||||||
|
|
|
@ -15,12 +15,12 @@ pub enum DiffHunkStatus {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct DiffHunk<T> {
|
pub struct DiffHunk<T> {
|
||||||
pub buffer_range: Range<T>,
|
pub buffer_range: Range<T>,
|
||||||
pub head_byte_range: Range<usize>,
|
pub diff_base_byte_range: Range<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiffHunk<u32> {
|
impl DiffHunk<u32> {
|
||||||
pub fn status(&self) -> DiffHunkStatus {
|
pub fn status(&self) -> DiffHunkStatus {
|
||||||
if self.head_byte_range.is_empty() {
|
if self.diff_base_byte_range.is_empty() {
|
||||||
DiffHunkStatus::Added
|
DiffHunkStatus::Added
|
||||||
} else if self.buffer_range.is_empty() {
|
} else if self.buffer_range.is_empty() {
|
||||||
DiffHunkStatus::Removed
|
DiffHunkStatus::Removed
|
||||||
|
@ -75,6 +75,7 @@ impl BufferDiff {
|
||||||
&'a self,
|
&'a self,
|
||||||
query_row_range: Range<u32>,
|
query_row_range: Range<u32>,
|
||||||
buffer: &'a BufferSnapshot,
|
buffer: &'a BufferSnapshot,
|
||||||
|
reversed: bool,
|
||||||
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
||||||
let start = buffer.anchor_before(Point::new(query_row_range.start, 0));
|
let start = buffer.anchor_before(Point::new(query_row_range.start, 0));
|
||||||
let end = buffer.anchor_after(Point::new(query_row_range.end, 0));
|
let end = buffer.anchor_after(Point::new(query_row_range.end, 0));
|
||||||
|
@ -86,7 +87,12 @@ impl BufferDiff {
|
||||||
});
|
});
|
||||||
|
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
cursor.next(buffer);
|
if reversed {
|
||||||
|
cursor.prev(buffer);
|
||||||
|
} else {
|
||||||
|
cursor.next(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
let hunk = cursor.item()?;
|
let hunk = cursor.item()?;
|
||||||
|
|
||||||
let range = hunk.buffer_range.to_point(buffer);
|
let range = hunk.buffer_range.to_point(buffer);
|
||||||
|
@ -98,7 +104,7 @@ impl BufferDiff {
|
||||||
|
|
||||||
Some(DiffHunk {
|
Some(DiffHunk {
|
||||||
buffer_range: range.start.row..end_row,
|
buffer_range: range.start.row..end_row,
|
||||||
head_byte_range: hunk.head_byte_range.clone(),
|
diff_base_byte_range: hunk.diff_base_byte_range.clone(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -135,7 +141,7 @@ impl BufferDiff {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn hunks<'a>(&'a self, text: &'a BufferSnapshot) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
fn hunks<'a>(&'a self, text: &'a BufferSnapshot) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
|
||||||
self.hunks_in_range(0..u32::MAX, text)
|
self.hunks_in_range(0..u32::MAX, text, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff<'a>(head: &'a str, current: &'a str) -> Option<GitPatch<'a>> {
|
fn diff<'a>(head: &'a str, current: &'a str) -> Option<GitPatch<'a>> {
|
||||||
|
@ -171,7 +177,7 @@ impl BufferDiff {
|
||||||
|
|
||||||
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 head_byte_range: Option<Range<usize>> = None;
|
let mut diff_base_byte_range: Option<Range<usize>> = None;
|
||||||
|
|
||||||
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();
|
||||||
|
@ -192,9 +198,9 @@ impl BufferDiff {
|
||||||
if kind == GitDiffLineType::Deletion {
|
if kind == GitDiffLineType::Deletion {
|
||||||
let end = content_offset + content_len;
|
let end = content_offset + content_len;
|
||||||
|
|
||||||
match &mut head_byte_range {
|
match &mut diff_base_byte_range {
|
||||||
Some(head_byte_range) => head_byte_range.end = end as usize,
|
Some(head_byte_range) => head_byte_range.end = end as usize,
|
||||||
None => head_byte_range = Some(content_offset as usize..end as usize),
|
None => diff_base_byte_range = Some(content_offset as usize..end as usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
if first_deletion_buffer_row.is_none() {
|
if first_deletion_buffer_row.is_none() {
|
||||||
|
@ -215,14 +221,14 @@ impl BufferDiff {
|
||||||
});
|
});
|
||||||
|
|
||||||
//unwrap_or addition without deletion
|
//unwrap_or addition without deletion
|
||||||
let head_byte_range = head_byte_range.unwrap_or(0..0);
|
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);
|
||||||
DiffHunk {
|
DiffHunk {
|
||||||
buffer_range,
|
buffer_range,
|
||||||
head_byte_range,
|
diff_base_byte_range,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +248,7 @@ pub fn assert_hunks<Iter>(
|
||||||
.map(|hunk| {
|
.map(|hunk| {
|
||||||
(
|
(
|
||||||
hunk.buffer_range.clone(),
|
hunk.buffer_range.clone(),
|
||||||
&diff_base[hunk.head_byte_range],
|
&diff_base[hunk.diff_base_byte_range],
|
||||||
buffer
|
buffer
|
||||||
.text_for_range(
|
.text_for_range(
|
||||||
Point::new(hunk.buffer_range.start, 0)
|
Point::new(hunk.buffer_range.start, 0)
|
||||||
|
@ -349,7 +355,7 @@ mod tests {
|
||||||
assert_eq!(diff.hunks(&buffer).count(), 8);
|
assert_eq!(diff.hunks(&buffer).count(), 8);
|
||||||
|
|
||||||
assert_hunks(
|
assert_hunks(
|
||||||
diff.hunks_in_range(7..12, &buffer),
|
diff.hunks_in_range(7..12, &buffer, false),
|
||||||
&buffer,
|
&buffer,
|
||||||
&diff_base,
|
&diff_base,
|
||||||
&[
|
&[
|
||||||
|
|
|
@ -681,7 +681,7 @@ impl Buffer {
|
||||||
self.diff_base.as_deref()
|
self.diff_base.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_diff_base(&mut self, diff_base: Option<String>, cx: &mut ModelContext<Self>) {
|
pub fn set_diff_base(&mut self, diff_base: Option<String>, cx: &mut ModelContext<Self>) {
|
||||||
self.diff_base = diff_base;
|
self.diff_base = diff_base;
|
||||||
self.git_diff_recalc(cx);
|
self.git_diff_recalc(cx);
|
||||||
}
|
}
|
||||||
|
@ -2294,8 +2294,10 @@ impl BufferSnapshot {
|
||||||
pub fn git_diff_hunks_in_range<'a>(
|
pub fn git_diff_hunks_in_range<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
query_row_range: Range<u32>,
|
query_row_range: Range<u32>,
|
||||||
|
reversed: bool,
|
||||||
) -> impl 'a + Iterator<Item = git::diff::DiffHunk<u32>> {
|
) -> impl 'a + Iterator<Item = git::diff::DiffHunk<u32>> {
|
||||||
self.git_diff.hunks_in_range(query_row_range, self)
|
self.git_diff
|
||||||
|
.hunks_in_range(query_row_range, self, reversed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostics_in_range<'a, T, O>(
|
pub fn diagnostics_in_range<'a, T, O>(
|
||||||
|
|
|
@ -4410,7 +4410,7 @@ impl Project {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let buffer_id = buffer.update(&mut cx, |buffer, cx| {
|
let buffer_id = buffer.update(&mut cx, |buffer, cx| {
|
||||||
buffer.update_diff_base(diff_base.clone(), cx);
|
buffer.set_diff_base(diff_base.clone(), cx);
|
||||||
buffer.remote_id()
|
buffer.remote_id()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4968,7 +4968,7 @@ impl Project {
|
||||||
.and_then(|b| b.upgrade(cx))
|
.and_then(|b| b.upgrade(cx))
|
||||||
.ok_or_else(|| anyhow!("No such buffer {}", buffer_id))?;
|
.ok_or_else(|| anyhow!("No such buffer {}", buffer_id))?;
|
||||||
|
|
||||||
buffer.update(cx, |buffer, cx| buffer.update_diff_base(diff_base, cx));
|
buffer.update(cx, |buffer, cx| buffer.set_diff_base(diff_base, cx));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
|
@ -3257,7 +3257,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"c.txt": "",
|
"c.txt": "",
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let http_client = FakeHttpClient::with_404_response();
|
let http_client = FakeHttpClient::with_404_response();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue