Compare commits

...

31 Commits

Author SHA1 Message Date
Cole Miller
f5f7eb0c70 wip it renders 2025-11-14 16:48:23 -05:00
cameron
b15c7be652 some doc comments 2025-11-14 15:13:09 +00:00
Cole Miller
6cde3b2502 get the fold map compiling 2025-11-14 02:15:58 -05:00
Cole Miller
c12c220d3e get the inlay map compiling 2025-11-14 00:50:31 -05:00
Cole Miller
744de9cefa finish implementing chunks 2025-11-13 22:51:06 -05:00
Cole Miller
a75230c63f implement some filtermap apis for the inlaymap to consume
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-11-13 19:40:00 -05:00
Cole Miller
9ecc2445e2 wip 2025-11-12 20:34:22 -05:00
Cole Miller
d3c1a5fe20 candidate fix for insertion halves of diff hunks going missing 2025-11-12 01:48:43 -05:00
Cole Miller
8dfe7ebdb6 wip 2025-11-11 00:43:08 -05:00
Cole Miller
a1a4e8ebe5 fix wrong diff hunk row ranges with adjacent hunks 2025-11-10 23:47:13 -05:00
Cole Miller
fc448d7d44 simplify and fix up edit expansion 2025-11-10 20:06:28 -05:00
Cole Miller
56c70e9d1c fix diff hunks disagreeing with transforms when an excerpt boundary
falls in the middle of an added line

Co-authored-by: Cameron <cameron@zed.dev>
2025-11-10 19:06:47 -05:00
Cole Miller
52cdc838f5 more 2025-11-09 16:33:04 -05:00
Cole Miller
6f3c81ab8c fix wrong changed range computation in buffer_diff 2025-11-09 15:28:53 -05:00
Cole Miller
6ab75d3d4b expand edit new ranges to the whole line 2025-11-09 15:24:37 -05:00
Cole Miller
34d2063456 progress on seed = 266 2025-11-08 16:25:40 -05:00
Cole Miller
bb56a17798 correct overshoot calculation for edit old range 2025-11-08 15:47:23 -05:00
Cole Miller
8c008cb9ae fix output edit overshoot calculation
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-11-07 09:54:06 -05:00
cameron
b2a8c08cef slightly improved debug outputs 2025-11-07 14:03:55 +00:00
Cole Miller
e080b3a75a add some assertions about the output edits 2025-11-07 00:09:18 -05:00
Cole Miller
f5557a1867 actually fix the coalescing logic 2025-11-06 22:29:08 -05:00
Cole Miller
11a356b6e6 fix coalescing logic for additional edits 2025-11-06 22:01:24 -05:00
cameron
3614b623b8 extend test to check case where next edit exhibits same wonky
intersection behaviour
2025-11-06 19:36:23 +00:00
Cole Miller
4e1b674e39 fix an issue with the multibuffer emitting incorrect edits when a diff
hunk is invalidated

Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-11-06 13:29:50 -05:00
Cole Miller
532dce97f1 rework expected text/summary calculation to use row infos, first fails
for seed = 111
2025-11-04 20:14:20 -05:00
Cole Miller
97ea848ac0 checkpoint, passing up to seed = 13
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-11-04 17:04:30 -05:00
Cole Miller
49ebf59d05 wip turns out the test wasn't working
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-11-03 18:48:31 -05:00
Cole Miller
20b457de2a get randomized test passing for many iterations 🎉
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-11-03 16:38:10 -05:00
Cole Miller
a63fada5f5 expand all diff hunks and implement todo 2025-11-02 23:49:46 -05:00
Cole Miller
23adeab8d2 wip
Co-authored-by: Cameron <cameron@zed.dev>
2025-11-02 23:38:10 -05:00
Cole Miller
6e0f65e05a sketch in split editor and start working on new logic in block map
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
2025-10-29 21:30:16 -04:00
21 changed files with 2866 additions and 443 deletions

View File

@@ -70,6 +70,7 @@ pub struct DiffHunk {
pub secondary_status: DiffHunkSecondaryStatus,
}
// FIXME
/// We store [`InternalDiffHunk`]s internally so we don't need to store the additional row range.
#[derive(Debug, Clone, PartialEq, Eq)]
struct InternalDiffHunk {
@@ -268,6 +269,16 @@ impl BufferDiffSnapshot {
self.secondary_diff.as_deref()
}
pub fn valid_and_invalid_hunks_intersecting_range<'a>(
&'a self,
range: Range<Anchor>,
buffer: &'a text::BufferSnapshot,
) -> impl 'a + Iterator<Item = DiffHunk> {
let unstaged_counterpart = self.secondary_diff.as_ref().map(|diff| &diff.inner);
self.inner
.hunks_intersecting_range(range, buffer, unstaged_counterpart)
}
pub fn hunks_intersecting_range<'a>(
&'a self,
range: Range<Anchor>,
@@ -276,6 +287,7 @@ impl BufferDiffSnapshot {
let unstaged_counterpart = self.secondary_diff.as_ref().map(|diff| &diff.inner);
self.inner
.hunks_intersecting_range(range, buffer, unstaged_counterpart)
.filter(|hunk| hunk.buffer_range.start.is_valid(buffer))
}
pub fn hunks_intersecting_range_rev<'a>(
@@ -562,10 +574,6 @@ impl BufferDiffInner {
let (start_point, (start_anchor, start_base)) = summaries.next()?;
let (mut end_point, (mut end_anchor, end_base)) = summaries.next()?;
if !start_anchor.is_valid(buffer) {
continue;
}
if end_point.column > 0 && end_point < max_point {
end_point.row += 1;
end_point.column = 0;
@@ -671,7 +679,7 @@ impl BufferDiffInner {
old_cursor.next();
new_cursor.next();
let mut start = None;
let mut end = None;
let mut end: Option<Anchor> = None;
loop {
match (new_cursor.item(), old_cursor.item()) {
@@ -683,7 +691,11 @@ impl BufferDiffInner {
{
Ordering::Less => {
start.get_or_insert(new_hunk.buffer_range.start);
end.replace(new_hunk.buffer_range.end);
if end.is_none_or(|end| {
end.cmp(&new_hunk.buffer_range.end, new_snapshot).is_lt()
}) {
end.replace(new_hunk.buffer_range.end);
}
new_cursor.next();
}
Ordering::Equal => {
@@ -695,8 +707,14 @@ impl BufferDiffInner {
.cmp(&new_hunk.buffer_range.end, new_snapshot)
.is_ge()
{
debug_assert!(end.is_none_or(|end| {
end.cmp(&old_hunk.buffer_range.end, new_snapshot).is_le()
}));
end.replace(old_hunk.buffer_range.end);
} else {
debug_assert!(end.is_none_or(|end| {
end.cmp(&new_hunk.buffer_range.end, new_snapshot).is_le()
}));
end.replace(new_hunk.buffer_range.end);
}
}
@@ -706,19 +724,31 @@ impl BufferDiffInner {
}
Ordering::Greater => {
start.get_or_insert(old_hunk.buffer_range.start);
end.replace(old_hunk.buffer_range.end);
if end.is_none_or(|end| {
end.cmp(&old_hunk.buffer_range.end, new_snapshot).is_lt()
}) {
end.replace(old_hunk.buffer_range.end);
}
old_cursor.next();
}
}
}
(Some(new_hunk), None) => {
start.get_or_insert(new_hunk.buffer_range.start);
end.replace(new_hunk.buffer_range.end);
if end
.is_none_or(|end| end.cmp(&new_hunk.buffer_range.end, new_snapshot).is_lt())
{
end.replace(new_hunk.buffer_range.end);
}
new_cursor.next();
}
(None, Some(old_hunk)) => {
start.get_or_insert(old_hunk.buffer_range.start);
end.replace(old_hunk.buffer_range.end);
if end
.is_none_or(|end| end.cmp(&old_hunk.buffer_range.end, new_snapshot).is_lt())
{
end.replace(old_hunk.buffer_range.end);
}
old_cursor.next();
}
(None, None) => break,
@@ -1140,6 +1170,7 @@ impl BufferDiff {
.map(|diff| &diff.read(cx).inner);
self.inner
.hunks_intersecting_range(range, buffer_snapshot, unstaged_counterpart)
.filter(|hunk| hunk.buffer_range.start.is_valid(buffer_snapshot))
}
pub fn hunks_intersecting_range_rev<'a>(

View File

@@ -21,7 +21,8 @@ fn to_tab_point_benchmark(c: &mut Criterion) {
let buffer_snapshot = cx.read(|cx| buffer.read(cx).snapshot(cx));
use editor::display_map::*;
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
let fold_point = fold_snapshot.to_fold_point(
inlay_snapshot.to_point(InlayOffset(rng.random_range(0..length))),
@@ -65,7 +66,8 @@ fn to_fold_point_benchmark(c: &mut Criterion) {
let buffer_snapshot = cx.read(|cx| buffer.read(cx).snapshot(cx));
use editor::display_map::*;
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
let fold_point = fold_snapshot.to_fold_point(

View File

@@ -20,6 +20,7 @@
mod block_map;
mod crease_map;
mod custom_highlights;
mod filter_map;
mod fold_map;
mod inlay_map;
pub(crate) mod invisibles;
@@ -27,7 +28,8 @@ mod tab_map;
mod wrap_map;
use crate::{
EditorStyle, RowExt, hover_links::InlayHighlight, inlays::Inlay, movement::TextLayoutDetails,
EditorStyle, RowExt, display_map::filter_map::FilterSnapshot, hover_links::InlayHighlight,
inlays::Inlay, movement::TextLayoutDetails,
};
pub use block_map::{
Block, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap, BlockPlacement,
@@ -37,6 +39,7 @@ pub use block_map::{
use block_map::{BlockRow, BlockSnapshot};
use collections::{HashMap, HashSet};
pub use crease_map::*;
pub use filter_map::FilterMode;
use fold_map::FoldSnapshot;
pub use fold_map::{
ChunkRenderer, ChunkRendererContext, ChunkRendererId, Fold, FoldId, FoldPlaceholder, FoldPoint,
@@ -72,7 +75,9 @@ use ui::{SharedString, px};
use unicode_segmentation::UnicodeSegmentation;
use wrap_map::{WrapMap, WrapSnapshot};
pub use crate::display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap};
pub use crate::display_map::{
filter_map::FilterMap, fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FoldStatus {
@@ -101,6 +106,9 @@ pub struct DisplayMap {
/// The buffer that we are displaying.
buffer: Entity<MultiBuffer>,
buffer_subscription: BufferSubscription,
/// Filters out certain entire lines (based on git status). Used to
/// implement split diff view
filter_map: FilterMap,
/// Decides where the [`Inlay`]s should be displayed.
inlay_map: InlayMap,
/// Decides where the fold indicators should be and tracks parts of a source file that are currently folded.
@@ -133,6 +141,7 @@ impl DisplayMap {
excerpt_header_height: u32,
fold_placeholder: FoldPlaceholder,
diagnostics_max_severity: DiagnosticSeverity,
filter_mode: Option<FilterMode>,
cx: &mut Context<Self>,
) -> Self {
let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
@@ -140,7 +149,8 @@ impl DisplayMap {
let tab_size = Self::tab_size(&buffer, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let crease_map = CreaseMap::new(&buffer_snapshot);
let (inlay_map, snapshot) = InlayMap::new(buffer_snapshot);
let (filter_map, snapshot) = FilterMap::new(filter_mode, buffer_snapshot);
let (inlay_map, snapshot) = InlayMap::new(snapshot);
let (fold_map, snapshot) = FoldMap::new(snapshot);
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
@@ -151,8 +161,9 @@ impl DisplayMap {
DisplayMap {
buffer,
buffer_subscription,
fold_map,
filter_map,
inlay_map,
fold_map,
tab_map,
wrap_map,
block_map,
@@ -169,7 +180,8 @@ impl DisplayMap {
pub fn snapshot(&mut self, cx: &mut Context<Self>) -> DisplaySnapshot {
let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let (inlay_snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
let (filter_snapshot, edits) = self.filter_map.sync(buffer_snapshot, edits);
let (inlay_snapshot, edits) = self.inlay_map.sync(filter_snapshot, edits);
let (fold_snapshot, edits) = self.fold_map.read(inlay_snapshot, edits);
let tab_size = Self::tab_size(&self.buffer, cx);
let (tab_snapshot, edits) = self.tab_map.sync(fold_snapshot, edits, tab_size);
@@ -210,7 +222,8 @@ impl DisplayMap {
let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot.clone(), edits);
let (snapshot, edits) = self.filter_map.sync(buffer_snapshot.clone(), edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
@@ -283,6 +296,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -312,6 +326,7 @@ impl DisplayMap {
.collect::<Vec<_>>();
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -334,6 +349,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -352,6 +368,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -370,6 +387,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -414,6 +432,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -428,6 +447,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -446,6 +466,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -464,6 +485,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -534,6 +556,30 @@ impl DisplayMap {
.update(cx, |map, cx| map.set_wrap_width(width, cx))
}
#[cfg(test)]
pub fn set_filter_mode(&mut self, filter_mode: Option<FilterMode>, cx: &mut Context<Self>) {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (_, snapshot, edits) = self.fold_map.write(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
self.block_map.read(snapshot, edits);
let (filter_snapshot, filter_edits) = self.filter_map.set_mode(filter_mode);
let (snapshot, edits) = self.inlay_map.sync(filter_snapshot, filter_edits);
let (_, snapshot, edits) = self.fold_map.write(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
let (snapshot, edits) = self
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
self.block_map.read(snapshot, edits);
}
pub fn update_fold_widths(
&mut self,
widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
@@ -542,6 +588,7 @@ impl DisplayMap {
let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.filter_map.sync(snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -571,12 +618,19 @@ impl DisplayMap {
to_insert: Vec<Inlay>,
cx: &mut Context<Self>,
) {
// when I type `ggdG`
// - calculate start and end of edit in display coords
// - call display_map.edit(vec![edit_range])
// - call buffer.set_text(new_text)
//
//
if to_remove.is_empty() && to_insert.is_empty() {
return;
}
let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner();
let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
let (snapshot, edits) = self.filter_map.sync(buffer_snapshot, edits);
let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
let (snapshot, edits) = self.fold_map.read(snapshot, edits);
let tab_size = Self::tab_size(&self.buffer, cx);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
@@ -773,6 +827,16 @@ impl DisplaySnapshot {
.inlay_snapshot
}
pub fn filter_snapshot(&self) -> &FilterSnapshot {
&self
.block_snapshot
.wrap_snapshot
.tab_snapshot
.fold_snapshot
.inlay_snapshot
.filter_snapshot
}
pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
&self
.block_snapshot
@@ -780,7 +844,8 @@ impl DisplaySnapshot {
.tab_snapshot
.fold_snapshot
.inlay_snapshot
.buffer
.filter_snapshot
.buffer_snapshot
}
#[cfg(test)]
@@ -802,11 +867,15 @@ impl DisplaySnapshot {
pub fn prev_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
loop {
let mut inlay_point = self.inlay_snapshot().to_inlay_point(point);
let mut filter_point = self.filter_snapshot().to_filter_point(point);
let mut inlay_point = self.inlay_snapshot().to_inlay_point(filter_point);
let mut fold_point = self.fold_snapshot().to_fold_point(inlay_point, Bias::Left);
fold_point.0.column = 0;
inlay_point = fold_point.to_inlay_point(self.fold_snapshot());
point = self.inlay_snapshot().to_buffer_point(inlay_point);
filter_point = self.inlay_snapshot().to_filter_point(inlay_point);
point = self
.filter_snapshot()
.to_buffer_point(filter_point, Bias::Left);
let mut display_point = self.point_to_display_point(point, Bias::Left);
*display_point.column_mut() = 0;
@@ -824,11 +893,15 @@ impl DisplaySnapshot {
) -> (MultiBufferPoint, DisplayPoint) {
let original_point = point;
loop {
let mut inlay_point = self.inlay_snapshot().to_inlay_point(point);
let mut filter_point = self.filter_snapshot().to_filter_point(point);
let mut inlay_point = self.inlay_snapshot().to_inlay_point(filter_point);
let mut fold_point = self.fold_snapshot().to_fold_point(inlay_point, Bias::Right);
fold_point.0.column = self.fold_snapshot().line_len(fold_point.row());
inlay_point = fold_point.to_inlay_point(self.fold_snapshot());
point = self.inlay_snapshot().to_buffer_point(inlay_point);
filter_point = self.inlay_snapshot().to_filter_point(inlay_point);
point = self
.filter_snapshot()
.to_buffer_point(filter_point, Bias::Right);
let mut display_point = self.point_to_display_point(point, Bias::Right);
*display_point.column_mut() = self.line_len(display_point.row());
@@ -857,7 +930,8 @@ impl DisplaySnapshot {
}
pub fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
let inlay_point = self.inlay_snapshot().to_inlay_point(point);
let filter_point = self.filter_snapshot().to_filter_point(point);
let inlay_point = self.inlay_snapshot().to_inlay_point(filter_point);
let fold_point = self.fold_snapshot().to_fold_point(inlay_point, bias);
let tab_point = self.tab_snapshot().to_tab_point(fold_point);
let wrap_point = self.wrap_snapshot().tab_point_to_wrap_point(tab_point);
@@ -866,8 +940,9 @@ impl DisplaySnapshot {
}
pub fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
self.inlay_snapshot()
.to_buffer_point(self.display_point_to_inlay_point(point, bias))
let inlay_point = self.display_point_to_inlay_point(point, bias);
let filter_point = self.inlay_snapshot().to_filter_point(inlay_point);
self.filter_snapshot().to_buffer_point(filter_point, bias)
}
pub fn display_point_to_inlay_offset(&self, point: DisplayPoint, bias: Bias) -> InlayOffset {
@@ -876,8 +951,10 @@ impl DisplaySnapshot {
}
pub fn anchor_to_inlay_offset(&self, anchor: Anchor) -> InlayOffset {
self.inlay_snapshot()
.to_inlay_offset(anchor.to_offset(self.buffer_snapshot()))
let filter_offset = self
.filter_snapshot()
.to_filter_offset(anchor.to_offset(self.buffer_snapshot()));
self.inlay_snapshot().to_inlay_offset(filter_offset)
}
pub fn display_point_to_anchor(&self, point: DisplayPoint, bias: Bias) -> Anchor {
@@ -1521,8 +1598,9 @@ impl DisplayPoint {
let tab_point = map.wrap_snapshot().to_tab_point(wrap_point);
let fold_point = map.tab_snapshot().to_fold_point(tab_point, bias).0;
let inlay_point = fold_point.to_inlay_point(map.fold_snapshot());
map.inlay_snapshot()
.to_buffer_offset(map.inlay_snapshot().to_offset(inlay_point))
let filter_point = map.inlay_snapshot().to_filter_point(inlay_point);
map.filter_snapshot()
.to_buffer_offset(map.filter_snapshot().point_to_offset(filter_point), bias)
}
}
@@ -1627,6 +1705,7 @@ pub mod tests {
excerpt_header_height,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -1876,6 +1955,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -1986,6 +2066,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2048,6 +2129,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2145,6 +2227,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2246,6 +2329,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2350,6 +2434,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2439,6 +2524,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2580,6 +2666,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2668,6 +2755,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2793,6 +2881,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
);
let snapshot = map.buffer.read(cx).snapshot(cx);
@@ -2831,6 +2920,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -2907,6 +2997,7 @@ pub mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});

View File

@@ -257,6 +257,16 @@ pub enum BlockId {
ExcerptBoundary(ExcerptId),
FoldedBuffer(ExcerptId),
Custom(CustomBlockId),
Spacer(SpacerId),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct SpacerId(pub usize);
impl From<SpacerId> for EntityId {
fn from(value: SpacerId) -> Self {
EntityId::from(value.0 as u64)
}
}
impl From<BlockId> for ElementId {
@@ -267,6 +277,7 @@ impl From<BlockId> for ElementId {
("ExcerptBoundary", EntityId::from(excerpt_id)).into()
}
BlockId::FoldedBuffer(id) => ("FoldedBuffer", EntityId::from(id)).into(),
BlockId::Spacer(id) => ("Spacer", EntityId::from(id)).into(),
}
}
}
@@ -277,6 +288,7 @@ impl std::fmt::Display for BlockId {
Self::Custom(id) => write!(f, "Block({id:?})"),
Self::ExcerptBoundary(id) => write!(f, "ExcerptHeader({id:?})"),
Self::FoldedBuffer(id) => write!(f, "FoldedBuffer({id:?})"),
Self::Spacer(id) => write!(f, "Spacer({id:?})"),
}
}
}
@@ -302,6 +314,10 @@ pub enum Block {
excerpt: ExcerptInfo,
height: u32,
},
Spacer {
height: u32,
id: SpacerId,
},
}
impl Block {
@@ -317,6 +333,7 @@ impl Block {
excerpt: next_excerpt,
..
} => BlockId::ExcerptBoundary(next_excerpt.id),
Block::Spacer { id, .. } => BlockId::Spacer(*id),
}
}
@@ -325,7 +342,8 @@ impl Block {
Block::Custom(block) => block.height.is_some(),
Block::ExcerptBoundary { .. }
| Block::FoldedBuffer { .. }
| Block::BufferHeader { .. } => true,
| Block::BufferHeader { .. }
| Block::Spacer { .. } => true,
}
}
@@ -334,7 +352,8 @@ impl Block {
Block::Custom(block) => block.height.unwrap_or(0),
Block::ExcerptBoundary { height, .. }
| Block::FoldedBuffer { height, .. }
| Block::BufferHeader { height, .. } => *height,
| Block::BufferHeader { height, .. }
| Block::Spacer { height, .. } => *height,
}
}
@@ -343,7 +362,8 @@ impl Block {
Block::Custom(block) => block.style,
Block::ExcerptBoundary { .. }
| Block::FoldedBuffer { .. }
| Block::BufferHeader { .. } => BlockStyle::Sticky,
| Block::BufferHeader { .. }
| Block::Spacer { .. } => BlockStyle::Sticky,
}
}
@@ -353,6 +373,7 @@ impl Block {
Block::FoldedBuffer { .. } => false,
Block::ExcerptBoundary { .. } => true,
Block::BufferHeader { .. } => true,
Block::Spacer { .. } => false,
}
}
@@ -362,6 +383,7 @@ impl Block {
Block::FoldedBuffer { .. } => false,
Block::ExcerptBoundary { .. } => false,
Block::BufferHeader { .. } => false,
Block::Spacer { .. } => false,
}
}
@@ -374,6 +396,7 @@ impl Block {
Block::FoldedBuffer { .. } => false,
Block::ExcerptBoundary { .. } => false,
Block::BufferHeader { .. } => false,
Block::Spacer { .. } => false,
}
}
@@ -383,6 +406,7 @@ impl Block {
Block::FoldedBuffer { .. } => true,
Block::ExcerptBoundary { .. } => false,
Block::BufferHeader { .. } => false,
Block::Spacer { .. } => true,
}
}
@@ -392,6 +416,7 @@ impl Block {
Block::FoldedBuffer { .. } => true,
Block::ExcerptBoundary { .. } => true,
Block::BufferHeader { .. } => true,
Block::Spacer { .. } => false,
}
}
@@ -401,6 +426,7 @@ impl Block {
Block::FoldedBuffer { .. } => true,
Block::ExcerptBoundary { .. } => false,
Block::BufferHeader { .. } => true,
Block::Spacer { .. } => false,
}
}
}
@@ -427,6 +453,11 @@ impl Debug for Block {
.field("excerpt", excerpt)
.field("height", height)
.finish(),
Self::Spacer { height, id } => f
.debug_struct("Spacer")
.field("height", height)
.field("id", id)
.finish(),
}
}
}
@@ -556,6 +587,7 @@ impl BlockMap {
{
// Preserve the transform (push and next)
new_transforms.push(transform.clone(), ());
// ^ FIXME wrong?
cursor.next();
// Preserve below blocks at end of edit
@@ -745,10 +777,11 @@ impl BlockMap {
}
new_transforms.append(cursor.suffix(), ());
debug_assert_eq!(
new_transforms.summary().input_rows,
wrap_snapshot.max_point().row() + 1
);
// FIXME
// debug_assert_eq!(
// new_transforms.summary().input_rows,
// wrap_snapshot.max_point().row() + 1
// );
drop(cursor);
*transforms = new_transforms;
@@ -1429,31 +1462,42 @@ impl BlockSnapshot {
pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
let buffer = self.wrap_snapshot.buffer_snapshot();
let wrap_point = match block_id {
let wrap_row = match block_id {
BlockId::Custom(custom_block_id) => {
let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
return Some(Block::Custom(custom_block.clone()));
}
BlockId::ExcerptBoundary(next_excerpt_id) => {
let excerpt_range = buffer.range_for_excerpt(next_excerpt_id)?;
self.wrap_snapshot
.make_wrap_point(excerpt_range.start, Bias::Left)
Some(
self.wrap_snapshot
.make_wrap_point(excerpt_range.start, Bias::Left)
.row(),
)
}
BlockId::FoldedBuffer(excerpt_id) => self
.wrap_snapshot
.make_wrap_point(buffer.range_for_excerpt(excerpt_id)?.start, Bias::Left),
BlockId::FoldedBuffer(excerpt_id) => Some(
self.wrap_snapshot
.make_wrap_point(buffer.range_for_excerpt(excerpt_id)?.start, Bias::Left)
.row(),
),
BlockId::Spacer(_) => None,
};
let wrap_row = WrapRow(wrap_point.row());
let mut cursor = self.transforms.cursor::<WrapRow>(());
cursor.seek(&wrap_row, Bias::Left);
if let Some(wrap_row) = wrap_row {
cursor.seek(&WrapRow(wrap_row), Bias::Left);
} else {
cursor.next();
}
while let Some(transform) = cursor.item() {
if let Some(block) = transform.block.as_ref() {
if block.id() == block_id {
return Some(block.clone());
}
} else if *cursor.start() > wrap_row {
} else if let Some(wrap_row) = wrap_row
&& *cursor.start() > WrapRow(wrap_row)
{
break;
}
@@ -1969,7 +2013,10 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
mod tests {
use super::*;
use crate::{
display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap},
display_map::{
filter_map::FilterMap, fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap,
wrap_map::WrapMap,
},
test::test_font,
};
use gpui::{App, AppContext as _, Element, div, font, px};
@@ -2004,7 +2051,8 @@ mod tests {
let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
let (wrap_map, wraps_snapshot) =
@@ -2153,8 +2201,9 @@ mod tests {
buffer.snapshot(cx)
});
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tab_snapshot, tab_edits) =
tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
@@ -2208,13 +2257,14 @@ mod tests {
}
let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, multi_buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
let (_, wrap_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
let snapshot = block_map.read(wraps_snapshot, Default::default());
let block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
let snapshot = block_map.read(wrap_snapshot.clone(), Default::default());
// Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
@@ -2242,7 +2292,8 @@ mod tests {
let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
let (_wrap_map, wraps_snapshot) =
@@ -2341,7 +2392,8 @@ mod tests {
let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
let (_, wraps_snapshot) = cx.update(|cx| {
@@ -2385,7 +2437,8 @@ mod tests {
let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
let tab_size = 1.try_into().unwrap();
let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
@@ -2412,8 +2465,9 @@ mod tests {
buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
buffer.snapshot(cx)
});
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
@@ -2433,10 +2487,11 @@ mod tests {
);
buffer.snapshot(cx)
});
let (inlay_snapshot, inlay_edits) = inlay_map.sync(
let (filter_snapshot, filter_edits) = filter_map.sync(
buffer_snapshot.clone(),
buffer_subscription.consume().into_inner(),
);
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
@@ -2551,7 +2606,8 @@ mod tests {
let buffer_id_2 = buffer_ids[1];
let buffer_id_3 = buffer_ids[2];
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
let (_, wrap_snapshot) =
@@ -2896,7 +2952,8 @@ mod tests {
assert_eq!(buffer_ids.len(), 1);
let buffer_id = buffer_ids[0];
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
let (_, wrap_snapshot) =
@@ -2971,7 +3028,8 @@ mod tests {
};
let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
let font = test_font();
@@ -3028,8 +3086,10 @@ mod tests {
})
.collect::<Vec<_>>();
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot.clone(), vec![]);
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), vec![]);
inlay_map.sync(filter_snapshot, filter_edits);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tab_snapshot, tab_edits) =
tab_map.sync(fold_snapshot, fold_edits, tab_size);
@@ -3066,8 +3126,9 @@ mod tests {
.map(|block| block.id)
.collect::<HashSet<_>>();
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), vec![]);
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot.clone(), vec![]);
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, vec![]);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tab_snapshot, tab_edits) =
tab_map.sync(fold_snapshot, fold_edits, tab_size);
@@ -3087,8 +3148,10 @@ mod tests {
log::info!("Noop fold/unfold operation on a singleton buffer");
continue;
}
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot.clone(), vec![]);
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), vec![]);
inlay_map.sync(filter_snapshot.clone(), vec![]);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tab_snapshot, tab_edits) =
tab_map.sync(fold_snapshot, fold_edits, tab_size);
@@ -3175,8 +3238,9 @@ mod tests {
}
}
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot.clone(), buffer_edits);
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
@@ -3552,7 +3616,8 @@ mod tests {
let text = "abc\ndef\nghi\njkl\nmno";
let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
let (_wrap_map, wraps_snapshot) =
@@ -3602,7 +3667,8 @@ mod tests {
assert_eq!(buffer_ids.len(), 1);
let buffer_id = buffer_ids[0];
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
let (_, wrap_snapshot) =
@@ -3647,7 +3713,8 @@ mod tests {
assert_eq!(buffer_ids.len(), 1);
let buffer_id = buffer_ids[0];
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
let (_, wrap_snapshot) =

File diff suppressed because it is too large Load Diff

View File

@@ -145,7 +145,7 @@ impl FoldMapWriter<'_> {
let mut folds = Vec::new();
let snapshot = self.0.snapshot.inlay_snapshot.clone();
for (range, fold_text) in ranges.into_iter() {
let buffer = &snapshot.buffer;
let buffer = &snapshot.filter_snapshot.buffer_snapshot;
let range = range.start.to_offset(buffer)..range.end.to_offset(buffer);
// Ignore any empty ranges.
@@ -159,6 +159,7 @@ impl FoldMapWriter<'_> {
if fold_range.0.start.excerpt_id != fold_range.0.end.excerpt_id {
continue;
}
// FIXME we should ignore any folds that start or end in a filtered region
folds.push(Fold {
id: FoldId(post_inc(&mut self.0.next_fold_id.0)),
@@ -166,15 +167,17 @@ impl FoldMapWriter<'_> {
placeholder: fold_text,
});
let inlay_range =
snapshot.to_inlay_offset(range.start)..snapshot.to_inlay_offset(range.end);
let filter_range = snapshot.filter_snapshot.to_filter_offset(range.start)
..snapshot.filter_snapshot.to_filter_offset(range.end);
let inlay_range = snapshot.to_inlay_offset(filter_range.start)
..snapshot.to_inlay_offset(filter_range.end);
edits.push(InlayEdit {
old: inlay_range.clone(),
new: inlay_range,
});
}
let buffer = &snapshot.buffer;
let buffer = &snapshot.filter_snapshot.buffer_snapshot;
folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer));
self.0.snapshot.folds = {
@@ -233,7 +236,7 @@ impl FoldMapWriter<'_> {
let mut edits = Vec::new();
let mut fold_ixs_to_delete = Vec::new();
let snapshot = self.0.snapshot.inlay_snapshot.clone();
let buffer = &snapshot.buffer;
let buffer = &snapshot.filter_snapshot.buffer_snapshot;
for range in ranges.into_iter() {
let range = range.start.to_offset(buffer)..range.end.to_offset(buffer);
let mut folds_cursor =
@@ -241,10 +244,14 @@ impl FoldMapWriter<'_> {
while let Some(fold) = folds_cursor.item() {
let offset_range =
fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer);
let filter_range = snapshot
.filter_snapshot
.to_filter_offset(offset_range.start)
..snapshot.filter_snapshot.to_filter_offset(offset_range.end);
if should_unfold(fold) {
if offset_range.end > offset_range.start {
let inlay_range = snapshot.to_inlay_offset(offset_range.start)
..snapshot.to_inlay_offset(offset_range.end);
let inlay_range = snapshot.to_inlay_offset(filter_range.start)
..snapshot.to_inlay_offset(filter_range.end);
edits.push(InlayEdit {
old: inlay_range.clone(),
new: inlay_range,
@@ -282,7 +289,7 @@ impl FoldMapWriter<'_> {
) -> (FoldSnapshot, Vec<FoldEdit>) {
let mut edits = Vec::new();
let inlay_snapshot = self.0.snapshot.inlay_snapshot.clone();
let buffer = &inlay_snapshot.buffer;
let buffer = &inlay_snapshot.filter_snapshot.buffer_snapshot;
for (id, new_width) in new_widths {
let ChunkRendererId::Fold(id) = id else {
@@ -293,8 +300,12 @@ impl FoldMapWriter<'_> {
{
let buffer_start = metadata.range.start.to_offset(buffer);
let buffer_end = metadata.range.end.to_offset(buffer);
let inlay_range = inlay_snapshot.to_inlay_offset(buffer_start)
..inlay_snapshot.to_inlay_offset(buffer_end);
let filter_range = inlay_snapshot
.filter_snapshot
.to_filter_offset(buffer_start)
..inlay_snapshot.filter_snapshot.to_filter_offset(buffer_end);
let inlay_range = inlay_snapshot.to_inlay_offset(filter_range.start)
..inlay_snapshot.to_inlay_offset(filter_range.end);
edits.push(InlayEdit {
old: inlay_range.clone(),
new: inlay_range.clone(),
@@ -328,7 +339,7 @@ impl FoldMap {
pub fn new(inlay_snapshot: InlaySnapshot) -> (Self, FoldSnapshot) {
let this = Self {
snapshot: FoldSnapshot {
folds: SumTree::new(&inlay_snapshot.buffer),
folds: SumTree::new(&inlay_snapshot.filter_snapshot.buffer_snapshot),
transforms: SumTree::from_item(
Transform {
summary: TransformSummary {
@@ -461,25 +472,41 @@ impl FoldMap {
edit.new.end =
InlayOffset(((edit.new.start + edit.old_len()).0 as isize + delta) as usize);
let filter_offset = inlay_snapshot.to_filter_offset(edit.new.start);
let anchor = inlay_snapshot
.buffer
.anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start));
.filter_snapshot
.buffer_snapshot
.anchor_before(
inlay_snapshot
.filter_snapshot
.to_buffer_offset(filter_offset, Bias::Left),
);
let mut folds_cursor = self
.snapshot
.folds
.cursor::<FoldRange>(&inlay_snapshot.buffer);
.cursor::<FoldRange>(&inlay_snapshot.filter_snapshot.buffer_snapshot);
folds_cursor.seek(&FoldRange(anchor..Anchor::max()), Bias::Left);
let mut folds = iter::from_fn({
let inlay_snapshot = &inlay_snapshot;
move || {
let item = folds_cursor.item().map(|fold| {
let buffer_start = fold.range.start.to_offset(&inlay_snapshot.buffer);
let buffer_end = fold.range.end.to_offset(&inlay_snapshot.buffer);
let buffer_start = fold
.range
.start
.to_offset(&inlay_snapshot.filter_snapshot.buffer_snapshot);
let buffer_end = fold
.range
.end
.to_offset(&inlay_snapshot.filter_snapshot.buffer_snapshot);
let filter_range = inlay_snapshot
.filter_snapshot
.to_filter_offset(buffer_start)
..inlay_snapshot.filter_snapshot.to_filter_offset(buffer_end);
(
fold.clone(),
inlay_snapshot.to_inlay_offset(buffer_start)
..inlay_snapshot.to_inlay_offset(buffer_end),
inlay_snapshot.to_inlay_offset(filter_range.start)
..inlay_snapshot.to_inlay_offset(filter_range.end),
)
});
folds_cursor.next();
@@ -632,7 +659,7 @@ pub struct FoldSnapshot {
impl FoldSnapshot {
pub fn buffer(&self) -> &MultiBufferSnapshot {
&self.inlay_snapshot.buffer
&self.inlay_snapshot.filter_snapshot.buffer_snapshot
}
fn fold_width(&self, fold_id: &FoldId) -> Option<Pixels> {
@@ -648,7 +675,9 @@ impl FoldSnapshot {
#[cfg(test)]
pub fn fold_count(&self) -> usize {
self.folds.items(&self.inlay_snapshot.buffer).len()
self.folds
.items(&self.inlay_snapshot.filter_snapshot.buffer_snapshot)
.len()
}
pub fn text_summary_for_range(&self, range: Range<FoldPoint>) -> TextSummary {
@@ -769,7 +798,7 @@ impl FoldSnapshot {
where
T: ToOffset,
{
let buffer = &self.inlay_snapshot.buffer;
let buffer = &self.inlay_snapshot.filter_snapshot.buffer_snapshot;
let range = range.start.to_offset(buffer)..range.end.to_offset(buffer);
let mut folds = intersecting_folds(&self.inlay_snapshot, &self.folds, range, false);
iter::from_fn(move || {
@@ -783,8 +812,12 @@ impl FoldSnapshot {
where
T: ToOffset,
{
let buffer_offset = offset.to_offset(&self.inlay_snapshot.buffer);
let inlay_offset = self.inlay_snapshot.to_inlay_offset(buffer_offset);
let buffer_offset = offset.to_offset(&self.inlay_snapshot.filter_snapshot.buffer_snapshot);
let filter_offset = self
.inlay_snapshot
.filter_snapshot
.to_filter_offset(buffer_offset);
let inlay_offset = self.inlay_snapshot.to_inlay_offset(filter_offset);
let (_, _, item) = self
.transforms
.find::<InlayOffset, _>((), &inlay_offset, Bias::Right);
@@ -792,15 +825,22 @@ impl FoldSnapshot {
}
pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
let mut inlay_point = self
let filter_point = self
.inlay_snapshot
.to_inlay_point(Point::new(buffer_row.0, 0));
.filter_snapshot
.to_filter_point(Point::new(buffer_row.0, 0));
let mut inlay_point = self.inlay_snapshot.to_inlay_point(filter_point);
let mut cursor = self.transforms.cursor::<InlayPoint>(());
cursor.seek(&inlay_point, Bias::Right);
loop {
match cursor.item() {
Some(transform) => {
let buffer_point = self.inlay_snapshot.to_buffer_point(inlay_point);
let filter_point = self.inlay_snapshot.to_filter_point(inlay_point);
// FIXME unclear what bias to use here
let buffer_point = self
.inlay_snapshot
.filter_snapshot
.to_buffer_point(filter_point, Bias::Left);
if buffer_point.row != buffer_row.0 {
return false;
} else if transform.placeholder.is_some() {
@@ -945,7 +985,7 @@ fn intersecting_folds<'a>(
range: Range<usize>,
inclusive: bool,
) -> FilterCursor<'a, 'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, usize> {
let buffer = &inlay_snapshot.buffer;
let buffer = &inlay_snapshot.filter_snapshot.buffer_snapshot;
let start = buffer.anchor_before(range.start.to_offset(buffer));
let end = buffer.anchor_after(range.end.to_offset(buffer));
let mut cursor = folds.filter::<_, usize>(buffer, move |summary| {
@@ -1559,6 +1599,7 @@ pub type FoldEdit = Edit<FoldOffset>;
#[cfg(test)]
mod tests {
use super::*;
use crate::display_map::filter_map::FilterMap;
use crate::{MultiBuffer, ToPoint, display_map::inlay_map::InlayMap};
use Bias::{Left, Right};
use collections::HashSet;
@@ -1575,7 +1616,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let mut map = FoldMap::new(inlay_snapshot.clone()).0;
let (mut writer, _, _) = map.write(inlay_snapshot, vec![]);
@@ -1610,8 +1652,9 @@ mod tests {
buffer.snapshot(cx)
});
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
let (snapshot3, edits) = map.read(inlay_snapshot, inlay_edits);
assert_eq!(snapshot3.text(), "123a⋯c123c⋯eeeee");
assert_eq!(
@@ -1632,8 +1675,9 @@ mod tests {
buffer.edit([(Point::new(2, 6)..Point::new(4, 3), "456")], None, cx);
buffer.snapshot(cx)
});
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
let (snapshot4, _) = map.read(inlay_snapshot.clone(), inlay_edits);
assert_eq!(snapshot4.text(), "123a⋯c123456eee");
@@ -1654,7 +1698,8 @@ mod tests {
let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
{
let mut map = FoldMap::new(inlay_snapshot.clone()).0;
@@ -1700,8 +1745,9 @@ mod tests {
buffer.edit([(0..1, "12345")], None, cx);
buffer.snapshot(cx)
});
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
let (snapshot, _) = map.read(inlay_snapshot, inlay_edits);
assert_eq!(snapshot.text(), "12345⋯fghijkl");
}
@@ -1711,7 +1757,8 @@ mod tests {
fn test_overlapping_folds(cx: &mut gpui::App) {
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let mut map = FoldMap::new(inlay_snapshot.clone()).0;
let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
writer.fold(vec![
@@ -1730,7 +1777,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let mut map = FoldMap::new(inlay_snapshot.clone()).0;
let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
@@ -1745,8 +1793,9 @@ mod tests {
buffer.edit([(Point::new(2, 2)..Point::new(3, 1), "")], None, cx);
buffer.snapshot(cx)
});
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot, subscription.consume().into_inner());
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
let (snapshot, _) = map.read(inlay_snapshot, inlay_edits);
assert_eq!(snapshot.text(), "aa⋯eeeee");
}
@@ -1755,7 +1804,8 @@ mod tests {
fn test_folds_in_range(cx: &mut gpui::App) {
let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let mut map = FoldMap::new(inlay_snapshot.clone()).0;
let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
@@ -1797,7 +1847,8 @@ mod tests {
MultiBuffer::build_random(&mut rng, cx)
};
let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let mut map = FoldMap::new(inlay_snapshot.clone()).0;
let (mut initial_snapshot, _) = map.read(inlay_snapshot, vec![]);
@@ -1827,8 +1878,10 @@ mod tests {
}),
};
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot.clone(), buffer_edits);
let (inlay_snapshot, new_inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
inlay_map.sync(filter_snapshot.clone(), filter_edits);
log::info!("inlay text {:?}", inlay_snapshot.text());
let inlay_edits = Patch::new(inlay_edits)
@@ -1839,9 +1892,11 @@ mod tests {
let mut expected_text: String = inlay_snapshot.text().to_string();
for fold_range in map.merged_folds().into_iter().rev() {
let fold_inlay_start = inlay_snapshot.to_inlay_offset(fold_range.start);
let fold_inlay_end = inlay_snapshot.to_inlay_offset(fold_range.end);
expected_text.replace_range(fold_inlay_start.0..fold_inlay_end.0, "");
let fold_filter_range = filter_snapshot.to_filter_offset(fold_range.start)
..filter_snapshot.to_filter_offset(fold_range.end);
let fold_inlay_range = inlay_snapshot.to_inlay_offset(fold_filter_range.start)
..inlay_snapshot.to_inlay_offset(fold_filter_range.end);
expected_text.replace_range(fold_inlay_range.start.0..fold_inlay_range.end.0, "");
}
assert_eq!(snapshot.text(), expected_text);
@@ -1854,18 +1909,20 @@ mod tests {
let mut prev_row = 0;
let mut expected_buffer_rows = Vec::new();
for fold_range in map.merged_folds() {
let fold_start = inlay_snapshot
.to_point(inlay_snapshot.to_inlay_offset(fold_range.start))
.row();
let fold_end = inlay_snapshot
.to_point(inlay_snapshot.to_inlay_offset(fold_range.end))
.row();
let fold_filter_range = filter_snapshot.to_filter_offset(fold_range.start)
..filter_snapshot.to_filter_offset(fold_range.end);
let fold_row_range = inlay_snapshot
.to_point(inlay_snapshot.to_inlay_offset(fold_filter_range.start))
.row()
..inlay_snapshot
.to_point(inlay_snapshot.to_inlay_offset(fold_filter_range.end))
.row();
expected_buffer_rows.extend(
inlay_snapshot
.row_infos(prev_row)
.take((1 + fold_start - prev_row) as usize),
.take((1 + fold_row_range.start - prev_row) as usize),
);
prev_row = 1 + fold_end;
prev_row = 1 + fold_row_range.end;
}
expected_buffer_rows.extend(inlay_snapshot.row_infos(prev_row));
@@ -2063,7 +2120,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(&text, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let mut map = FoldMap::new(inlay_snapshot.clone()).0;
let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
@@ -2105,7 +2163,8 @@ mod tests {
MultiBuffer::build_random(&mut rng, cx)
};
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (mut fold_map, _) = FoldMap::new(inlay_snapshot.clone());
// Perform random mutations
@@ -2189,7 +2248,7 @@ mod tests {
impl FoldMap {
fn merged_folds(&self) -> Vec<Range<usize>> {
let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
let buffer = &inlay_snapshot.buffer;
let buffer = &inlay_snapshot.filter_snapshot.buffer_snapshot;
let mut folds = self.snapshot.folds.items(buffer);
// Ensure sorting doesn't change how folds get merged and displayed.
folds.sort_by(|a, b| a.range.cmp(&b.range, buffer));
@@ -2225,7 +2284,7 @@ mod tests {
match rng.random_range(0..=100) {
0..=39 if !self.snapshot.folds.is_empty() => {
let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
let buffer = &inlay_snapshot.buffer;
let buffer = &inlay_snapshot.filter_snapshot.buffer_snapshot;
let mut to_unfold = Vec::new();
for _ in 0..rng.random_range(1..=3) {
let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
@@ -2241,7 +2300,7 @@ mod tests {
}
_ => {
let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
let buffer = &inlay_snapshot.buffer;
let buffer = &inlay_snapshot.filter_snapshot.buffer_snapshot;
let mut to_fold = Vec::new();
for _ in 0..rng.random_range(1..=2) {
let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);

View File

@@ -1,10 +1,13 @@
use crate::{
ChunkRenderer, HighlightStyles,
display_map::filter_map::{
FilterChunks, FilterOffset, FilterPoint, FilterRow, FilterRows, FilterSnapshot,
},
inlays::{Inlay, InlayContent},
};
use collections::BTreeSet;
use language::{Chunk, Edit, Point, TextSummary};
use multi_buffer::{MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, RowInfo, ToOffset};
use multi_buffer::{MultiBufferRow, MultiBufferRows, RowInfo, ToOffset};
use project::InlayId;
use std::{
cmp,
@@ -27,7 +30,7 @@ pub struct InlayMap {
#[derive(Clone)]
pub struct InlaySnapshot {
pub buffer: MultiBufferSnapshot,
pub filter_snapshot: FilterSnapshot,
transforms: SumTree<Transform>,
pub version: usize,
}
@@ -144,37 +147,37 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
}
}
impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
impl<'a> sum_tree::Dimension<'a, TransformSummary> for FilterOffset {
fn zero(_cx: ()) -> Self {
Default::default()
}
fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
*self += &summary.input.len;
self.0 += &summary.input.len;
}
}
impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
impl<'a> sum_tree::Dimension<'a, TransformSummary> for FilterPoint {
fn zero(_cx: ()) -> Self {
Default::default()
}
fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
*self += &summary.input.lines;
self.0 += &summary.input.lines;
}
}
#[derive(Clone)]
pub struct InlayBufferRows<'a> {
transforms: Cursor<'a, 'static, Transform, Dimensions<InlayPoint, Point>>,
buffer_rows: MultiBufferRows<'a>,
transforms: Cursor<'a, 'static, Transform, Dimensions<InlayPoint, FilterPoint>>,
filter_rows: FilterRows<'a>,
inlay_row: u32,
max_buffer_row: MultiBufferRow,
max_filter_row: FilterRow,
}
pub struct InlayChunks<'a> {
transforms: Cursor<'a, 'static, Transform, Dimensions<InlayOffset, usize>>,
buffer_chunks: CustomHighlightsChunks<'a>,
transforms: Cursor<'a, 'static, Transform, Dimensions<InlayOffset, FilterOffset>>,
filter_chunks: FilterChunks<'a>,
buffer_chunk: Option<Chunk<'a>>,
inlay_chunks: Option<text::ChunkWithBitmaps<'a>>,
/// text, char bitmap, tabs bitmap
@@ -197,9 +200,9 @@ impl InlayChunks<'_> {
pub fn seek(&mut self, new_range: Range<InlayOffset>) {
self.transforms.seek(&new_range.start, Bias::Right);
let buffer_range = self.snapshot.to_buffer_offset(new_range.start)
..self.snapshot.to_buffer_offset(new_range.end);
self.buffer_chunks.seek(buffer_range);
let filter_range = self.snapshot.to_filter_offset(new_range.start)
..self.snapshot.to_filter_offset(new_range.end);
self.filter_chunks.seek(filter_range);
self.inlay_chunks = None;
self.buffer_chunk = None;
self.output_offset = new_range.start;
@@ -223,9 +226,9 @@ impl<'a> Iterator for InlayChunks<'a> {
Transform::Isomorphic(_) => {
let chunk = self
.buffer_chunk
.get_or_insert_with(|| self.buffer_chunks.next().unwrap());
.get_or_insert_with(|| self.filter_chunks.next().unwrap());
if chunk.text.is_empty() {
*chunk = self.buffer_chunks.next().unwrap();
*chunk = self.filter_chunks.next().unwrap();
}
let desired_bytes = self.transforms.end().0.0 - self.output_offset.0;
@@ -415,20 +418,20 @@ impl InlayBufferRows<'_> {
let inlay_point = InlayPoint::new(row, 0);
self.transforms.seek(&inlay_point, Bias::Left);
let mut buffer_point = self.transforms.start().1;
let buffer_row = MultiBufferRow(if row == 0 {
let mut filter_point = self.transforms.start().1;
let filter_row = if row == 0 {
0
} else {
match self.transforms.item() {
Some(Transform::Isomorphic(_)) => {
buffer_point += inlay_point.0 - self.transforms.start().0.0;
buffer_point.row
filter_point += FilterPoint(inlay_point.0 - self.transforms.start().0.0);
filter_point.0.row
}
_ => cmp::min(buffer_point.row + 1, self.max_buffer_row.0),
_ => cmp::min(filter_point.0.row + 1, self.max_filter_row.0),
}
});
};
self.inlay_row = inlay_point.row();
self.buffer_rows.seek(buffer_row);
self.filter_rows.seek(FilterRow(filter_row));
}
}
@@ -437,11 +440,11 @@ impl Iterator for InlayBufferRows<'_> {
fn next(&mut self) -> Option<Self::Item> {
let buffer_row = if self.inlay_row == 0 {
self.buffer_rows.next().unwrap()
self.filter_rows.next().unwrap()
} else {
match self.transforms.item()? {
Transform::Inlay(_) => Default::default(),
Transform::Isomorphic(_) => self.buffer_rows.next().unwrap(),
Transform::Isomorphic(_) => self.filter_rows.next().unwrap(),
}
};
@@ -464,11 +467,14 @@ impl InlayPoint {
}
impl InlayMap {
pub fn new(buffer: MultiBufferSnapshot) -> (Self, InlaySnapshot) {
pub fn new(filter_snapshot: FilterSnapshot) -> (Self, InlaySnapshot) {
let version = 0;
let snapshot = InlaySnapshot {
buffer: buffer.clone(),
transforms: SumTree::from_iter(Some(Transform::Isomorphic(buffer.text_summary())), ()),
filter_snapshot: filter_snapshot.clone(),
transforms: SumTree::from_iter(
Some(Transform::Isomorphic(filter_snapshot.text_summary())),
(),
),
version,
};
@@ -483,136 +489,126 @@ impl InlayMap {
pub fn sync(
&mut self,
buffer_snapshot: MultiBufferSnapshot,
mut buffer_edits: Vec<text::Edit<usize>>,
filter_snapshot: FilterSnapshot,
filter_edits: Vec<text::Edit<FilterOffset>>,
) -> (InlaySnapshot, Vec<InlayEdit>) {
let snapshot = &mut self.snapshot;
if buffer_edits.is_empty()
&& snapshot.buffer.trailing_excerpt_update_count()
!= buffer_snapshot.trailing_excerpt_update_count()
{
buffer_edits.push(Edit {
old: snapshot.buffer.len()..snapshot.buffer.len(),
new: buffer_snapshot.len()..buffer_snapshot.len(),
});
}
if buffer_edits.is_empty() {
if snapshot.buffer.edit_count() != buffer_snapshot.edit_count()
|| snapshot.buffer.non_text_state_update_count()
!= buffer_snapshot.non_text_state_update_count()
|| snapshot.buffer.trailing_excerpt_update_count()
!= buffer_snapshot.trailing_excerpt_update_count()
{
if filter_edits.is_empty() {
if snapshot.filter_snapshot.version != filter_snapshot.version {
snapshot.version += 1;
}
snapshot.buffer = buffer_snapshot;
(snapshot.clone(), Vec::new())
} else {
let mut inlay_edits = Patch::default();
let mut new_transforms = SumTree::default();
let mut cursor = snapshot
.transforms
.cursor::<Dimensions<usize, InlayOffset>>(());
let mut buffer_edits_iter = buffer_edits.iter().peekable();
while let Some(buffer_edit) = buffer_edits_iter.next() {
new_transforms.append(cursor.slice(&buffer_edit.old.start, Bias::Left), ());
if let Some(Transform::Isomorphic(transform)) = cursor.item()
&& cursor.end().0 == buffer_edit.old.start
{
push_isomorphic(&mut new_transforms, *transform);
cursor.next();
}
// Remove all the inlays and transforms contained by the edit.
let old_start =
cursor.start().1 + InlayOffset(buffer_edit.old.start - cursor.start().0);
cursor.seek(&buffer_edit.old.end, Bias::Right);
let old_end =
cursor.start().1 + InlayOffset(buffer_edit.old.end - cursor.start().0);
// Push the unchanged prefix.
let prefix_start = new_transforms.summary().input.len;
let prefix_end = buffer_edit.new.start;
push_isomorphic(
&mut new_transforms,
buffer_snapshot.text_summary_for_range(prefix_start..prefix_end),
);
let new_start = InlayOffset(new_transforms.summary().output.len);
let start_ix = match self.inlays.binary_search_by(|probe| {
probe
.position
.to_offset(&buffer_snapshot)
.cmp(&buffer_edit.new.start)
.then(std::cmp::Ordering::Greater)
}) {
Ok(ix) | Err(ix) => ix,
};
for inlay in &self.inlays[start_ix..] {
if !inlay.position.is_valid(&buffer_snapshot) {
continue;
}
let buffer_offset = inlay.position.to_offset(&buffer_snapshot);
if buffer_offset > buffer_edit.new.end {
break;
}
let prefix_start = new_transforms.summary().input.len;
let prefix_end = buffer_offset;
push_isomorphic(
&mut new_transforms,
buffer_snapshot.text_summary_for_range(prefix_start..prefix_end),
);
new_transforms.push(Transform::Inlay(inlay.clone()), ());
}
// Apply the rest of the edit.
let transform_start = new_transforms.summary().input.len;
push_isomorphic(
&mut new_transforms,
buffer_snapshot.text_summary_for_range(transform_start..buffer_edit.new.end),
);
let new_end = InlayOffset(new_transforms.summary().output.len);
inlay_edits.push(Edit {
old: old_start..old_end,
new: new_start..new_end,
});
// If the next edit doesn't intersect the current isomorphic transform, then
// we can push its remainder.
if buffer_edits_iter
.peek()
.is_none_or(|edit| edit.old.start >= cursor.end().0)
{
let transform_start = new_transforms.summary().input.len;
let transform_end =
buffer_edit.new.end + (cursor.end().0 - buffer_edit.old.end);
push_isomorphic(
&mut new_transforms,
buffer_snapshot.text_summary_for_range(transform_start..transform_end),
);
cursor.next();
}
}
new_transforms.append(cursor.suffix(), ());
if new_transforms.is_empty() {
new_transforms.push(Transform::Isomorphic(Default::default()), ());
}
drop(cursor);
snapshot.transforms = new_transforms;
snapshot.version += 1;
snapshot.buffer = buffer_snapshot;
snapshot.check_invariants();
(snapshot.clone(), inlay_edits.into_inner())
snapshot.filter_snapshot = filter_snapshot;
return (snapshot.clone(), Vec::new());
}
let mut inlay_edits = Patch::default();
let mut new_transforms = SumTree::default();
let mut cursor = snapshot
.transforms
.cursor::<Dimensions<FilterOffset, InlayOffset>>(());
let mut filter_edits_iter = filter_edits.iter().peekable();
while let Some(filter_edit) = filter_edits_iter.next() {
new_transforms.append(cursor.slice(&filter_edit.old.start, Bias::Left), ());
if let Some(Transform::Isomorphic(transform)) = cursor.item()
&& cursor.end().0 == filter_edit.old.start
{
push_isomorphic(&mut new_transforms, *transform);
cursor.next();
}
// Remove all the inlays and transforms contained by the edit.
let old_start =
cursor.start().1 + InlayOffset(filter_edit.old.start.0 - cursor.start().0.0);
cursor.seek(&filter_edit.old.end, Bias::Right);
let old_end =
cursor.start().1 + InlayOffset(filter_edit.old.end.0 - cursor.start().0.0);
// Push the unchanged prefix.
let prefix_start = new_transforms.summary().input.len;
let prefix_end = filter_edit.new.start;
push_isomorphic(
&mut new_transforms,
filter_snapshot.text_summary_for_range(FilterOffset(prefix_start)..prefix_end),
);
let new_start = InlayOffset(new_transforms.summary().output.len);
let start_ix = match self.inlays.binary_search_by(|probe| {
let buffer_position = probe.position.to_offset(&filter_snapshot.buffer_snapshot);
filter_snapshot
.to_filter_offset(buffer_position)
.cmp(&filter_edit.new.start)
.then(std::cmp::Ordering::Greater)
}) {
Ok(ix) | Err(ix) => ix,
};
for inlay in &self.inlays[start_ix..] {
if !inlay.position.is_valid(&filter_snapshot.buffer_snapshot) {
continue;
}
if filter_snapshot.is_anchor_filtered(inlay.position) {
continue;
}
let buffer_offset = inlay.position.to_offset(&filter_snapshot.buffer_snapshot);
let filter_offset = filter_snapshot.to_filter_offset(buffer_offset);
if filter_offset > filter_edit.new.end {
break;
}
let prefix_start = new_transforms.summary().input.len;
let prefix_end = buffer_offset;
push_isomorphic(
&mut new_transforms,
filter_snapshot.text_summary_for_range(
FilterOffset(prefix_start)..FilterOffset(prefix_end),
),
);
new_transforms.push(Transform::Inlay(inlay.clone()), ());
}
// Apply the rest of the edit.
let transform_start = new_transforms.summary().input.len;
push_isomorphic(
&mut new_transforms,
filter_snapshot
.text_summary_for_range(FilterOffset(transform_start)..filter_edit.new.end),
);
let new_end = InlayOffset(new_transforms.summary().output.len);
inlay_edits.push(Edit {
old: old_start..old_end,
new: new_start..new_end,
});
// If the next edit doesn't intersect the current isomorphic transform, then
// we can push its remainder.
if filter_edits_iter
.peek()
.is_none_or(|edit| edit.old.start >= cursor.end().0)
{
let transform_start = FilterOffset(new_transforms.summary().input.len);
let transform_end = filter_edit.new.end + (cursor.end().0 - filter_edit.old.end);
push_isomorphic(
&mut new_transforms,
filter_snapshot.text_summary_for_range(transform_start..transform_end),
);
cursor.next();
}
}
new_transforms.append(cursor.suffix(), ());
if new_transforms.is_empty() {
new_transforms.push(Transform::Isomorphic(Default::default()), ());
}
drop(cursor);
snapshot.transforms = new_transforms;
snapshot.version += 1;
snapshot.filter_snapshot = filter_snapshot;
snapshot.check_invariants();
(snapshot.clone(), inlay_edits.into_inner())
}
pub fn splice(
@@ -626,8 +622,11 @@ impl InlayMap {
self.inlays.retain(|inlay| {
let retain = !to_remove.contains(&inlay.id);
if !retain {
let offset = inlay.position.to_offset(&snapshot.buffer);
edits.insert(offset);
let buffer_offset = inlay
.position
.to_offset(&snapshot.filter_snapshot.buffer_snapshot);
let filter_offset = snapshot.filter_snapshot.to_filter_offset(buffer_offset);
edits.insert(filter_offset);
}
retain
});
@@ -638,11 +637,17 @@ impl InlayMap {
continue;
}
let offset = inlay_to_insert.position.to_offset(&snapshot.buffer);
let buffer_offset = inlay_to_insert
.position
.to_offset(&snapshot.filter_snapshot.buffer_snapshot);
let filter_offset = snapshot.filter_snapshot.to_filter_offset(buffer_offset);
match self.inlays.binary_search_by(|probe| {
probe
.position
.cmp(&inlay_to_insert.position, &snapshot.buffer)
.cmp(
&inlay_to_insert.position,
&snapshot.filter_snapshot.buffer_snapshot,
)
.then(std::cmp::Ordering::Less)
}) {
Ok(ix) | Err(ix) => {
@@ -650,18 +655,18 @@ impl InlayMap {
}
}
edits.insert(offset);
edits.insert(filter_offset);
}
let buffer_edits = edits
let filter_edits = edits
.into_iter()
.map(|offset| Edit {
old: offset..offset,
new: offset..offset,
})
.collect();
let buffer_snapshot = snapshot.buffer.clone();
let (snapshot, edits) = self.sync(buffer_snapshot, buffer_edits);
let filter_snapshot = snapshot.filter_snapshot.clone();
let (snapshot, edits) = self.sync(filter_snapshot, filter_edits);
(snapshot, edits)
}
@@ -683,7 +688,11 @@ impl InlayMap {
let snapshot = &mut self.snapshot;
for i in 0..rng.random_range(1..=5) {
if self.inlays.is_empty() || rng.random() {
let position = snapshot.buffer.random_byte_range(0, rng).start;
let position = snapshot
.filter_snapshot
.buffer_snapshot
.random_byte_range(0, rng)
.start;
let bias = if rng.random() {
Bias::Left
} else {
@@ -702,13 +711,19 @@ impl InlayMap {
let next_inlay = if i % 2 == 0 {
Inlay::mock_hint(
post_inc(next_inlay_id),
snapshot.buffer.anchor_at(position, bias),
snapshot
.filter_snapshot
.buffer_snapshot
.anchor_at(position, bias),
&text,
)
} else {
Inlay::edit_prediction(
post_inc(next_inlay_id),
snapshot.buffer.anchor_at(position, bias),
snapshot
.filter_snapshot
.buffer_snapshot
.anchor_at(position, bias),
&text,
)
};
@@ -738,15 +753,15 @@ impl InlaySnapshot {
pub fn to_point(&self, offset: InlayOffset) -> InlayPoint {
let (start, _, item) = self
.transforms
.find::<Dimensions<InlayOffset, InlayPoint, usize>, _>((), &offset, Bias::Right);
.find::<Dimensions<InlayOffset, InlayPoint, FilterOffset>, _>((), &offset, Bias::Right);
let overshoot = offset.0 - start.0.0;
match item {
Some(Transform::Isomorphic(_)) => {
let buffer_offset_start = start.2;
let buffer_offset_end = buffer_offset_start + overshoot;
let buffer_start = self.buffer.offset_to_point(buffer_offset_start);
let buffer_end = self.buffer.offset_to_point(buffer_offset_end);
InlayPoint(start.1.0 + (buffer_end - buffer_start))
let filter_offset_start = start.2;
let filter_offset_end = filter_offset_start + FilterOffset(overshoot);
let filter_start = self.filter_snapshot.offset_to_point(filter_offset_start);
let filter_end = self.filter_snapshot.offset_to_point(filter_offset_end);
InlayPoint(start.1.0 + (filter_end - filter_start).0)
}
Some(Transform::Inlay(inlay)) => {
let overshoot = inlay.text().offset_to_point(overshoot);
@@ -767,15 +782,15 @@ impl InlaySnapshot {
pub fn to_offset(&self, point: InlayPoint) -> InlayOffset {
let (start, _, item) = self
.transforms
.find::<Dimensions<InlayPoint, InlayOffset, Point>, _>((), &point, Bias::Right);
.find::<Dimensions<InlayPoint, InlayOffset, FilterPoint>, _>((), &point, Bias::Right);
let overshoot = point.0 - start.0.0;
match item {
Some(Transform::Isomorphic(_)) => {
let buffer_point_start = start.2;
let buffer_point_end = buffer_point_start + overshoot;
let buffer_offset_start = self.buffer.point_to_offset(buffer_point_start);
let buffer_offset_end = self.buffer.point_to_offset(buffer_point_end);
InlayOffset(start.1.0 + (buffer_offset_end - buffer_offset_start))
let filter_point_start = start.2;
let filter_point_end = filter_point_start + FilterPoint(overshoot);
let buffer_offset_start = self.filter_snapshot.point_to_offset(filter_point_start);
let buffer_offset_end = self.filter_snapshot.point_to_offset(filter_point_end);
InlayOffset(start.1.0 + (buffer_offset_end - buffer_offset_start).0)
}
Some(Transform::Inlay(inlay)) => {
let overshoot = inlay.text().point_to_offset(overshoot);
@@ -784,35 +799,39 @@ impl InlaySnapshot {
None => self.len(),
}
}
pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
let (start, _, item) =
self.transforms
.find::<Dimensions<InlayPoint, Point>, _>((), &point, Bias::Right);
pub(crate) fn to_filter_point(&self, point: InlayPoint) -> FilterPoint {
let (start, _, item) = self
.transforms
.find::<Dimensions<InlayPoint, FilterPoint>, _>((), &point, Bias::Right);
match item {
Some(Transform::Isomorphic(_)) => {
let overshoot = point.0 - start.0.0;
start.1 + overshoot
start.1 + FilterPoint(overshoot)
}
Some(Transform::Inlay(_)) => start.1,
None => self.buffer.max_point(),
}
}
pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize {
let (start, _, item) =
self.transforms
.find::<Dimensions<InlayOffset, usize>, _>((), &offset, Bias::Right);
match item {
Some(Transform::Isomorphic(_)) => {
let overshoot = offset - start.0;
start.1 + overshoot.0
}
Some(Transform::Inlay(_)) => start.1,
None => self.buffer.len(),
None => self.filter_snapshot.max_point(),
}
}
pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset {
let mut cursor = self.transforms.cursor::<Dimensions<usize, InlayOffset>>(());
pub(crate) fn to_filter_offset(&self, offset: InlayOffset) -> FilterOffset {
let (start, _, item) = self
.transforms
.find::<Dimensions<InlayOffset, FilterOffset>, _>((), &offset, Bias::Right);
match item {
Some(Transform::Isomorphic(_)) => {
let overshoot = offset - start.0;
start.1 + FilterOffset(overshoot.0)
}
Some(Transform::Inlay(_)) => start.1,
None => self.filter_snapshot.len(),
}
}
pub fn to_inlay_offset(&self, offset: FilterOffset) -> InlayOffset {
let mut cursor = self
.transforms
.cursor::<Dimensions<FilterOffset, InlayOffset>>(());
cursor.seek(&offset, Bias::Left);
loop {
match cursor.item() {
@@ -828,7 +847,7 @@ impl InlaySnapshot {
return cursor.end().1;
} else {
let overshoot = offset - cursor.start().0;
return InlayOffset(cursor.start().1.0 + overshoot);
return InlayOffset(cursor.start().1.0 + overshoot.0);
}
}
Some(Transform::Inlay(inlay)) => {
@@ -844,8 +863,11 @@ impl InlaySnapshot {
}
}
}
pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
let mut cursor = self.transforms.cursor::<Dimensions<Point, InlayPoint>>(());
pub fn to_inlay_point(&self, point: FilterPoint) -> InlayPoint {
let mut cursor = self
.transforms
.cursor::<Dimensions<FilterPoint, InlayPoint>>(());
cursor.seek(&point, Bias::Left);
loop {
match cursor.item() {
@@ -861,7 +883,7 @@ impl InlaySnapshot {
return cursor.end().1;
} else {
let overshoot = point - cursor.start().0;
return InlayPoint(cursor.start().1.0 + overshoot);
return InlayPoint(cursor.start().1.0 + overshoot.0);
}
}
Some(Transform::Inlay(inlay)) => {
@@ -879,7 +901,9 @@ impl InlaySnapshot {
}
pub fn clip_point(&self, mut point: InlayPoint, mut bias: Bias) -> InlayPoint {
let mut cursor = self.transforms.cursor::<Dimensions<InlayPoint, Point>>(());
let mut cursor = self
.transforms
.cursor::<Dimensions<InlayPoint, FilterPoint>>(());
cursor.seek(&point, Bias::Left);
loop {
match cursor.item() {
@@ -915,10 +939,11 @@ impl InlaySnapshot {
}
} else {
let overshoot = point.0 - cursor.start().0.0;
let buffer_point = cursor.start().1 + overshoot;
let clipped_buffer_point = self.buffer.clip_point(buffer_point, bias);
let clipped_overshoot = clipped_buffer_point - cursor.start().1;
let clipped_point = InlayPoint(cursor.start().0.0 + clipped_overshoot);
let filter_point = cursor.start().1 + FilterPoint(overshoot);
let clipped_filter_point =
self.filter_snapshot.clip_point(filter_point, bias);
let clipped_overshoot = clipped_filter_point - cursor.start().1;
let clipped_point = InlayPoint(cursor.start().0.0 + clipped_overshoot.0);
if clipped_point == point {
return clipped_point;
} else {
@@ -976,17 +1001,21 @@ impl InlaySnapshot {
pub fn text_summary_for_range(&self, range: Range<InlayOffset>) -> TextSummary {
let mut summary = TextSummary::default();
let mut cursor = self.transforms.cursor::<Dimensions<InlayOffset, usize>>(());
let mut cursor = self
.transforms
.cursor::<Dimensions<InlayOffset, FilterOffset>>(());
cursor.seek(&range.start, Bias::Right);
let overshoot = range.start.0 - cursor.start().0.0;
match cursor.item() {
Some(Transform::Isomorphic(_)) => {
let buffer_start = cursor.start().1;
let suffix_start = buffer_start + overshoot;
let suffix_end =
buffer_start + (cmp::min(cursor.end().0, range.end).0 - cursor.start().0.0);
summary = self.buffer.text_summary_for_range(suffix_start..suffix_end);
let filter_start = cursor.start().1;
let suffix_start = filter_start + FilterOffset(overshoot);
let suffix_end = filter_start
+ FilterOffset(cmp::min(cursor.end().0, range.end).0 - cursor.start().0.0);
summary = self
.filter_snapshot
.text_summary_for_range(suffix_start..suffix_end);
cursor.next();
}
Some(Transform::Inlay(inlay)) => {
@@ -1007,10 +1036,10 @@ impl InlaySnapshot {
match cursor.item() {
Some(Transform::Isomorphic(_)) => {
let prefix_start = cursor.start().1;
let prefix_end = prefix_start + overshoot;
let prefix_end = prefix_start + FilterOffset(overshoot);
summary += self
.buffer
.text_summary_for_range::<TextSummary, _>(prefix_start..prefix_end);
.filter_snapshot
.text_summary_for_range(prefix_start..prefix_end);
}
Some(Transform::Inlay(inlay)) => {
let prefix_end = overshoot;
@@ -1024,29 +1053,31 @@ impl InlaySnapshot {
}
pub fn row_infos(&self, row: u32) -> InlayBufferRows<'_> {
let mut cursor = self.transforms.cursor::<Dimensions<InlayPoint, Point>>(());
let mut cursor = self
.transforms
.cursor::<Dimensions<InlayPoint, FilterPoint>>(());
let inlay_point = InlayPoint::new(row, 0);
cursor.seek(&inlay_point, Bias::Left);
let max_buffer_row = self.buffer.max_row();
let mut buffer_point = cursor.start().1;
let buffer_row = if row == 0 {
MultiBufferRow(0)
let max_filter_row = self.filter_snapshot.max_row();
let mut filter_point = cursor.start().1;
let filter_row = if row == 0 {
FilterRow(0)
} else {
match cursor.item() {
Some(Transform::Isomorphic(_)) => {
buffer_point += inlay_point.0 - cursor.start().0.0;
MultiBufferRow(buffer_point.row)
filter_point.0 += inlay_point.0 - cursor.start().0.0;
FilterRow(filter_point.0.row)
}
_ => cmp::min(MultiBufferRow(buffer_point.row + 1), max_buffer_row),
_ => cmp::min(FilterRow(filter_point.0.row + 1), max_filter_row),
}
};
InlayBufferRows {
transforms: cursor,
inlay_row: inlay_point.row(),
buffer_rows: self.buffer.row_infos(buffer_row),
max_buffer_row,
filter_rows: self.filter_snapshot.row_infos(filter_row),
max_filter_row,
}
}
@@ -1066,20 +1097,19 @@ impl InlaySnapshot {
language_aware: bool,
highlights: Highlights<'a>,
) -> InlayChunks<'a> {
let mut cursor = self.transforms.cursor::<Dimensions<InlayOffset, usize>>(());
let mut cursor = self
.transforms
.cursor::<Dimensions<InlayOffset, FilterOffset>>(());
cursor.seek(&range.start, Bias::Right);
let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end);
let buffer_chunks = CustomHighlightsChunks::new(
buffer_range,
language_aware,
highlights.text_highlights,
&self.buffer,
);
let filter_range = self.to_filter_offset(range.start)..self.to_filter_offset(range.end);
let filter_chunks = self
.filter_snapshot
.chunks(filter_range, language_aware, &highlights);
InlayChunks {
transforms: cursor,
buffer_chunks,
filter_chunks,
inlay_chunks: None,
inlay_chunk: None,
buffer_chunk: None,
@@ -1101,7 +1131,10 @@ impl InlaySnapshot {
fn check_invariants(&self) {
#[cfg(any(debug_assertions, feature = "test-support"))]
{
assert_eq!(self.transforms.summary().input, self.buffer.text_summary());
assert_eq!(
self.transforms.summary().input,
self.filter_snapshot.text_summary()
);
let mut transforms = self.transforms.iter().peekable();
while let Some(transform) = transforms.next() {
let transform_is_isomorphic = matches!(transform, Transform::Isomorphic(_));
@@ -1168,7 +1201,7 @@ mod tests {
use super::*;
use crate::{
MultiBuffer,
display_map::{HighlightKey, InlayHighlights, TextHighlights},
display_map::{HighlightKey, InlayHighlights, TextHighlights, filter_map::FilterMap},
hover_links::InlayHighlight,
};
use gpui::{App, HighlightStyle};
@@ -1292,7 +1325,9 @@ mod tests {
fn test_basic_inlays(cx: &mut App) {
let buffer = MultiBuffer::build_simple("abcdefghi", cx);
let buffer_edits = buffer.update(cx, |buffer, _| buffer.subscribe());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
assert_eq!(inlay_snapshot.text(), "abcdefghi");
let mut next_inlay_id = 0;
@@ -1306,27 +1341,27 @@ mod tests {
);
assert_eq!(inlay_snapshot.text(), "abc|123|defghi");
assert_eq!(
inlay_snapshot.to_inlay_point(Point::new(0, 0)),
inlay_snapshot.to_inlay_point(FilterPoint(Point::new(0, 0))),
InlayPoint::new(0, 0)
);
assert_eq!(
inlay_snapshot.to_inlay_point(Point::new(0, 1)),
inlay_snapshot.to_inlay_point(FilterPoint(Point::new(0, 1))),
InlayPoint::new(0, 1)
);
assert_eq!(
inlay_snapshot.to_inlay_point(Point::new(0, 2)),
inlay_snapshot.to_inlay_point(FilterPoint(Point::new(0, 2))),
InlayPoint::new(0, 2)
);
assert_eq!(
inlay_snapshot.to_inlay_point(Point::new(0, 3)),
inlay_snapshot.to_inlay_point(FilterPoint(Point::new(0, 3))),
InlayPoint::new(0, 3)
);
assert_eq!(
inlay_snapshot.to_inlay_point(Point::new(0, 4)),
inlay_snapshot.to_inlay_point(FilterPoint(Point::new(0, 4))),
InlayPoint::new(0, 9)
);
assert_eq!(
inlay_snapshot.to_inlay_point(Point::new(0, 5)),
inlay_snapshot.to_inlay_point(FilterPoint(Point::new(0, 5))),
InlayPoint::new(0, 10)
);
assert_eq!(
@@ -1358,18 +1393,20 @@ mod tests {
buffer.update(cx, |buffer, cx| {
buffer.edit([(2..3, "x"), (3..3, "y"), (4..4, "z")], None, cx)
});
let (inlay_snapshot, _) = inlay_map.sync(
let (filter_snapshot, filter_edits) = filter_map.sync(
buffer.read(cx).snapshot(cx),
buffer_edits.consume().into_inner(),
);
let (inlay_snapshot, _) = inlay_map.sync(filter_snapshot, filter_edits);
assert_eq!(inlay_snapshot.text(), "abxy|123|dzefghi");
// An edit surrounding the inlay should invalidate it.
buffer.update(cx, |buffer, cx| buffer.edit([(4..5, "D")], None, cx));
let (inlay_snapshot, _) = inlay_map.sync(
let (filter_snapshot, filter_edits) = filter_map.sync(
buffer.read(cx).snapshot(cx),
buffer_edits.consume().into_inner(),
);
let (inlay_snapshot, _) = inlay_map.sync(filter_snapshot, filter_edits);
assert_eq!(inlay_snapshot.text(), "abxyDzefghi");
let (inlay_snapshot, _) = inlay_map.splice(
@@ -1391,10 +1428,11 @@ mod tests {
// Edits ending where the inlay starts should not move it if it has a left bias.
buffer.update(cx, |buffer, cx| buffer.edit([(3..3, "JKL")], None, cx));
let (inlay_snapshot, _) = inlay_map.sync(
let (filter_snapshot, filter_edits) = filter_map.sync(
buffer.read(cx).snapshot(cx),
buffer_edits.consume().into_inner(),
);
let (inlay_snapshot, _) = inlay_map.sync(filter_snapshot, filter_edits);
assert_eq!(inlay_snapshot.text(), "abx|123|JKL|456|yDzefghi");
assert_eq!(
@@ -1583,7 +1621,8 @@ mod tests {
#[gpui::test]
fn test_inlay_buffer_rows(cx: &mut App) {
let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer.read(cx).snapshot(cx));
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi");
let mut next_inlay_id = 0;
@@ -1637,7 +1676,8 @@ mod tests {
let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
let mut next_inlay_id = 0;
log::info!("buffer text: {:?}", buffer_snapshot.text());
let (mut inlay_map, mut inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (mut inlay_map, mut inlay_snapshot) = InlayMap::new(filter_snapshot.clone());
for _ in 0..operations {
let mut inlay_edits = Patch::default();
@@ -1660,8 +1700,10 @@ mod tests {
}),
};
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot.clone(), buffer_edits);
let (new_inlay_snapshot, new_inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
inlay_map.sync(filter_snapshot, filter_edits);
inlay_snapshot = new_inlay_snapshot;
inlay_edits = inlay_edits.compose(new_inlay_edits);
@@ -1809,14 +1851,15 @@ mod tests {
assert_eq!(expected_text.len(), inlay_snapshot.len().0);
let mut buffer_point = Point::default();
let mut inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
let mut inlay_point = inlay_snapshot.to_inlay_point(FilterPoint(buffer_point));
let mut buffer_chars = buffer_snapshot.chars_at(0);
loop {
// Ensure conversion from buffer coordinates to inlay coordinates
// is consistent.
let buffer_offset = buffer_snapshot.point_to_offset(buffer_point);
assert_eq!(
inlay_snapshot.to_point(inlay_snapshot.to_inlay_offset(buffer_offset)),
inlay_snapshot
.to_point(inlay_snapshot.to_inlay_offset(FilterOffset(buffer_offset))),
inlay_point
);
@@ -1843,7 +1886,7 @@ mod tests {
}
// Ensure that moving forward in the buffer always moves the inlay point forward as well.
let new_inlay_point = inlay_snapshot.to_inlay_point(buffer_point);
let new_inlay_point = inlay_snapshot.to_inlay_point(FilterPoint(buffer_point));
assert!(new_inlay_point > inlay_point);
inlay_point = new_inlay_point;
} else {
@@ -1903,19 +1946,19 @@ mod tests {
// Ensure the clipped points are at valid buffer locations.
assert_eq!(
inlay_snapshot
.to_inlay_point(inlay_snapshot.to_buffer_point(clipped_left_point)),
.to_inlay_point(inlay_snapshot.to_filter_point(clipped_left_point)),
clipped_left_point,
"to_buffer_point({:?}) = {:?}",
"to_filter_point({:?}) = {:?}",
clipped_left_point,
inlay_snapshot.to_buffer_point(clipped_left_point),
inlay_snapshot.to_filter_point(clipped_left_point),
);
assert_eq!(
inlay_snapshot
.to_inlay_point(inlay_snapshot.to_buffer_point(clipped_right_point)),
.to_inlay_point(inlay_snapshot.to_filter_point(clipped_right_point)),
clipped_right_point,
"to_buffer_point({:?}) = {:?}",
"to_filter_point({:?}) = {:?}",
clipped_right_point,
inlay_snapshot.to_buffer_point(clipped_right_point),
inlay_snapshot.to_filter_point(clipped_right_point),
);
}
}
@@ -1938,7 +1981,8 @@ mod tests {
};
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (mut inlay_map, _) = InlayMap::new(buffer_snapshot.clone());
let (_filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (mut inlay_map, _) = InlayMap::new(filter_snapshot.clone());
// Perform random mutations to add inlays
let mut next_inlay_id = 0;
@@ -1947,7 +1991,7 @@ mod tests {
inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
}
let (snapshot, _) = inlay_map.sync(buffer_snapshot, vec![]);
let (snapshot, _) = inlay_map.sync(filter_snapshot, vec![]);
// Get all chunks and verify their bitmaps
let chunks = snapshot.chunks(
@@ -2057,7 +2101,8 @@ mod tests {
//
// See https://github.com/zed-industries/zed/issues/33641
let buffer = MultiBuffer::build_simple("fn main() {}\n", cx);
let (mut inlay_map, _) = InlayMap::new(buffer.read(cx).snapshot(cx));
let (_filter_map, filter_snapshot) = FilterMap::new(None, buffer.read(cx).snapshot(cx));
let (mut inlay_map, _) = InlayMap::new(filter_snapshot);
// Create an inlay with text that contains a multi-byte character
// The string "SortingDirec…" contains an ellipsis character '…' which is 3 bytes (E2 80 A6)
@@ -2175,7 +2220,8 @@ mod tests {
for test_case in test_cases {
let buffer = MultiBuffer::build_simple("test", cx);
let (mut inlay_map, _) = InlayMap::new(buffer.read(cx).snapshot(cx));
let (_filter_map, filter_snapshot) = FilterMap::new(None, buffer.read(cx).snapshot(cx));
let (mut inlay_map, _) = InlayMap::new(filter_snapshot);
let position = buffer.read(cx).snapshot(cx).anchor_before(Point::new(0, 2));
let inlay = Inlay {

View File

@@ -169,7 +169,11 @@ pub struct TabSnapshot {
impl TabSnapshot {
pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
&self.fold_snapshot.inlay_snapshot.buffer
&self
.fold_snapshot
.inlay_snapshot
.filter_snapshot
.buffer_snapshot
}
pub fn line_len(&self, row: u32) -> u32 {
@@ -319,7 +323,15 @@ impl TabSnapshot {
}
pub fn make_tab_point(&self, point: Point, bias: Bias) -> TabPoint {
let inlay_point = self.fold_snapshot.inlay_snapshot.to_inlay_point(point);
let filter_point = self
.fold_snapshot
.inlay_snapshot
.filter_snapshot
.to_filter_point(point);
let inlay_point = self
.fold_snapshot
.inlay_snapshot
.to_inlay_point(filter_point);
let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
self.to_tab_point(fold_point)
}
@@ -327,9 +339,14 @@ impl TabSnapshot {
pub fn to_point(&self, point: TabPoint, bias: Bias) -> Point {
let fold_point = self.to_fold_point(point, bias).0;
let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
let filter_point = self
.fold_snapshot
.inlay_snapshot
.to_filter_point(inlay_point);
self.fold_snapshot
.inlay_snapshot
.to_buffer_point(inlay_point)
.filter_snapshot
.to_buffer_point(filter_point, bias)
}
fn expand_tabs<'a, I>(&self, mut cursor: TabStopCursor<'a, I>, column: u32) -> u32
@@ -636,6 +653,7 @@ mod tests {
use crate::{
MultiBuffer,
display_map::{
filter_map::FilterMap,
fold_map::{FoldMap, FoldOffset},
inlay_map::InlayMap,
},
@@ -750,7 +768,8 @@ mod tests {
];
let buffer = MultiBuffer::build_simple("", cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
@@ -786,7 +805,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(input, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
@@ -825,7 +845,8 @@ mod tests {
let text = "γ\tw⭐\n🍐🍗 \t";
let buffer = MultiBuffer::build_simple(text, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
@@ -864,7 +885,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(&input, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, mut tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
tab_snapshot.max_expansion_column = rng.random_range(0..323);
@@ -909,7 +931,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(input, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, mut tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
@@ -956,7 +979,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(input, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, mut tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
@@ -970,7 +994,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(input, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
@@ -1030,7 +1055,8 @@ mod tests {
let buffer_snapshot = buffer.read(cx).snapshot(cx);
log::info!("Buffer text: {:?}", buffer_snapshot.text());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
log::info!("InlayMap text: {:?}", inlay_snapshot.text());
let (mut fold_map, _) = FoldMap::new(inlay_snapshot.clone());
fold_map.randomly_mutate(&mut rng);
@@ -1106,7 +1132,8 @@ mod tests {
// Create buffer and tab map
let buffer = MultiBuffer::build_simple(&text, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
let (mut tab_map, _) = TabMap::new(fold_snapshot, tab_size);
@@ -1145,7 +1172,8 @@ mod tests {
let text = "\tfoo\tbarbarbar\t\tbaz\n";
let buffer = MultiBuffer::build_simple(text, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let chunks = fold_snapshot.chunks(
FoldOffset(0)..fold_snapshot.len(),
@@ -1183,7 +1211,8 @@ mod tests {
let buffer = MultiBuffer::build_simple(input, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let chunks = fold_snapshot.chunks_at(FoldPoint::new(0, 0));
@@ -1241,7 +1270,8 @@ mod tests {
// Build the buffer and create cursor
let buffer = MultiBuffer::build_simple(&input, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
// First, collect all expected tab positions
@@ -1307,7 +1337,8 @@ mod tests {
let text = "\r\t😁foo\tb😀arbar🤯bar\t\tbaz\n";
let buffer = MultiBuffer::build_simple(text, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot);
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
let chunks = fold_snapshot.chunks(
FoldOffset(0)..fold_snapshot.len(),
@@ -1351,7 +1382,8 @@ mod tests {
// Build the buffer and create cursor
let buffer = MultiBuffer::build_simple(&input, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx);
let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (_, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (_, inlay_snapshot) = InlayMap::new(filter_snapshot);
let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
// First, collect all expected tab positions

View File

@@ -31,18 +31,21 @@ pub struct WrapMap {
#[derive(Clone)]
pub struct WrapSnapshot {
pub(super) tab_snapshot: TabSnapshot,
transforms: SumTree<Transform>,
// FIXME
pub(crate) transforms: SumTree<Transform>,
interpolated: bool,
}
// FIXME
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct Transform {
pub(crate) struct Transform {
summary: TransformSummary,
display_text: Option<&'static str>,
}
// FIXME
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct TransformSummary {
pub(crate) struct TransformSummary {
input: TextSummary,
output: TextSummary,
}
@@ -1201,7 +1204,9 @@ mod tests {
use super::*;
use crate::{
MultiBuffer,
display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap},
display_map::{
filter_map::FilterMap, fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap,
},
test::test_font,
};
use gpui::{LineFragment, px, test::observe};
@@ -1250,7 +1255,8 @@ mod tests {
});
let mut buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
log::info!("Buffer text: {:?}", buffer_snapshot.text());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
let (mut filter_map, filter_snapshot) = FilterMap::new(None, buffer_snapshot.clone());
let (mut inlay_map, inlay_snapshot) = InlayMap::new(filter_snapshot.clone());
log::info!("InlayMap text: {:?}", inlay_snapshot.text());
let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
log::info!("FoldMap text: {:?}", fold_snapshot.text());
@@ -1334,8 +1340,9 @@ mod tests {
}
log::info!("Buffer text: {:?}", buffer_snapshot.text());
let (inlay_snapshot, inlay_edits) =
inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
let (filter_snapshot, filter_edits) =
filter_map.sync(buffer_snapshot.clone(), buffer_edits);
let (inlay_snapshot, inlay_edits) = inlay_map.sync(filter_snapshot, filter_edits);
log::info!("InlayMap text: {:?}", inlay_snapshot.text());
let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
log::info!("FoldMap text: {:?}", fold_snapshot.text());

View File

@@ -1728,6 +1728,24 @@ impl Editor {
clone
}
pub fn filtered(
buffer: Entity<MultiBuffer>,
project: Entity<Project>,
filter_mode: FilterMode,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
Editor::new_internal(
EditorMode::full(),
buffer,
Some(project),
None,
Some(filter_mode),
window,
cx,
)
}
pub fn new(
mode: EditorMode,
buffer: Entity<MultiBuffer>,
@@ -1735,7 +1753,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
Editor::new_internal(mode, buffer, project, None, window, cx)
Editor::new_internal(mode, buffer, project, None, None, window, cx)
}
fn new_internal(
@@ -1743,6 +1761,7 @@ impl Editor {
multi_buffer: Entity<MultiBuffer>,
project: Option<Entity<Project>>,
display_map: Option<Entity<DisplayMap>>,
filter_mode: Option<FilterMode>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -1806,6 +1825,7 @@ impl Editor {
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
fold_placeholder,
diagnostics_max_severity,
filter_mode,
cx,
)
})
@@ -2855,6 +2875,7 @@ impl Editor {
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
Default::default(),
DiagnosticSeverity::Off,
None,
cx,
)
}));
@@ -19157,6 +19178,7 @@ impl Editor {
self.buffer.clone(),
None,
Some(self.display_map.clone()),
None,
window,
cx,
);
@@ -19281,6 +19303,13 @@ impl Editor {
}
}
#[cfg(test)]
pub(crate) fn set_filter_mode(&mut self, filter_mode: Option<FilterMode>, cx: &mut App) {
self.display_map.update(cx, |display_map, cx| {
display_map.set_filter_mode(filter_mode, cx);
});
}
pub fn set_soft_wrap(&mut self) {
self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
}

View File

@@ -1506,6 +1506,62 @@ fn test_move_cursor(cx: &mut TestAppContext) {
});
}
// FIXME
#[gpui::test]
async fn test_filtered_editor(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
cx.update_editor(|editor, window, cx| {
editor.set_expand_all_diff_hunks(cx);
editor.set_text("one\nTWO\nTHREE\nfour\n", window, cx);
});
cx.set_head_text("one\ntwo\nthree\nfour\n");
cx.update_editor(|editor, _, cx| {
editor.set_filter_mode(Some(FilterMode::RemoveDeletions), cx);
});
cx.run_until_parked();
let text = cx.editor(|editor, window, cx| editor.display_text(cx));
pretty_assertions::assert_eq!(text, "one\nTWO\nTHREE\nfour\n");
cx.set_selections_state(indoc! {"
ˇone
two
three
TWO
THREE
four
"});
cx.update_editor(|editor, window, cx| {
editor.move_down(&Default::default(), window, cx);
});
cx.run_until_parked();
cx.update_editor(|editor, window, cx| {
editor.move_down(&Default::default(), window, cx);
});
cx.run_until_parked();
cx.update_editor(|editor, window, cx| {
editor.move_down(&Default::default(), window, cx);
});
cx.run_until_parked();
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
one
two
three
TWO
THREE
ˇfour
"});
// let row_infos = cx.editor(|editor, window, cx| {
// editor
// .display_snapshot(cx)
// .row_infos(DisplayRow(0))
// .collect::<Vec<_>>()
// });
// dbg!(&row_infos);
}
#[gpui::test]
fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
init_test(cx, |_| {});

View File

@@ -3757,6 +3757,16 @@ impl EditorElement {
result.into_any()
}
Block::Spacer { height, .. } => v_flex()
.id(block_id)
.w_full()
.child(
div()
.h(*height as f32 * window.line_height())
.debug_bg_magenta(),
)
.into_any(),
};
// Discover the element's content height, then round up to the nearest multiple of line height.

View File

@@ -1012,6 +1012,7 @@ mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -1210,6 +1211,7 @@ mod tests {
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});

130
crates/editor/src/split.rs Normal file
View File

@@ -0,0 +1,130 @@
use crate::EditorEvent;
use super::{Editor, EditorElement, EditorStyle};
use gpui::{App, Context, Entity, Render, Window};
use ui::{Element, IntoElement};
use workspace::ItemHandle;
// stage 1: render two side-by-side editors with the correct scrolling behavior
// stage 2: add alignment map to insert blank lines
/// An editor that can be rendered with a split diff layout.
///
/// When [secondary] is `None`, it is rendered with an inline diff style.
pub struct SplittableEditor {
primary: Entity<Editor>,
secondary: Option<Entity<Editor>>,
}
impl SplittableEditor {
fn subscribe(&mut self, window: &mut Window, cx: &mut Context<Self>) {
cx.subscribe_in(
&self.primary,
window,
|this, editor, event: &EditorEvent, window, cx| {},
)
.detach();
}
fn sync_state(&mut self, cx: &mut App) {}
}
impl SplittableEditor {}
struct SplitEditorElement {
primary: Entity<Editor>,
secondary: Entity<Editor>,
style: EditorStyle,
}
struct SplitEditorElementLayout {}
impl Element for SplitEditorElement {
type RequestLayoutState = ();
type PrepaintState = SplitEditorElementLayout;
fn id(&self) -> Option<ui::ElementId> {
todo!()
}
fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
todo!()
}
fn request_layout(
&mut self,
id: Option<&gpui::GlobalElementId>,
inspector_id: Option<&gpui::InspectorElementId>,
window: &mut ui::Window,
cx: &mut ui::App,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
}
fn prepaint(
&mut self,
id: Option<&gpui::GlobalElementId>,
inspector_id: Option<&gpui::InspectorElementId>,
bounds: gpui::Bounds<ui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
window: &mut ui::Window,
cx: &mut ui::App,
) -> Self::PrepaintState {
todo!()
}
fn paint(
&mut self,
id: Option<&gpui::GlobalElementId>,
inspector_id: Option<&gpui::InspectorElementId>,
bounds: gpui::Bounds<ui::Pixels>,
request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
window: &mut ui::Window,
cx: &mut ui::App,
) {
todo!()
}
}
impl Render for SplittableEditor {
fn render(
&mut self,
window: &mut ui::Window,
cx: &mut ui::Context<Self>,
) -> impl ui::IntoElement {
enum SplittableEditorElement {
Single(EditorElement),
Split(SplitEditorElement),
}
impl Element for SplittableEditorElement {}
impl IntoElement for SplittableEditorElement {
type Element = Self;
fn into_element(self) -> Self::Element {
self
}
}
let style;
if let Some(secondary) = self.secondary.clone() {
SplittableEditorElement::Split(SplitEditorElement {
primary: self.primary.clone(),
secondary,
style,
})
} else {
SplittableEditorElement::Single(EditorElement::new(&self.primary.clone(), style))
}
}
}
impl IntoElement for SplitEditorElement {
type Element = Self;
fn into_element(self) -> Self::Element {
self
}
}

View File

@@ -72,6 +72,7 @@ pub fn marked_display_snapshot(
1,
FoldPlaceholder::test(),
DiagnosticSeverity::Warning,
None,
cx,
)
});
@@ -238,6 +239,11 @@ pub fn editor_content_with_blocks(editor: &Entity<Editor>, cx: &mut VisualTestCo
lines[row as usize].push_str("§ -----");
}
}
Block::Spacer { height, .. } => {
for row in row.0..row.0 + height {
lines[row as usize].push_str("@@@@@@@");
}
}
}
}
lines.join("\n")

View File

@@ -3,7 +3,9 @@ use std::any::Any;
use ::settings::Settings;
use command_palette_hooks::CommandPaletteFilter;
use commit_modal::CommitModal;
use editor::{Editor, actions::DiffClipboardWithSelectionData};
use editor::{
Editor, MultiBuffer, actions::DiffClipboardWithSelectionData, display_map::FilterMode,
};
use ui::{
Headline, HeadlineSize, Icon, IconName, IconSize, IntoElement, ParentElement, Render, Styled,
StyledExt, div, h_flex, rems, v_flex,
@@ -25,7 +27,10 @@ use onboarding::GitOnboardingModal;
use project::git_store::Repository;
use project_diff::ProjectDiff;
use ui::prelude::*;
use workspace::{ModalView, Workspace, notifications::DetachAndPromptErr};
use util::rel_path::RelPath;
use workspace::{
ModalView, OpenOptions, SplitDirection, Workspace, notifications::DetachAndPromptErr,
};
use zed_actions;
use crate::{git_panel::GitPanel, text_diff_view::TextDiffView};
@@ -51,7 +56,8 @@ actions!(
git,
[
/// Resets the git onboarding state to show the tutorial again.
ResetOnboarding
ResetOnboarding,
OpenSplitDiff
]
);
@@ -221,6 +227,69 @@ pub fn init(cx: &mut App) {
};
},
);
workspace.register_action(|workspace, _: &OpenSplitDiff, window, cx| {
open_split_diff(workspace, window, cx);
});
})
.detach();
}
fn open_split_diff(
workspace: &mut Workspace,
window: &mut Window,
cx: &mut Context<'_, Workspace>,
) {
let buffer = workspace.project().update(cx, |project, cx| {
let worktree = project.worktrees(cx).next().unwrap();
project.open_buffer(
(
worktree.read(cx).id(),
RelPath::unix("scripts/spellcheck.sh").unwrap(),
),
cx,
)
});
cx.spawn_in(window, async move |workspace, cx| {
let buffer = buffer.await?;
workspace.update_in(cx, |workspace, window, cx| {
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::singleton(buffer, cx);
multibuffer.set_all_diff_hunks_expanded(cx);
multibuffer
});
// let deletions_only_editor = cx.new(|cx| {
// Editor::filtered(
// multibuffer.clone(),
// workspace.project().clone(),
// FilterMode::RemoveInsertions,
// window,
// cx,
// )
// });
let insertions_only_editor = cx.new(|cx| {
Editor::filtered(
multibuffer,
workspace.project().clone(),
FilterMode::RemoveDeletions,
window,
cx,
)
});
// workspace.add_item_to_active_pane(
// Box::new(deletions_only_editor),
// None,
// true,
// window,
// cx,
// );
workspace.split_item(
SplitDirection::horizontal(cx),
Box::new(insertions_only_editor),
window,
cx,
);
})?;
anyhow::Ok(())
})
.detach();
}

View File

@@ -38,7 +38,9 @@ use std::{
any::type_name,
borrow::Cow,
cell::{Cell, Ref, RefCell},
cmp, fmt,
cmp,
collections::VecDeque,
fmt,
future::Future,
io,
iter::{self, FromIterator},
@@ -56,7 +58,7 @@ use text::{
subscription::{Subscription, Topic},
};
use theme::SyntaxTheme;
use util::post_inc;
use util::{RangeExt, post_inc};
pub use self::path_key::PathKey;
@@ -126,6 +128,13 @@ pub enum Event {
BufferDiffChanged,
}
// ex1
// line4 of buffer1
// ex2
// line17 of buffer2
//
// in multibuffer coordinates, Point::new(1, 0)
/// A diff hunk, representing a range of consequent lines in a multibuffer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MultiBufferDiffHunk {
@@ -252,6 +261,23 @@ pub struct MultiBufferSnapshot {
show_headers: bool,
}
/// A section of text in a [`MultiBuffer`] that may or may not have changed relative to some "base
/// text".
///
/// Despite having two variants, there are conceptually three different states:
/// - `BufferContent { inserted_hunk_info: None, ..}` represents a region of text that has not
/// changed, relative to the base text.
/// - `BufferContent { inserted_hunk_info: Some(info), ..}` represents a region of text that has
/// been added, relative to the base text.
/// - `DeletedHunk` represents a region of text that has been deleted, relative to the base text.
///
/// The [`TextSummaries`](TextSummary) refer to either:
/// - the buffer text (`multi_buffer.buffers[buffer_id].buffer.snapshot`)
/// - the base text (`multi_buffer.diffs[buffer_id].diff.inner.base_text`)
///
/// The multibuffer tries to maintain an invariant that: "no two consecutive [`DiffTransform`]s can
/// be merged". In other words, if two consecutive [`DiffTransform`]s *could* be merged, they will
/// be.
#[derive(Debug, Clone)]
enum DiffTransform {
BufferContent {
@@ -502,6 +528,19 @@ struct MultiBufferRegion<'a, D: TextDimension> {
has_trailing_newline: bool,
}
impl<'a, D: TextDimension + fmt::Debug> fmt::Debug for MultiBufferRegion<'a, D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MultiBufferRegion")
.field("is_main_buffer", &self.is_main_buffer)
.field("diff_hunk_status", &self.diff_hunk_status)
.field("excerpt", &self.excerpt)
.field("buffer_range", &self.buffer_range)
.field("range", &self.range)
.field("has_trailing_newline", &self.has_trailing_newline)
.finish()
}
}
struct ExcerptChunks<'a> {
excerpt_id: ExcerptId,
content_chunks: BufferChunks<'a>,
@@ -2453,8 +2492,8 @@ impl MultiBuffer {
let mut at_transform_boundary = true;
let mut end_of_current_insert = None;
let mut excerpt_edits = excerpt_edits.into_iter().peekable();
while let Some(edit) = excerpt_edits.next() {
let mut excerpt_edits: VecDeque<_> = excerpt_edits.into_iter().collect();
while let Some(edit) = excerpt_edits.pop_front() {
excerpts.seek_forward(&edit.new.start, Bias::Right);
if excerpts.item().is_none() && *excerpts.start() == edit.new.start {
excerpts.prev();
@@ -2479,7 +2518,31 @@ impl MultiBuffer {
let edit_old_start = old_diff_transforms.start().1 + edit_start_overshoot;
let edit_new_start = (edit_old_start as isize + output_delta) as usize;
let changed_diff_hunks = Self::recompute_diff_transforms_for_edit(
// -----OLD-----
// - foo
// + bar
//
// -----NEW----
// - foo
// <b><AR>
//
//
// 1. b a r
// |-added-|
//
// 2. b a r
// |-added-|
// <--e-->
// 3. b A R
// |-|-----| // PROBLEM: "diff hunks intersecting `e`" is empty
// <--e-->
// 4. b A R // recalculate diffs
// |-------|
//
//
// solution 1:
let (changed_diff_hunks, additional_edit) = Self::recompute_diff_transforms_for_edit(
&edit,
&mut excerpts,
&mut old_diff_transforms,
@@ -2490,6 +2553,26 @@ impl MultiBuffer {
change_kind,
);
if let Some(mut additional_edit) = additional_edit {
let mut last_overlapping_edit = None;
while let Some(next_edit) = excerpt_edits.front()
&& next_edit.new.overlaps(&additional_edit.new)
{
last_overlapping_edit = Some(next_edit.clone());
excerpt_edits.pop_front();
}
if let Some(last_overlapping_edit) = last_overlapping_edit {
additional_edit.old.end = last_overlapping_edit.old.end;
if additional_edit.new.end > last_overlapping_edit.new.end {
let overshoot = additional_edit.new.end - last_overlapping_edit.new.end;
additional_edit.old.end += overshoot;
} else {
additional_edit.new.end = last_overlapping_edit.new.end;
}
}
excerpt_edits.push_front(additional_edit);
}
// Compute the end of the edit in output coordinates.
let edit_old_end_overshoot = edit.old.end - old_diff_transforms.start().0;
let edit_new_end_overshoot = edit.new.end - new_diff_transforms.summary().excerpt_len();
@@ -2504,14 +2587,15 @@ impl MultiBuffer {
output_delta += (output_edit.new.end - output_edit.new.start) as isize;
output_delta -= (output_edit.old.end - output_edit.old.start) as isize;
if changed_diff_hunks || matches!(change_kind, DiffChangeKind::BufferEdited) {
output_edits.push(output_edit);
let pushing_output_edit = output_edit;
output_edits.push(pushing_output_edit);
}
// If this is the last edit that intersects the current diff transform,
// then recreate the content up to the end of this transform, to prepare
// for reusing additional slices of the old transforms.
if excerpt_edits
.peek()
.front()
.is_none_or(|next_edit| next_edit.old.start >= old_diff_transforms.end().0)
{
let keep_next_old_transform = (old_diff_transforms.start().0 >= edit.old.end)
@@ -2575,7 +2659,7 @@ impl MultiBuffer {
old_expanded_hunks: &mut HashSet<DiffTransformHunkInfo>,
snapshot: &MultiBufferSnapshot,
change_kind: DiffChangeKind,
) -> bool {
) -> (bool, Option<Edit<TypedOffset<Excerpt>>>) {
log::trace!(
"recomputing diff transform for edit {:?} => {:?}",
edit.old.start.value..edit.old.end.value,
@@ -2603,11 +2687,14 @@ impl MultiBuffer {
&& change_kind == DiffChangeKind::BufferEdited
&& !all_diff_hunks_expanded
{
return false;
return (false, None);
}
let mut override_query_range_start = Some(new_diff_transforms.summary().excerpt_len());
// Visit each excerpt that intersects the edit.
let mut did_expand_hunks = false;
let mut additional_edit = None;
while let Some(excerpt) = excerpts.item() {
// Recompute the expanded hunks in the portion of the excerpt that
// intersects the edit.
@@ -2622,10 +2709,21 @@ impl MultiBuffer {
let edit_buffer_end =
excerpt_buffer_start + edit.new.end.value.saturating_sub(excerpt_start.value);
let edit_buffer_end = edit_buffer_end.min(excerpt_buffer_end);
let edit_anchor_range =
buffer.anchor_before(edit_buffer_start)..buffer.anchor_after(edit_buffer_end);
for hunk in diff.hunks_intersecting_range(edit_anchor_range, buffer) {
let query_range = if let Some(query_range_start) = override_query_range_start.take()
{
let query_range_buffer_start = query_range_start
.max(excerpt_start)
.sub(excerpt_start)
.value
+ excerpt_buffer_start;
buffer.anchor_before(query_range_buffer_start)
..buffer.anchor_after(edit_buffer_end)
} else {
buffer.anchor_before(edit_buffer_start)..buffer.anchor_after(edit_buffer_end)
};
for hunk in diff.valid_and_invalid_hunks_intersecting_range(query_range, buffer) {
if hunk.is_created_file() && !all_diff_hunks_expanded {
continue;
}
@@ -2651,6 +2749,19 @@ impl MultiBuffer {
+ ExcerptOffset::new(hunk_buffer_range.end - excerpt_buffer_start),
);
if !hunk.buffer_range.start.is_valid(buffer) {
if old_expanded_hunks.contains(&hunk_info)
&& hunk_excerpt_end > edit.new.end
{
let overshoot = hunk_excerpt_end - edit.new.end;
additional_edit = Some(Edit {
old: edit.old.end..edit.old.end + overshoot,
new: edit.new.end..hunk_excerpt_end,
});
}
continue;
}
Self::push_buffer_content_transform(
snapshot,
new_diff_transforms,
@@ -2728,7 +2839,10 @@ impl MultiBuffer {
}
}
did_expand_hunks || !old_expanded_hunks.is_empty()
(
did_expand_hunks || !old_expanded_hunks.is_empty(),
additional_edit,
)
}
fn append_diff_transforms(
@@ -2798,7 +2912,7 @@ impl MultiBuffer {
new_transforms.push(
DiffTransform::BufferContent {
summary: summary_to_add,
inserted_hunk_info,
inserted_hunk_info: inserted_hunk_info,
},
(),
)
@@ -3140,6 +3254,9 @@ impl MultiBufferSnapshot {
)
})
.filter_map(move |(range, hunk, excerpt)| {
if hunk.range.start < excerpt.range.context.start.to_point(&excerpt.buffer) {
return None;
}
if range.start != range.end && range.end == query_range.start && !hunk.range.is_empty()
{
return None;
@@ -3393,6 +3510,8 @@ impl MultiBufferSnapshot {
while let Some(region) = cursor.region() {
if region.is_main_buffer
&& (region.buffer_range.end > metadata_buffer_range.end
|| (region.buffer_range.end == metadata_buffer_range.end
&& region.diff_hunk_status.is_some())
|| cursor.is_at_end_of_excerpt())
{
break;
@@ -7173,3 +7292,174 @@ pub mod debug {
}
}
}
// FIXME figure out whether this should have a different API or something
#[cfg(feature = "test-support")]
pub fn randomly_mutate_multibuffer_with_diffs(
multibuffer: Entity<MultiBuffer>,
buffers: &mut Vec<Entity<Buffer>>,
base_texts: &mut HashMap<BufferId, String>,
needs_diff_calculation: &mut bool,
rng: &mut rand::rngs::StdRng,
cx: &mut gpui::TestAppContext,
) {
use rand::Rng as _;
use rand::seq::IndexedRandom as _;
fn format_diff(
text: &str,
row_infos: &Vec<RowInfo>,
boundary_rows: &HashSet<MultiBufferRow>,
has_diff: Option<bool>,
) -> String {
let has_diff =
has_diff.unwrap_or_else(|| row_infos.iter().any(|info| info.diff_status.is_some()));
let mut running_total = 0;
text.split('\n')
.enumerate()
.zip(row_infos)
.map(|((ix, line), info)| {
let running_total_before = running_total;
let line_length = line.len();
running_total += line_length;
let running_total_after = running_total;
running_total += 1;
let marker = match info.diff_status.map(|status| status.kind) {
Some(DiffHunkStatusKind::Added) => "+ ",
Some(DiffHunkStatusKind::Deleted) => "- ",
Some(DiffHunkStatusKind::Modified) => unreachable!(),
None => {
if has_diff && !line.is_empty() {
" "
} else {
""
}
}
};
// format: 123->124 (len: 001)
let line_numbers = format!(
"{running_total_before:0>3}->{running_total_after:0>3} (len: {line_length:0>3})"
);
if boundary_rows.contains(&MultiBufferRow(ix as u32)) {
let boundary_row = if has_diff {
" | ----------\n"
} else {
" |----------\n"
};
format!("{boundary_row}{line_numbers}|{marker}{line}")
} else {
format!("{line_numbers}|{marker}{line}")
}
})
.collect::<Vec<_>>()
.join("\n")
}
let excerpt_ids = multibuffer.read_with(cx, |multibuffer, _| multibuffer.excerpt_ids());
// TODO cover changes of base text?
match rng.random_range(0..100) {
0..=49 if !buffers.is_empty() => {
let buffer = buffers.choose(rng).unwrap();
buffer.update(cx, |buf, cx| {
let edit_count = rng.random_range(1..5);
log::info!("editing buffer");
buf.randomly_edit(rng, edit_count, cx);
*needs_diff_calculation = true;
});
}
50..=69 if *needs_diff_calculation => {
multibuffer.update(cx, |multibuffer, cx| {
for buffer in buffers {
use util::rel_path::rel_path;
let snapshot = buffer.read(cx).snapshot();
let diff = multibuffer.diff_for(snapshot.remote_id()).unwrap();
diff.update(cx, |diff, cx| {
log::info!("recalculating diff for buffer {:?}", snapshot.remote_id());
diff.recalculate_diff_sync(snapshot.text.clone(), cx);
});
let ranges = diff
.read(cx)
.hunks(&snapshot, cx)
.map(|hunk| hunk.buffer_range.to_point(&snapshot))
.collect::<Vec<_>>();
multibuffer.set_excerpts_for_path(
PathKey {
sort_prefix: None,
path: rel_path(&buffer.read(cx).remote_id().to_string()).into_arc(),
},
buffer.clone(),
ranges.into_iter(),
2,
cx,
);
}
*needs_diff_calculation = false;
});
}
70..=79 if !excerpt_ids.is_empty() => {
multibuffer.update(cx, |multibuffer, cx| {
let ids = multibuffer.excerpt_ids();
let mut excerpts = HashSet::default();
for _ in 0..rng.random_range(0..ids.len()) {
excerpts.extend(ids.choose(rng).copied());
}
let line_count = rng.random_range(0..5);
log::info!("Expanding excerpts by {line_count} lines");
multibuffer.expand_excerpts(
excerpts.iter().cloned(),
line_count,
ExpandExcerptDirection::UpAndDown,
cx,
);
});
}
_ => {
let mut base_text = util::RandomCharIter::new(&mut *rng)
.take(256)
.collect::<String>();
let buffer = cx.new(|cx| Buffer::local(base_text.clone(), cx));
text::LineEnding::normalize(&mut base_text);
base_texts.insert(
buffer.read_with(cx, |buffer, _| buffer.remote_id()),
base_text,
);
buffers.push(buffer);
let buffer_handle = buffers.last().unwrap();
multibuffer.update(cx, |multibuffer, cx| {
let id = buffer_handle.read(cx).remote_id();
if multibuffer.diff_for(id).is_none() {
let base_text = base_texts.get(&id).unwrap();
let diff =
cx.new(|cx| BufferDiff::new_with_base_text(base_text, buffer_handle, cx));
multibuffer.add_diff(diff, cx)
}
});
*needs_diff_calculation = true;
}
}
{
let snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
let actual_boundary_rows = snapshot
.excerpt_boundaries_in_range(0..)
.map(|b| b.row)
.collect::<HashSet<_>>();
log::info!(
"multibuffer contents:\n{}",
format_diff(
&snapshot.text(),
&snapshot.row_infos(MultiBufferRow(0)).collect(),
&actual_boundary_rows,
Some(true)
)
);
}
}

View File

@@ -616,6 +616,70 @@ fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
);
}
/// This test checks for cases where an edit invalidates a diff hunk (by
/// invalidating the anchor at the start of the added region), but does not
/// cover the whole added region. The multibuffer should handle this by
/// extending the edit until the end of the added region.
#[gpui::test]
async fn test_edits_invalidating_diff_hunks(cx: &mut TestAppContext) {
let base_text = "one\ntwo\nfour\nsix\n";
let text = "one\ntwo\nTHREE\nfour\nFIVE\nsix\n";
let buffer = cx.new(|cx| Buffer::local(text, cx));
let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
multibuffer.add_diff(diff.clone(), cx);
multibuffer.set_all_diff_hunks_expanded(cx);
(multibuffer.snapshot(cx), multibuffer.subscribe())
});
assert_new_snapshot(
&multibuffer,
&mut snapshot,
&mut subscription,
cx,
indoc! {
"
one
two
+ THREE
four
+ FIVE
six
"
},
);
// Make an edit to the buffer that invalidates the added hunk, but does not cover the whole added region.
multibuffer.update(cx, |multibuffer, cx| {
multibuffer.edit([(Point::new(1, 1)..Point::new(4, 2), "")], None, cx);
});
let edits = assert_new_snapshot(
&multibuffer,
&mut snapshot,
&mut subscription,
cx,
indoc! {
"
one
tVE
six
"
},
);
// The old and new end of the edit are extended to cover the rest of the former diff hunk's added region.
pretty_assertions::assert_eq!(
edits,
vec![Edit {
old: 5..24,
new: 5..8,
}]
);
}
#[gpui::test]
fn test_excerpt_events(cx: &mut App) {
let buffer_1 = cx.new(|cx| Buffer::local(sample_text(10, 3, 'a'), cx));
@@ -3439,7 +3503,7 @@ fn assert_new_snapshot(
subscription: &mut Subscription,
cx: &mut TestAppContext,
expected_diff: &str,
) {
) -> Vec<Edit<usize>> {
let new_snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
let actual_text = new_snapshot.text();
let line_infos = new_snapshot
@@ -3447,12 +3511,10 @@ fn assert_new_snapshot(
.collect::<Vec<_>>();
let actual_diff = format_diff(&actual_text, &line_infos, &Default::default(), None);
pretty_assertions::assert_eq!(actual_diff, expected_diff);
check_edits(
snapshot,
&new_snapshot,
&subscription.consume().into_inner(),
);
let edits = subscription.consume().into_inner();
check_edits(snapshot, &new_snapshot, &edits);
*snapshot = new_snapshot;
edits
}
#[track_caller]

View File

@@ -1,15 +1,21 @@
use std::{
cmp::Ordering,
ops::{Add, AddAssign, Range, Sub},
cmp::Ordering, fmt::Debug, ops::{Add, AddAssign, Range, Sub}
};
/// A zero-indexed point in a text buffer consisting of a row and column.
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash)]
#[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
pub struct Point {
pub row: u32,
pub column: u32,
}
impl Debug for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { row, column } = self;
write!(f, "Point({row}:{column})")
}
}
impl Point {
pub const MAX: Self = Self {
row: u32::MAX,

View File

@@ -486,7 +486,7 @@ struct Edits<'a, D: TextDimension, F: FnMut(&FragmentSummary) -> bool> {
buffer_id: BufferId,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
pub struct Edit<D> {
pub old: Range<D>,
pub new: Range<D>,

View File

@@ -842,6 +842,7 @@ pub trait RangeExt<T> {
fn to_inclusive(&self) -> RangeInclusive<T>;
fn overlaps(&self, other: &Range<T>) -> bool;
fn contains_inclusive(&self, other: &Range<T>) -> bool;
fn clamp(self, other: Self) -> Self;
}
impl<T: Ord + Clone> RangeExt<T> for Range<T> {
@@ -860,6 +861,10 @@ impl<T: Ord + Clone> RangeExt<T> for Range<T> {
fn contains_inclusive(&self, other: &Range<T>) -> bool {
self.start <= other.start && other.end <= self.end
}
fn clamp(self, other: Range<T>) -> Range<T> {
self.start.max(other.start)..self.end.min(other.end)
}
}
impl<T: Ord + Clone> RangeExt<T> for RangeInclusive<T> {
@@ -878,6 +883,11 @@ impl<T: Ord + Clone> RangeExt<T> for RangeInclusive<T> {
fn contains_inclusive(&self, other: &Range<T>) -> bool {
self.start() <= &other.start && &other.end <= self.end()
}
fn clamp(self, other: Self) -> Self {
self.start().clone().max(other.start().clone())
..=self.end().clone().min(other.end().clone())
}
}
/// A way to sort strings with starting numbers numerically first, falling back to alphanumeric one,
@@ -1311,4 +1321,17 @@ Line 3"#
assert_eq!(result[0], (0..6, "héllo")); // 'é' is 2 bytes
assert_eq!(result[1], (10..15, "world")); // '🦀' is 4 bytes
}
#[test]
fn test_range_clamp() {
assert_eq!((5..10).clamp(0..20), 5..10);
assert_eq!((5..10).clamp(0..9), 5..9);
assert_eq!((5..10).clamp(6..20), 6..10);
assert_eq!((5..10).clamp(6..9), 6..9);
assert_eq!((5..=10).clamp(0..=20), 5..=10);
assert_eq!((5..=10).clamp(0..=9), 5..=9);
assert_eq!((5..=10).clamp(6..=20), 6..=10);
assert_eq!((5..=10).clamp(6..=9), 6..=9);
}
}