Rework diff rendering to allow putting the cursor into deleted text, soft-wrapping and scrolling deleted text correctly (#22994)
Closes #12553 * [x] Fix `diff_hunk_before` * [x] Fix failure to show deleted text when expanding hunk w/ cursor on second line of the hunk * [x] Failure to expand diff hunk below the cursor. * [x] Delete the whole file, and expand the diff. Backspace over the deleted hunk, panic! * [x] Go-to-line now counts the diff hunks, but it should not * [x] backspace at the beginning of a deleted hunk deletes too much text * [x] Indent guides are rendered incorrectly * [ ] Fix randomized multi buffer tests Maybe: * [ ] Buffer search should include deleted text (in vim mode it turns out I use `/x` all the time to jump to the next x I can see). * [ ] vim: should refuse to switch into insert mode if selection is fully within a diff. * [ ] vim `o` command when cursor is on last line of deleted hunk. * [ ] vim `shift-o` on first line of deleted hunk moves cursor but doesn't insert line * [x] `enter` at end of diff hunk inserts a new line but doesn't move cursor * [x] (`shift-enter` at start of diff hunk does nothing) * [ ] Inserting a line just before an expanded hunk collapses it Release Notes: - Improved diff rendering, allowing you to navigate with your cursor inside of deleted text in diff hunks. --------- Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Cole <cole@zed.dev> Co-authored-by: Mikayla <mikayla@zed.dev> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Michael <michael@zed.dev> Co-authored-by: Agus <agus@zed.dev> Co-authored-by: João <joao@zed.dev>
This commit is contained in:
parent
1fdae4bae0
commit
d2c55cbe3d
64 changed files with 7653 additions and 5495 deletions
|
@ -2,17 +2,16 @@ use std::{ops::Range, time::Duration};
|
|||
|
||||
use collections::HashSet;
|
||||
use gpui::{AppContext, Task};
|
||||
use language::{language_settings::language_settings, BufferRow};
|
||||
use multi_buffer::{MultiBufferIndentGuide, MultiBufferRow};
|
||||
use text::{BufferId, LineIndent, Point};
|
||||
use language::language_settings::language_settings;
|
||||
use multi_buffer::{IndentGuide, MultiBufferRow};
|
||||
use text::{LineIndent, Point};
|
||||
use ui::ViewContext;
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::{DisplaySnapshot, Editor};
|
||||
|
||||
struct ActiveIndentedRange {
|
||||
buffer_id: BufferId,
|
||||
row_range: Range<BufferRow>,
|
||||
row_range: Range<MultiBufferRow>,
|
||||
indent: LineIndent,
|
||||
}
|
||||
|
||||
|
@ -36,7 +35,7 @@ impl Editor {
|
|||
visible_buffer_range: Range<MultiBufferRow>,
|
||||
snapshot: &DisplaySnapshot,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Option<Vec<MultiBufferIndentGuide>> {
|
||||
) -> Option<Vec<IndentGuide>> {
|
||||
let show_indent_guides = self.should_show_indent_guides().unwrap_or_else(|| {
|
||||
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
||||
language_settings(
|
||||
|
@ -66,7 +65,7 @@ impl Editor {
|
|||
|
||||
pub fn find_active_indent_guide_indices(
|
||||
&mut self,
|
||||
indent_guides: &[MultiBufferIndentGuide],
|
||||
indent_guides: &[IndentGuide],
|
||||
snapshot: &DisplaySnapshot,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Option<HashSet<usize>> {
|
||||
|
@ -134,9 +133,7 @@ impl Editor {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, indent_guide)| {
|
||||
indent_guide.buffer_id == active_indent_range.buffer_id
|
||||
&& indent_guide.indent_level()
|
||||
== active_indent_range.indent.len(indent_guide.tab_size)
|
||||
indent_guide.indent_level() == active_indent_range.indent.len(indent_guide.tab_size)
|
||||
});
|
||||
|
||||
let mut matches = HashSet::default();
|
||||
|
@ -158,7 +155,7 @@ pub fn indent_guides_in_range(
|
|||
ignore_disabled_for_language: bool,
|
||||
snapshot: &DisplaySnapshot,
|
||||
cx: &AppContext,
|
||||
) -> Vec<MultiBufferIndentGuide> {
|
||||
) -> Vec<IndentGuide> {
|
||||
let start_anchor = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_before(Point::new(visible_buffer_range.start.0, 0));
|
||||
|
@ -169,14 +166,12 @@ pub fn indent_guides_in_range(
|
|||
snapshot
|
||||
.buffer_snapshot
|
||||
.indent_guides_in_range(start_anchor..end_anchor, ignore_disabled_for_language, cx)
|
||||
.into_iter()
|
||||
.filter(|indent_guide| {
|
||||
if editor.buffer_folded(indent_guide.buffer_id, cx) {
|
||||
if editor.is_buffer_folded(indent_guide.buffer_id, cx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let start =
|
||||
MultiBufferRow(indent_guide.multibuffer_row_range.start.0.saturating_sub(1));
|
||||
let start = MultiBufferRow(indent_guide.start_row.0.saturating_sub(1));
|
||||
// Filter out indent guides that are inside a fold
|
||||
// All indent guides that are starting "offscreen" have a start value of the first visible row minus one
|
||||
// Therefore checking if a line is folded at first visible row minus one causes the other indent guides that are not related to the fold to disappear as well
|
||||
|
@ -193,24 +188,11 @@ async fn resolve_indented_range(
|
|||
snapshot: DisplaySnapshot,
|
||||
buffer_row: MultiBufferRow,
|
||||
) -> Option<ActiveIndentedRange> {
|
||||
let (buffer_row, buffer_snapshot, buffer_id) =
|
||||
if let Some((_, buffer_id, snapshot)) = snapshot.buffer_snapshot.as_singleton() {
|
||||
(buffer_row.0, snapshot, buffer_id)
|
||||
} else {
|
||||
let (snapshot, point) = snapshot.buffer_snapshot.buffer_line_for_row(buffer_row)?;
|
||||
|
||||
let buffer_id = snapshot.remote_id();
|
||||
(point.start.row, snapshot, buffer_id)
|
||||
};
|
||||
|
||||
buffer_snapshot
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.enclosing_indent(buffer_row)
|
||||
.await
|
||||
.map(|(row_range, indent)| ActiveIndentedRange {
|
||||
row_range,
|
||||
indent,
|
||||
buffer_id,
|
||||
})
|
||||
.map(|(row_range, indent)| ActiveIndentedRange { row_range, indent })
|
||||
}
|
||||
|
||||
fn should_recalculate_indented_range(
|
||||
|
@ -222,23 +204,23 @@ fn should_recalculate_indented_range(
|
|||
if prev_row.0 == new_row.0 {
|
||||
return false;
|
||||
}
|
||||
if let Some((_, _, snapshot)) = snapshot.buffer_snapshot.as_singleton() {
|
||||
if !current_indent_range.row_range.contains(&new_row.0) {
|
||||
if snapshot.buffer_snapshot.is_singleton() {
|
||||
if !current_indent_range.row_range.contains(&new_row) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let old_line_indent = snapshot.line_indent_for_row(prev_row.0);
|
||||
let new_line_indent = snapshot.line_indent_for_row(new_row.0);
|
||||
let old_line_indent = snapshot.buffer_snapshot.line_indent_for_row(prev_row);
|
||||
let new_line_indent = snapshot.buffer_snapshot.line_indent_for_row(new_row);
|
||||
|
||||
if old_line_indent.is_line_empty()
|
||||
|| new_line_indent.is_line_empty()
|
||||
|| old_line_indent != new_line_indent
|
||||
|| snapshot.max_point().row == new_row.0
|
||||
|| snapshot.buffer_snapshot.max_point().row == new_row.0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
let next_line_indent = snapshot.line_indent_for_row(new_row.0 + 1);
|
||||
let next_line_indent = snapshot.buffer_snapshot.line_indent_for_row(new_row + 1);
|
||||
next_line_indent.is_line_empty() || next_line_indent != old_line_indent
|
||||
} else {
|
||||
true
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue