mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 04:20:46 +00:00
Expand selections to Replace block boundaries (#20092)
Some checks are pending
CI / Check Postgres and Protobuf migrations, mergability (push) Waiting to run
CI / Check formatting and spelling (push) Waiting to run
CI / (macOS) Run Clippy and tests (push) Waiting to run
CI / (Linux) Run Clippy and tests (push) Waiting to run
CI / (Linux) Build Remote Server (push) Waiting to run
CI / (Windows) Run Clippy and tests (push) Waiting to run
CI / Create a macOS bundle (push) Blocked by required conditions
CI / Create a Linux bundle (push) Blocked by required conditions
CI / Create arm64 Linux bundle (push) Blocked by required conditions
Deploy Docs / Deploy Docs (push) Waiting to run
Docs / Check formatting (push) Waiting to run
Some checks are pending
CI / Check Postgres and Protobuf migrations, mergability (push) Waiting to run
CI / Check formatting and spelling (push) Waiting to run
CI / (macOS) Run Clippy and tests (push) Waiting to run
CI / (Linux) Run Clippy and tests (push) Waiting to run
CI / (Linux) Build Remote Server (push) Waiting to run
CI / (Windows) Run Clippy and tests (push) Waiting to run
CI / Create a macOS bundle (push) Blocked by required conditions
CI / Create a Linux bundle (push) Blocked by required conditions
CI / Create arm64 Linux bundle (push) Blocked by required conditions
Deploy Docs / Deploy Docs (push) Waiting to run
Docs / Check formatting (push) Waiting to run
Release Notes: - N/A --------- Co-authored-by: Antonio Scandurra <me@as-cii.com> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com> Co-authored-by: Antonio <antonio@zed.dev> Co-authored-by: Max <max@zed.dev>
This commit is contained in:
parent
b5c38e9a09
commit
773a3b335e
8 changed files with 472 additions and 120 deletions
|
@ -986,6 +986,7 @@ fn editor_blocks(
|
|||
em_width: px(0.),
|
||||
max_width: px(0.),
|
||||
block_id,
|
||||
selected: false,
|
||||
editor_style: &editor::EditorStyle::default(),
|
||||
});
|
||||
let element = element.downcast_mut::<Stateful<Div>>().unwrap();
|
||||
|
|
|
@ -660,7 +660,7 @@ impl DisplaySnapshot {
|
|||
new_start..new_end
|
||||
}
|
||||
|
||||
fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
|
||||
pub fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
|
||||
let inlay_point = self.inlay_snapshot.to_inlay_point(point);
|
||||
let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
|
||||
let tab_point = self.tab_snapshot.to_tab_point(fold_point);
|
||||
|
@ -669,7 +669,7 @@ impl DisplaySnapshot {
|
|||
DisplayPoint(block_point)
|
||||
}
|
||||
|
||||
fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
|
||||
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))
|
||||
}
|
||||
|
@ -691,7 +691,7 @@ impl DisplaySnapshot {
|
|||
|
||||
fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint {
|
||||
let block_point = point.0;
|
||||
let wrap_point = self.block_snapshot.to_wrap_point(block_point);
|
||||
let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
|
||||
let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
|
||||
let fold_point = self.tab_snapshot.to_fold_point(tab_point, bias).0;
|
||||
fold_point.to_inlay_point(&self.fold_snapshot)
|
||||
|
@ -699,7 +699,7 @@ impl DisplaySnapshot {
|
|||
|
||||
pub fn display_point_to_fold_point(&self, point: DisplayPoint, bias: Bias) -> FoldPoint {
|
||||
let block_point = point.0;
|
||||
let wrap_point = self.block_snapshot.to_wrap_point(block_point);
|
||||
let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
|
||||
let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
|
||||
self.tab_snapshot.to_fold_point(tab_point, bias).0
|
||||
}
|
||||
|
@ -990,7 +990,7 @@ impl DisplaySnapshot {
|
|||
pub fn soft_wrap_indent(&self, display_row: DisplayRow) -> Option<u32> {
|
||||
let wrap_row = self
|
||||
.block_snapshot
|
||||
.to_wrap_point(BlockPoint::new(display_row.0, 0))
|
||||
.to_wrap_point(BlockPoint::new(display_row.0, 0), Bias::Left)
|
||||
.row();
|
||||
self.wrap_snapshot.soft_wrap_indent(wrap_row)
|
||||
}
|
||||
|
@ -1222,7 +1222,7 @@ impl DisplayPoint {
|
|||
}
|
||||
|
||||
pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize {
|
||||
let wrap_point = map.block_snapshot.to_wrap_point(self.0);
|
||||
let wrap_point = map.block_snapshot.to_wrap_point(self.0, bias);
|
||||
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);
|
||||
|
@ -2048,6 +2048,112 @@ pub mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_point_translation_with_replace_blocks(cx: &mut gpui::TestAppContext) {
|
||||
cx.background_executor
|
||||
.set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
|
||||
cx.update(|cx| init_test(cx, |_| {}));
|
||||
|
||||
let buffer = cx.update(|cx| MultiBuffer::build_simple("abcde\nfghij\nklmno\npqrst", cx));
|
||||
let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
|
||||
let map = cx.new_model(|cx| {
|
||||
DisplayMap::new(
|
||||
buffer.clone(),
|
||||
font("Courier"),
|
||||
px(16.0),
|
||||
None,
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
FoldPlaceholder::test(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let snapshot = map.update(cx, |map, cx| {
|
||||
map.insert_blocks(
|
||||
[BlockProperties {
|
||||
placement: BlockPlacement::Replace(
|
||||
buffer_snapshot.anchor_before(Point::new(1, 2))
|
||||
..buffer_snapshot.anchor_after(Point::new(2, 3)),
|
||||
),
|
||||
height: 4,
|
||||
style: BlockStyle::Fixed,
|
||||
render: Box::new(|_| div().into_any()),
|
||||
priority: 0,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
map.snapshot(cx)
|
||||
});
|
||||
|
||||
assert_eq!(snapshot.text(), "abcde\n\n\n\n\npqrst");
|
||||
|
||||
let point_to_display_points = [
|
||||
(Point::new(1, 0), DisplayPoint::new(DisplayRow(1), 0)),
|
||||
(Point::new(2, 0), DisplayPoint::new(DisplayRow(1), 0)),
|
||||
(Point::new(3, 0), DisplayPoint::new(DisplayRow(5), 0)),
|
||||
];
|
||||
for (buffer_point, display_point) in point_to_display_points {
|
||||
assert_eq!(
|
||||
snapshot.point_to_display_point(buffer_point, Bias::Left),
|
||||
display_point,
|
||||
"point_to_display_point({:?}, Bias::Left)",
|
||||
buffer_point
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.point_to_display_point(buffer_point, Bias::Right),
|
||||
display_point,
|
||||
"point_to_display_point({:?}, Bias::Right)",
|
||||
buffer_point
|
||||
);
|
||||
}
|
||||
|
||||
let display_points_to_points = [
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(1), 0),
|
||||
Point::new(1, 0),
|
||||
Point::new(2, 5),
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(2), 0),
|
||||
Point::new(1, 0),
|
||||
Point::new(2, 5),
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(3), 0),
|
||||
Point::new(1, 0),
|
||||
Point::new(2, 5),
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(4), 0),
|
||||
Point::new(1, 0),
|
||||
Point::new(2, 5),
|
||||
),
|
||||
(
|
||||
DisplayPoint::new(DisplayRow(5), 0),
|
||||
Point::new(3, 0),
|
||||
Point::new(3, 0),
|
||||
),
|
||||
];
|
||||
for (display_point, left_buffer_point, right_buffer_point) in display_points_to_points {
|
||||
assert_eq!(
|
||||
snapshot.display_point_to_point(display_point, Bias::Left),
|
||||
left_buffer_point,
|
||||
"display_point_to_point({:?}, Bias::Left)",
|
||||
display_point
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.display_point_to_point(display_point, Bias::Right),
|
||||
right_buffer_point,
|
||||
"display_point_to_point({:?}, Bias::Right)",
|
||||
display_point
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// todo(linux) fails due to pixel differences in text rendering
|
||||
#[cfg(target_os = "macos")]
|
||||
#[gpui::test]
|
||||
|
|
|
@ -265,6 +265,7 @@ pub struct BlockContext<'a, 'b> {
|
|||
pub em_width: Pixels,
|
||||
pub line_height: Pixels,
|
||||
pub block_id: BlockId,
|
||||
pub selected: bool,
|
||||
pub editor_style: &'b EditorStyle,
|
||||
}
|
||||
|
||||
|
@ -1311,7 +1312,6 @@ impl BlockSnapshot {
|
|||
let (output_start_row, input_start_row) = cursor.start();
|
||||
let (output_end_row, input_end_row) = cursor.end(&());
|
||||
let output_start = Point::new(output_start_row.0, 0);
|
||||
let output_end = Point::new(output_end_row.0, 0);
|
||||
let input_start = Point::new(input_start_row.0, 0);
|
||||
let input_end = Point::new(input_end_row.0, 0);
|
||||
|
||||
|
@ -1319,10 +1319,10 @@ impl BlockSnapshot {
|
|||
Some(Block::Custom(block))
|
||||
if matches!(block.placement, BlockPlacement::Replace(_)) =>
|
||||
{
|
||||
if bias == Bias::Left {
|
||||
if ((bias == Bias::Left || search_left) && output_start <= point.0)
|
||||
|| (!search_left && output_start >= point.0)
|
||||
{
|
||||
return BlockPoint(output_start);
|
||||
} else {
|
||||
return BlockPoint(Point::new(output_end.row - 1, 0));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
|
@ -1364,12 +1364,7 @@ impl BlockSnapshot {
|
|||
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
if transform.block.is_some() {
|
||||
let wrap_start = WrapPoint::new(cursor.start().0 .0, 0);
|
||||
if wrap_start == wrap_point {
|
||||
BlockPoint::new(cursor.start().1 .0, 0)
|
||||
} else {
|
||||
BlockPoint::new(cursor.end(&()).1 .0 - 1, 0)
|
||||
}
|
||||
BlockPoint::new(cursor.start().1 .0, 0)
|
||||
} else {
|
||||
let (input_start_row, output_start_row) = cursor.start();
|
||||
let input_start = Point::new(input_start_row.0, 0);
|
||||
|
@ -1382,7 +1377,7 @@ impl BlockSnapshot {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
|
||||
pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
|
||||
let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
|
||||
cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
|
||||
if let Some(transform) = cursor.item() {
|
||||
|
@ -1391,7 +1386,9 @@ impl BlockSnapshot {
|
|||
if block.place_below() {
|
||||
let wrap_row = cursor.start().1 .0 - 1;
|
||||
WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
|
||||
} else if block.place_above() || block_point.row == cursor.start().0 .0 {
|
||||
} else if block.place_above() {
|
||||
WrapPoint::new(cursor.start().1 .0, 0)
|
||||
} else if bias == Bias::Left {
|
||||
WrapPoint::new(cursor.start().1 .0, 0)
|
||||
} else {
|
||||
let wrap_row = cursor.end(&()).1 .0 - 1;
|
||||
|
@ -1766,19 +1763,19 @@ mod tests {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
snapshot.to_wrap_point(BlockPoint::new(0, 3)),
|
||||
snapshot.to_wrap_point(BlockPoint::new(0, 3), Bias::Left),
|
||||
WrapPoint::new(0, 3)
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.to_wrap_point(BlockPoint::new(1, 0)),
|
||||
snapshot.to_wrap_point(BlockPoint::new(1, 0), Bias::Left),
|
||||
WrapPoint::new(1, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.to_wrap_point(BlockPoint::new(3, 0)),
|
||||
snapshot.to_wrap_point(BlockPoint::new(3, 0), Bias::Left),
|
||||
WrapPoint::new(1, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.to_wrap_point(BlockPoint::new(7, 0)),
|
||||
snapshot.to_wrap_point(BlockPoint::new(7, 0), Bias::Left),
|
||||
WrapPoint::new(3, 3)
|
||||
);
|
||||
|
||||
|
@ -2616,10 +2613,15 @@ mod tests {
|
|||
|
||||
// Ensure that conversion between block points and wrap points is stable.
|
||||
for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
|
||||
let original_wrap_point = WrapPoint::new(row, 0);
|
||||
let block_point = blocks_snapshot.to_block_point(original_wrap_point);
|
||||
let wrap_point = blocks_snapshot.to_wrap_point(block_point);
|
||||
assert_eq!(blocks_snapshot.to_block_point(wrap_point), block_point);
|
||||
let wrap_point = WrapPoint::new(row, 0);
|
||||
let block_point = blocks_snapshot.to_block_point(wrap_point);
|
||||
let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
|
||||
let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
|
||||
assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
|
||||
assert_eq!(
|
||||
blocks_snapshot.to_block_point(right_wrap_point),
|
||||
block_point
|
||||
);
|
||||
}
|
||||
|
||||
let mut block_point = BlockPoint::new(0, 0);
|
||||
|
@ -2627,10 +2629,12 @@ mod tests {
|
|||
let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
|
||||
let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
|
||||
assert_eq!(
|
||||
blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
|
||||
blocks_snapshot
|
||||
.to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
|
||||
left_point,
|
||||
"wrap point: {:?}",
|
||||
blocks_snapshot.to_wrap_point(left_point)
|
||||
"block point: {:?}, wrap point: {:?}",
|
||||
block_point,
|
||||
blocks_snapshot.to_wrap_point(left_point, Bias::Left)
|
||||
);
|
||||
assert_eq!(
|
||||
left_buffer_point,
|
||||
|
@ -2642,10 +2646,12 @@ mod tests {
|
|||
let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
|
||||
let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
|
||||
assert_eq!(
|
||||
blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
|
||||
blocks_snapshot
|
||||
.to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
|
||||
right_point,
|
||||
"wrap point: {:?}",
|
||||
blocks_snapshot.to_wrap_point(right_point)
|
||||
"block point: {:?}, wrap point: {:?}",
|
||||
block_point,
|
||||
blocks_snapshot.to_wrap_point(right_point, Bias::Right)
|
||||
);
|
||||
assert_eq!(
|
||||
right_buffer_point,
|
||||
|
@ -2681,7 +2687,8 @@ mod tests {
|
|||
|
||||
impl BlockSnapshot {
|
||||
fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
|
||||
self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
|
||||
self.wrap_snapshot
|
||||
.to_point(self.to_wrap_point(point, bias), bias)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,7 +131,9 @@ use project::{
|
|||
use rand::prelude::*;
|
||||
use rpc::{proto::*, ErrorExt};
|
||||
use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
|
||||
use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
|
||||
use selections_collection::{
|
||||
resolve_selections, MutableSelectionsCollection, SelectionsCollection,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
|
||||
use smallvec::SmallVec;
|
||||
|
@ -3484,8 +3486,8 @@ impl Editor {
|
|||
}
|
||||
let new_anchor_selections = new_selections.iter().map(|e| &e.0);
|
||||
let new_selection_deltas = new_selections.iter().map(|e| e.1);
|
||||
let snapshot = this.buffer.read(cx).read(cx);
|
||||
let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
|
||||
let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
|
||||
.zip(new_selection_deltas)
|
||||
.map(|(selection, delta)| Selection {
|
||||
id: selection.id,
|
||||
|
@ -3498,18 +3500,20 @@ impl Editor {
|
|||
|
||||
let mut i = 0;
|
||||
for (position, delta, selection_id, pair) in new_autoclose_regions {
|
||||
let position = position.to_offset(&snapshot) + delta;
|
||||
let start = snapshot.anchor_before(position);
|
||||
let end = snapshot.anchor_after(position);
|
||||
let position = position.to_offset(&map.buffer_snapshot) + delta;
|
||||
let start = map.buffer_snapshot.anchor_before(position);
|
||||
let end = map.buffer_snapshot.anchor_after(position);
|
||||
while let Some(existing_state) = this.autoclose_regions.get(i) {
|
||||
match existing_state.range.start.cmp(&start, &snapshot) {
|
||||
match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
|
||||
Ordering::Less => i += 1,
|
||||
Ordering::Greater => break,
|
||||
Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
|
||||
Ordering::Less => i += 1,
|
||||
Ordering::Equal => break,
|
||||
Ordering::Greater => break,
|
||||
},
|
||||
Ordering::Equal => {
|
||||
match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
|
||||
Ordering::Less => i += 1,
|
||||
Ordering::Equal => break,
|
||||
Ordering::Greater => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.autoclose_regions.insert(
|
||||
|
@ -3522,7 +3526,6 @@ impl Editor {
|
|||
);
|
||||
}
|
||||
|
||||
drop(snapshot);
|
||||
let had_active_inline_completion = this.has_active_inline_completion(cx);
|
||||
this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
|
||||
s.select(new_selections)
|
||||
|
@ -4038,7 +4041,7 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
(selection.clone(), enclosing)
|
||||
(selection, enclosing)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3989,6 +3989,76 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
cx.set_state(
|
||||
&"
|
||||
ˇzero
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
|
||||
// Create a four-line block that replaces three lines of text.
|
||||
cx.update_editor(|editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let snapshot = &snapshot.buffer_snapshot;
|
||||
let placement = BlockPlacement::Replace(
|
||||
snapshot.anchor_after(Point::new(1, 0))..snapshot.anchor_after(Point::new(3, 0)),
|
||||
);
|
||||
editor.insert_blocks(
|
||||
[BlockProperties {
|
||||
placement,
|
||||
height: 4,
|
||||
style: BlockStyle::Sticky,
|
||||
render: Box::new(|_| gpui::div().into_any_element()),
|
||||
priority: 0,
|
||||
}],
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
// Move down so that the cursor touches the block.
|
||||
cx.update_editor(|editor, cx| {
|
||||
editor.move_down(&Default::default(), cx);
|
||||
});
|
||||
cx.assert_editor_state(
|
||||
&"
|
||||
zero
|
||||
«one
|
||||
two
|
||||
threeˇ»
|
||||
four
|
||||
five
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
|
||||
// Move down past the block.
|
||||
cx.update_editor(|editor, cx| {
|
||||
editor.move_down(&Default::default(), cx);
|
||||
});
|
||||
cx.assert_editor_state(
|
||||
&"
|
||||
zero
|
||||
one
|
||||
two
|
||||
three
|
||||
ˇfour
|
||||
five
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_transpose(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
@ -4182,7 +4252,7 @@ async fn test_rewrap(cx: &mut TestAppContext) {
|
|||
// et porta nunc laoreet in. Integer sit amet scelerisque nisi. Lorem ipsum
|
||||
// dolor sit amet, consectetur adipiscing elit. Cras egestas porta metus, eu
|
||||
// viverra ipsum efficitur quis. Donec luctus eros turpis, id vulputate turpis
|
||||
// porttitor id. Aliquam id accumsan eros.ˇˇˇˇ
|
||||
// porttitor id. Aliquam id accumsan eros.ˇ
|
||||
"};
|
||||
|
||||
cx.set_state(unwrapped_text);
|
||||
|
@ -4212,7 +4282,7 @@ async fn test_rewrap(cx: &mut TestAppContext) {
|
|||
let wrapped_text = indoc! {"
|
||||
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit
|
||||
// purus, a ornare lacus gravida vitae. Proin consectetur felis vel purus
|
||||
// auctor, eu lacinia sapien scelerisque.ˇˇ
|
||||
// auctor, eu lacinia sapien scelerisque.ˇ
|
||||
//
|
||||
// Vivamus sit amet neque et quam tincidunt hendrerit. Praesent semper egestas
|
||||
// tellus id dignissim. Pellentesque odio lectus, iaculis ac volutpat et,
|
||||
|
@ -4220,7 +4290,7 @@ async fn test_rewrap(cx: &mut TestAppContext) {
|
|||
// molestie blandit quam, et porta nunc laoreet in. Integer sit amet scelerisque
|
||||
// nisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras egestas
|
||||
// porta metus, eu viverra ipsum efficitur quis. Donec luctus eros turpis, id
|
||||
// vulputate turpis porttitor id. Aliquam id accumsan eros.ˇˇ
|
||||
// vulputate turpis porttitor id. Aliquam id accumsan eros.ˇ
|
||||
"};
|
||||
|
||||
cx.set_state(unwrapped_text);
|
||||
|
|
|
@ -25,7 +25,7 @@ use crate::{
|
|||
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
|
||||
};
|
||||
use client::ParticipantIndex;
|
||||
use collections::{BTreeMap, HashMap};
|
||||
use collections::{BTreeMap, HashMap, HashSet};
|
||||
use git::{blame::BlameEntry, diff::DiffHunkStatus, Oid};
|
||||
use gpui::Subscription;
|
||||
use gpui::{
|
||||
|
@ -809,10 +809,12 @@ impl EditorElement {
|
|||
cx.notify()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn layout_selections(
|
||||
&self,
|
||||
start_anchor: Anchor,
|
||||
end_anchor: Anchor,
|
||||
local_selections: &[Selection<Point>],
|
||||
snapshot: &EditorSnapshot,
|
||||
start_row: DisplayRow,
|
||||
end_row: DisplayRow,
|
||||
|
@ -827,13 +829,9 @@ impl EditorElement {
|
|||
let mut newest_selection_head = None;
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
if editor.show_local_selections {
|
||||
let mut local_selections: Vec<Selection<Point>> = editor
|
||||
.selections
|
||||
.disjoint_in_range(start_anchor..end_anchor, cx);
|
||||
local_selections.extend(editor.selections.pending(cx));
|
||||
let mut layouts = Vec::new();
|
||||
let newest = editor.selections.newest(cx);
|
||||
for selection in local_selections.drain(..) {
|
||||
for selection in local_selections.iter().cloned() {
|
||||
let is_empty = selection.start == selection.end;
|
||||
let is_newest = selection == newest;
|
||||
|
||||
|
@ -996,6 +994,7 @@ impl EditorElement {
|
|||
&self,
|
||||
snapshot: &EditorSnapshot,
|
||||
selections: &[(PlayerColor, Vec<SelectionLayout>)],
|
||||
block_start_rows: &HashSet<DisplayRow>,
|
||||
visible_display_row_range: Range<DisplayRow>,
|
||||
line_layouts: &[LineWithInvisibles],
|
||||
text_hitbox: &Hitbox,
|
||||
|
@ -1015,7 +1014,10 @@ impl EditorElement {
|
|||
let cursor_position = selection.head;
|
||||
|
||||
let in_range = visible_display_row_range.contains(&cursor_position.row());
|
||||
if (selection.is_local && !editor.show_local_cursors(cx)) || !in_range {
|
||||
if (selection.is_local && !editor.show_local_cursors(cx))
|
||||
|| !in_range
|
||||
|| block_start_rows.contains(&cursor_position.row())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2068,14 +2070,14 @@ impl EditorElement {
|
|||
editor_width: Pixels,
|
||||
scroll_width: &mut Pixels,
|
||||
resized_blocks: &mut HashMap<CustomBlockId, u32>,
|
||||
selections: &[Selection<Point>],
|
||||
cx: &mut WindowContext,
|
||||
) -> (AnyElement, Size<Pixels>) {
|
||||
let mut element = match block {
|
||||
Block::Custom(block) => {
|
||||
let align_to = block
|
||||
.start()
|
||||
.to_point(&snapshot.buffer_snapshot)
|
||||
.to_display_point(snapshot);
|
||||
let block_start = block.start().to_point(&snapshot.buffer_snapshot);
|
||||
let block_end = block.end().to_point(&snapshot.buffer_snapshot);
|
||||
let align_to = block_start.to_display_point(snapshot);
|
||||
let anchor_x = text_x
|
||||
+ if rows.contains(&align_to.row()) {
|
||||
line_layouts[align_to.row().minus(rows.start) as usize]
|
||||
|
@ -2085,6 +2087,18 @@ impl EditorElement {
|
|||
.x_for_index(align_to.column() as usize)
|
||||
};
|
||||
|
||||
let selected = selections
|
||||
.binary_search_by(|selection| {
|
||||
if selection.end <= block_start {
|
||||
Ordering::Less
|
||||
} else if selection.start >= block_end {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
})
|
||||
.is_ok();
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.child(block.render(&mut BlockContext {
|
||||
|
@ -2094,6 +2108,7 @@ impl EditorElement {
|
|||
line_height,
|
||||
em_width,
|
||||
block_id,
|
||||
selected,
|
||||
max_width: text_hitbox.size.width.max(*scroll_width),
|
||||
editor_style: &self.style,
|
||||
}))
|
||||
|
@ -2431,6 +2446,7 @@ impl EditorElement {
|
|||
text_x: Pixels,
|
||||
line_height: Pixels,
|
||||
line_layouts: &[LineWithInvisibles],
|
||||
selections: &[Selection<Point>],
|
||||
cx: &mut WindowContext,
|
||||
) -> Result<Vec<BlockLayout>, HashMap<CustomBlockId, u32>> {
|
||||
let (fixed_blocks, non_fixed_blocks) = snapshot
|
||||
|
@ -2467,6 +2483,7 @@ impl EditorElement {
|
|||
editor_width,
|
||||
scroll_width,
|
||||
&mut resized_blocks,
|
||||
selections,
|
||||
cx,
|
||||
);
|
||||
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
|
||||
|
@ -2511,6 +2528,7 @@ impl EditorElement {
|
|||
editor_width,
|
||||
scroll_width,
|
||||
&mut resized_blocks,
|
||||
selections,
|
||||
cx,
|
||||
);
|
||||
|
||||
|
@ -2556,6 +2574,7 @@ impl EditorElement {
|
|||
editor_width,
|
||||
scroll_width,
|
||||
&mut resized_blocks,
|
||||
selections,
|
||||
cx,
|
||||
);
|
||||
|
||||
|
@ -2584,6 +2603,7 @@ impl EditorElement {
|
|||
fn layout_blocks(
|
||||
&self,
|
||||
blocks: &mut Vec<BlockLayout>,
|
||||
block_starts: &mut HashSet<DisplayRow>,
|
||||
hitbox: &Hitbox,
|
||||
line_height: Pixels,
|
||||
scroll_pixel_position: gpui::Point<Pixels>,
|
||||
|
@ -2591,6 +2611,7 @@ impl EditorElement {
|
|||
) {
|
||||
for block in blocks {
|
||||
let mut origin = if let Some(row) = block.row {
|
||||
block_starts.insert(row);
|
||||
hitbox.origin
|
||||
+ point(
|
||||
Pixels::ZERO,
|
||||
|
@ -5101,9 +5122,19 @@ impl Element for EditorElement {
|
|||
cx,
|
||||
);
|
||||
|
||||
let local_selections: Vec<Selection<Point>> =
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let mut selections = editor
|
||||
.selections
|
||||
.disjoint_in_range(start_anchor..end_anchor, cx);
|
||||
selections.extend(editor.selections.pending(cx));
|
||||
selections
|
||||
});
|
||||
|
||||
let (selections, active_rows, newest_selection_head) = self.layout_selections(
|
||||
start_anchor,
|
||||
end_anchor,
|
||||
&local_selections,
|
||||
&snapshot,
|
||||
start_row,
|
||||
end_row,
|
||||
|
@ -5176,6 +5207,7 @@ impl Element for EditorElement {
|
|||
gutter_dimensions.full_width(),
|
||||
line_height,
|
||||
&line_layouts,
|
||||
&local_selections,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
@ -5315,9 +5347,11 @@ impl Element for EditorElement {
|
|||
cx,
|
||||
);
|
||||
|
||||
let mut block_start_rows = HashSet::default();
|
||||
cx.with_element_namespace("blocks", |cx| {
|
||||
self.layout_blocks(
|
||||
&mut blocks,
|
||||
&mut block_start_rows,
|
||||
&hitbox,
|
||||
line_height,
|
||||
scroll_pixel_position,
|
||||
|
@ -5334,6 +5368,7 @@ impl Element for EditorElement {
|
|||
let visible_cursors = self.layout_visible_cursors(
|
||||
&snapshot,
|
||||
&selections,
|
||||
&block_start_rows,
|
||||
start_row..end_row,
|
||||
&line_layouts,
|
||||
&text_hitbox,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
cell::Ref,
|
||||
iter, mem,
|
||||
cmp, iter, mem,
|
||||
ops::{Deref, DerefMut, Range, Sub},
|
||||
sync::Arc,
|
||||
};
|
||||
|
@ -98,9 +98,9 @@ impl SelectionsCollection {
|
|||
&self,
|
||||
cx: &mut AppContext,
|
||||
) -> Option<Selection<D>> {
|
||||
self.pending_anchor()
|
||||
.as_ref()
|
||||
.map(|pending| pending.map(|p| p.summary::<D>(&self.buffer(cx))))
|
||||
let map = self.display_map(cx);
|
||||
let selection = resolve_selections(self.pending_anchor().as_ref(), &map).next();
|
||||
selection
|
||||
}
|
||||
|
||||
pub(crate) fn pending_mode(&self) -> Option<SelectMode> {
|
||||
|
@ -111,12 +111,10 @@ impl SelectionsCollection {
|
|||
where
|
||||
D: 'a + TextDimension + Ord + Sub<D, Output = D>,
|
||||
{
|
||||
let map = self.display_map(cx);
|
||||
let disjoint_anchors = &self.disjoint;
|
||||
let mut disjoint =
|
||||
resolve_multiple::<D, _>(disjoint_anchors.iter(), &self.buffer(cx)).peekable();
|
||||
|
||||
let mut disjoint = resolve_selections::<D, _>(disjoint_anchors.iter(), &map).peekable();
|
||||
let mut pending_opt = self.pending::<D>(cx);
|
||||
|
||||
iter::from_fn(move || {
|
||||
if let Some(pending) = pending_opt.as_mut() {
|
||||
while let Some(next_selection) = disjoint.peek() {
|
||||
|
@ -199,34 +197,57 @@ impl SelectionsCollection {
|
|||
where
|
||||
D: 'a + TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug,
|
||||
{
|
||||
let buffer = self.buffer(cx);
|
||||
let map = self.display_map(cx);
|
||||
let start_ix = match self
|
||||
.disjoint
|
||||
.binary_search_by(|probe| probe.end.cmp(&range.start, &buffer))
|
||||
.binary_search_by(|probe| probe.end.cmp(&range.start, &map.buffer_snapshot))
|
||||
{
|
||||
Ok(ix) | Err(ix) => ix,
|
||||
};
|
||||
let end_ix = match self
|
||||
.disjoint
|
||||
.binary_search_by(|probe| probe.start.cmp(&range.end, &buffer))
|
||||
.binary_search_by(|probe| probe.start.cmp(&range.end, &map.buffer_snapshot))
|
||||
{
|
||||
Ok(ix) => ix + 1,
|
||||
Err(ix) => ix,
|
||||
};
|
||||
resolve_multiple(&self.disjoint[start_ix..end_ix], &buffer).collect()
|
||||
resolve_selections(&self.disjoint[start_ix..end_ix], &map).collect()
|
||||
}
|
||||
|
||||
pub fn all_display(
|
||||
&self,
|
||||
cx: &mut AppContext,
|
||||
) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
|
||||
let display_map = self.display_map(cx);
|
||||
let selections = self
|
||||
.all::<Point>(cx)
|
||||
.into_iter()
|
||||
.map(|selection| selection.map(|point| point.to_display_point(&display_map)))
|
||||
.collect();
|
||||
(display_map, selections)
|
||||
let map = self.display_map(cx);
|
||||
let disjoint_anchors = &self.disjoint;
|
||||
let mut disjoint = resolve_selections_display(disjoint_anchors.iter(), &map).peekable();
|
||||
let mut pending_opt =
|
||||
resolve_selections_display(self.pending_anchor().as_ref(), &map).next();
|
||||
let selections = iter::from_fn(move || {
|
||||
if let Some(pending) = pending_opt.as_mut() {
|
||||
while let Some(next_selection) = disjoint.peek() {
|
||||
if pending.start <= next_selection.end && pending.end >= next_selection.start {
|
||||
let next_selection = disjoint.next().unwrap();
|
||||
if next_selection.start < pending.start {
|
||||
pending.start = next_selection.start;
|
||||
}
|
||||
if next_selection.end > pending.end {
|
||||
pending.end = next_selection.end;
|
||||
}
|
||||
} else if next_selection.end < pending.start {
|
||||
return disjoint.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pending_opt.take()
|
||||
} else {
|
||||
disjoint.next()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(map, selections)
|
||||
}
|
||||
|
||||
pub fn newest_anchor(&self) -> &Selection<Anchor> {
|
||||
|
@ -241,15 +262,18 @@ impl SelectionsCollection {
|
|||
&self,
|
||||
cx: &mut AppContext,
|
||||
) -> Selection<D> {
|
||||
let buffer = self.buffer(cx);
|
||||
self.newest_anchor().map(|p| p.summary::<D>(&buffer))
|
||||
let map = self.display_map(cx);
|
||||
let selection = resolve_selections([self.newest_anchor()], &map)
|
||||
.next()
|
||||
.unwrap();
|
||||
selection
|
||||
}
|
||||
|
||||
pub fn newest_display(&self, cx: &mut AppContext) -> Selection<DisplayPoint> {
|
||||
let display_map = self.display_map(cx);
|
||||
let selection = self
|
||||
.newest_anchor()
|
||||
.map(|point| point.to_display_point(&display_map));
|
||||
let map = self.display_map(cx);
|
||||
let selection = resolve_selections_display([self.newest_anchor()], &map)
|
||||
.next()
|
||||
.unwrap();
|
||||
selection
|
||||
}
|
||||
|
||||
|
@ -265,8 +289,11 @@ impl SelectionsCollection {
|
|||
&self,
|
||||
cx: &mut AppContext,
|
||||
) -> Selection<D> {
|
||||
let buffer = self.buffer(cx);
|
||||
self.oldest_anchor().map(|p| p.summary::<D>(&buffer))
|
||||
let map = self.display_map(cx);
|
||||
let selection = resolve_selections([self.oldest_anchor()], &map)
|
||||
.next()
|
||||
.unwrap();
|
||||
selection
|
||||
}
|
||||
|
||||
pub fn first_anchor(&self) -> Selection<Anchor> {
|
||||
|
@ -538,9 +565,9 @@ impl<'a> MutableSelectionsCollection<'a> {
|
|||
}
|
||||
|
||||
pub fn select_anchors(&mut self, selections: Vec<Selection<Anchor>>) {
|
||||
let buffer = self.buffer.read(self.cx).snapshot(self.cx);
|
||||
let map = self.display_map();
|
||||
let resolved_selections =
|
||||
resolve_multiple::<usize, _>(&selections, &buffer).collect::<Vec<_>>();
|
||||
resolve_selections::<usize, _>(&selections, &map).collect::<Vec<_>>();
|
||||
self.select(resolved_selections);
|
||||
}
|
||||
|
||||
|
@ -650,20 +677,16 @@ impl<'a> MutableSelectionsCollection<'a> {
|
|||
) {
|
||||
let mut changed = false;
|
||||
let display_map = self.display_map();
|
||||
let selections = self
|
||||
.collection
|
||||
.all::<Point>(self.cx)
|
||||
let (_, selections) = self.collection.all_display(self.cx);
|
||||
let selections = selections
|
||||
.into_iter()
|
||||
.map(|selection| {
|
||||
let mut moved_selection =
|
||||
selection.map(|point| point.to_display_point(&display_map));
|
||||
let mut moved_selection = selection.clone();
|
||||
move_selection(&display_map, &mut moved_selection);
|
||||
let moved_selection =
|
||||
moved_selection.map(|display_point| display_point.to_point(&display_map));
|
||||
if selection != moved_selection {
|
||||
changed = true;
|
||||
}
|
||||
moved_selection
|
||||
moved_selection.map(|display_point| display_point.to_point(&display_map))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -804,8 +827,8 @@ impl<'a> MutableSelectionsCollection<'a> {
|
|||
.collect();
|
||||
|
||||
if !adjusted_disjoint.is_empty() {
|
||||
let resolved_selections =
|
||||
resolve_multiple(adjusted_disjoint.iter(), &self.buffer()).collect();
|
||||
let map = self.display_map();
|
||||
let resolved_selections = resolve_selections(adjusted_disjoint.iter(), &map).collect();
|
||||
self.select::<usize>(resolved_selections);
|
||||
}
|
||||
|
||||
|
@ -849,27 +872,76 @@ impl<'a> DerefMut for MutableSelectionsCollection<'a> {
|
|||
}
|
||||
|
||||
// Panics if passed selections are not in order
|
||||
pub(crate) fn resolve_multiple<'a, D, I>(
|
||||
selections: I,
|
||||
snapshot: &MultiBufferSnapshot,
|
||||
) -> impl 'a + Iterator<Item = Selection<D>>
|
||||
where
|
||||
D: TextDimension + Ord + Sub<D, Output = D>,
|
||||
I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
|
||||
{
|
||||
fn resolve_selections_display<'a>(
|
||||
selections: impl 'a + IntoIterator<Item = &'a Selection<Anchor>>,
|
||||
map: &'a DisplaySnapshot,
|
||||
) -> impl 'a + Iterator<Item = Selection<DisplayPoint>> {
|
||||
let (to_summarize, selections) = selections.into_iter().tee();
|
||||
let mut summaries = snapshot
|
||||
.summaries_for_anchors::<D, _>(
|
||||
to_summarize
|
||||
.flat_map(|s| [&s.start, &s.end])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
let mut summaries = map
|
||||
.buffer_snapshot
|
||||
.summaries_for_anchors::<Point, _>(to_summarize.flat_map(|s| [&s.start, &s.end]))
|
||||
.into_iter();
|
||||
selections.map(move |s| Selection {
|
||||
id: s.id,
|
||||
start: summaries.next().unwrap(),
|
||||
end: summaries.next().unwrap(),
|
||||
reversed: s.reversed,
|
||||
goal: s.goal,
|
||||
let mut selections = selections
|
||||
.map(move |s| {
|
||||
let start = summaries.next().unwrap();
|
||||
let end = summaries.next().unwrap();
|
||||
|
||||
let display_start = map.point_to_display_point(start, Bias::Left);
|
||||
let display_end = if start == end {
|
||||
map.point_to_display_point(end, Bias::Right)
|
||||
} else {
|
||||
map.point_to_display_point(end, Bias::Left)
|
||||
};
|
||||
|
||||
Selection {
|
||||
id: s.id,
|
||||
start: display_start,
|
||||
end: display_end,
|
||||
reversed: s.reversed,
|
||||
goal: s.goal,
|
||||
}
|
||||
})
|
||||
.peekable();
|
||||
iter::from_fn(move || {
|
||||
let mut selection = selections.next()?;
|
||||
while let Some(next_selection) = selections.peek() {
|
||||
if selection.end >= next_selection.start {
|
||||
selection.end = cmp::max(selection.end, next_selection.end);
|
||||
selections.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(selection)
|
||||
})
|
||||
}
|
||||
|
||||
// Panics if passed selections are not in order
|
||||
pub(crate) fn resolve_selections<'a, D, I>(
|
||||
selections: I,
|
||||
map: &'a DisplaySnapshot,
|
||||
) -> impl 'a + Iterator<Item = Selection<D>>
|
||||
where
|
||||
D: TextDimension + Clone + Ord + Sub<D, Output = D>,
|
||||
I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
|
||||
{
|
||||
let (to_convert, selections) = resolve_selections_display(selections, map).tee();
|
||||
let mut converted_endpoints =
|
||||
map.buffer_snapshot
|
||||
.dimensions_from_points::<D>(to_convert.flat_map(|s| {
|
||||
let start = map.display_point_to_point(s.start, Bias::Left);
|
||||
let end = map.display_point_to_point(s.end, Bias::Right);
|
||||
[start, end]
|
||||
}));
|
||||
selections.map(move |s| {
|
||||
let start = converted_endpoints.next().unwrap();
|
||||
let end = converted_endpoints.next().unwrap();
|
||||
Selection {
|
||||
id: s.id,
|
||||
start,
|
||||
end,
|
||||
reversed: s.reversed,
|
||||
goal: s.goal,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3083,6 +3083,58 @@ impl MultiBufferSnapshot {
|
|||
summaries
|
||||
}
|
||||
|
||||
pub fn dimensions_from_points<'a, D>(
|
||||
&'a self,
|
||||
points: impl 'a + IntoIterator<Item = Point>,
|
||||
) -> impl 'a + Iterator<Item = D>
|
||||
where
|
||||
D: TextDimension,
|
||||
{
|
||||
let mut cursor = self.excerpts.cursor::<TextSummary>(&());
|
||||
let mut memoized_source_start: Option<Point> = None;
|
||||
let mut points = points.into_iter();
|
||||
std::iter::from_fn(move || {
|
||||
let point = points.next()?;
|
||||
|
||||
// Clear the memoized source start if the point is in a different excerpt than previous.
|
||||
if memoized_source_start.map_or(false, |_| point >= cursor.end(&()).lines) {
|
||||
memoized_source_start = None;
|
||||
}
|
||||
|
||||
// Now determine where the excerpt containing the point starts in its source buffer.
|
||||
// We'll use this value to calculate overshoot next.
|
||||
let source_start = if let Some(source_start) = memoized_source_start {
|
||||
source_start
|
||||
} else {
|
||||
cursor.seek_forward(&point, Bias::Right, &());
|
||||
if let Some(excerpt) = cursor.item() {
|
||||
let source_start = excerpt.range.context.start.to_point(&excerpt.buffer);
|
||||
memoized_source_start = Some(source_start);
|
||||
source_start
|
||||
} else {
|
||||
return Some(D::from_text_summary(cursor.start()));
|
||||
}
|
||||
};
|
||||
|
||||
// First, assume the output dimension is at least the start of the excerpt containing the point
|
||||
let mut output = D::from_text_summary(cursor.start());
|
||||
|
||||
// If the point lands within its excerpt, calculate and add the overshoot in dimension D.
|
||||
if let Some(excerpt) = cursor.item() {
|
||||
let overshoot = point - cursor.start().lines;
|
||||
if !overshoot.is_zero() {
|
||||
let end_in_excerpt = source_start + overshoot;
|
||||
output.add_assign(
|
||||
&excerpt
|
||||
.buffer
|
||||
.text_summary_for_range::<D, _>(source_start..end_in_excerpt),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(output)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn refresh_anchors<'a, I>(&'a self, anchors: I) -> Vec<(usize, Anchor, bool)>
|
||||
where
|
||||
I: 'a + IntoIterator<Item = &'a Anchor>,
|
||||
|
@ -4706,6 +4758,12 @@ impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, TextSummary> for Point {
|
||||
fn cmp(&self, cursor_location: &TextSummary, _: &()) -> cmp::Ordering {
|
||||
Ord::cmp(self, &cursor_location.lines)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<&'a Locator>> for Locator {
|
||||
fn cmp(&self, cursor_location: &Option<&'a Locator>, _: &()) -> cmp::Ordering {
|
||||
Ord::cmp(&Some(self), cursor_location)
|
||||
|
|
Loading…
Reference in a new issue