Compare commits
31 Commits
logging
...
side-by-si
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5f7eb0c70 | ||
|
|
b15c7be652 | ||
|
|
6cde3b2502 | ||
|
|
c12c220d3e | ||
|
|
744de9cefa | ||
|
|
a75230c63f | ||
|
|
9ecc2445e2 | ||
|
|
d3c1a5fe20 | ||
|
|
8dfe7ebdb6 | ||
|
|
a1a4e8ebe5 | ||
|
|
fc448d7d44 | ||
|
|
56c70e9d1c | ||
|
|
52cdc838f5 | ||
|
|
6f3c81ab8c | ||
|
|
6ab75d3d4b | ||
|
|
34d2063456 | ||
|
|
bb56a17798 | ||
|
|
8c008cb9ae | ||
|
|
b2a8c08cef | ||
|
|
e080b3a75a | ||
|
|
f5557a1867 | ||
|
|
11a356b6e6 | ||
|
|
3614b623b8 | ||
|
|
4e1b674e39 | ||
|
|
532dce97f1 | ||
|
|
97ea848ac0 | ||
|
|
49ebf59d05 | ||
|
|
20b457de2a | ||
|
|
a63fada5f5 | ||
|
|
23adeab8d2 | ||
|
|
6e0f65e05a |
@@ -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>(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
});
|
||||
|
||||
@@ -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) =
|
||||
|
||||
1405
crates/editor/src/display_map/filter_map.rs
Normal file
1405
crates/editor/src/display_map/filter_map.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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, |_| {});
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
130
crates/editor/src/split.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user