Compare commits

...

17 Commits

Author SHA1 Message Date
Antonio Scandurra
3906b16cd6 WIP 2024-11-01 19:01:52 +01:00
Antonio Scandurra
896ee3c374 Rename Crease::new to Crease::inline 2024-11-01 18:39:34 +01:00
Richard Feldman
18bda72f1e Rename Crease::Fold to Crease::Inline
Co-Authored-By: Antonio <antonio@zed.dev>
Co-Authored-By: Max <max@zed.dev>
2024-11-01 12:51:56 -04:00
Antonio Scandurra
1ec5cd47a5 WIP 2024-11-01 17:45:35 +01:00
Antonio Scandurra
ab84bd1358 Avoid rendering cursor when within a replace block 2024-11-01 12:12:45 +01:00
Antonio Scandurra
ca16adabd7 Rework point translation when landing inside of a replacement 2024-11-01 12:02:51 +01:00
Max Brunsfeld
9819fe2bc8 WIP - fix infinite loop inside of DisplayMap::next_line_boundary
due to BlockSnapshot::to_block_point going to beginning of block despite right bias
2024-10-31 17:18:55 -07:00
Max Brunsfeld
13d0aeabd9 Start on a unit test about expanding selections to replace blocks
Co-authored-by: Richard <richard@zed.dev>
2024-10-31 16:38:43 -07:00
Max Brunsfeld
ef8f2c445c Only render blocks as selected if they intersect selections
Co-authored-by: Richard <richard@zed.dev>
2024-10-31 16:33:02 -07:00
Antonio Scandurra
7fb13c1cc7 WIP 2024-10-31 19:32:32 +01:00
Antonio Scandurra
c266e2ddba Expand oldest/newest selections 2024-10-31 14:37:23 +01:00
Antonio Scandurra
c4072d3183 Avoid unnecessary conversion when moving selections 2024-10-31 13:43:26 +01:00
Antonio Scandurra
7a1667e190 WIP 2024-10-31 12:35:21 +01:00
Antonio Scandurra
093ebe8001 WIP 2024-10-30 18:27:39 +01:00
Antonio Scandurra
46a76b3849 WIP: start using replace blocks for patches 2024-10-30 17:39:14 +01:00
Antonio Scandurra
8854ecd797 Introduce a new selected field to BlockContext 2024-10-30 17:38:50 +01:00
Antonio Scandurra
26ad0f78d7 Start on expanding selection when it lands inside of a fold or block
Co-Authored-By: Richard <richard@zed.dev>
2024-10-30 16:45:09 +01:00
10 changed files with 675 additions and 312 deletions

View File

@@ -1447,8 +1447,8 @@ struct ScrollPosition {
}
struct PatchViewState {
footer_block_id: CustomBlockId,
crease_id: CreaseId,
block_id: CustomBlockId,
// crease_id: CreaseId,
editor: Option<PatchEditorState>,
update_task: Option<Task<()>>,
}
@@ -1894,7 +1894,7 @@ impl ContextEditor {
);
});
Crease::new(
Crease::inline(
start..end,
placeholder,
fold_toggle("tool-use"),
@@ -2005,7 +2005,7 @@ impl ContextEditor {
let end = buffer
.anchor_in_excerpt(excerpt_id, command.source_range.end)
.unwrap();
Crease::new(start..end, placeholder, render_toggle, render_trailer)
Crease::inline(start..end, placeholder, render_toggle, render_trailer)
}),
cx,
);
@@ -2133,7 +2133,7 @@ impl ContextEditor {
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
let crease = Crease::new(
let crease = Crease::inline(
start..end,
placeholder,
fold_toggle("tool-use"),
@@ -2170,8 +2170,8 @@ impl ContextEditor {
for range in removed {
if let Some(state) = self.patches.remove(range) {
editors_to_close.extend(state.editor.and_then(|state| state.editor.upgrade()));
removed_block_ids.insert(state.footer_block_id);
removed_crease_ids.push(state.crease_id);
removed_block_ids.insert(state.block_id);
// removed_crease_ids.push(state.crease_id);
}
}
@@ -2200,12 +2200,14 @@ impl ContextEditor {
let max_width = cx.max_width;
let gutter_width = cx.gutter_dimensions.full_width();
let block_id = cx.block_id;
let selected = cx.selected;
this.update(&mut **cx, |this, cx| {
this.render_patch_footer(
this.render_patch(
patch_range.clone(),
max_width,
gutter_width,
block_id,
selected,
cx,
)
})
@@ -2215,26 +2217,9 @@ impl ContextEditor {
}
});
let header_placeholder = FoldPlaceholder {
render: {
let this = this.clone();
let patch_range = range.clone();
Arc::new(move |fold_id, _range, cx| {
this.update(cx, |this, cx| {
this.render_patch_header(patch_range.clone(), fold_id, cx)
})
.ok()
.flatten()
.unwrap_or_else(|| Empty.into_any())
})
},
constrain_width: false,
merge_adjacent: false,
};
let should_refold;
if let Some(state) = self.patches.get_mut(&range) {
replaced_blocks.insert(state.footer_block_id, render_block);
replaced_blocks.insert(state.block_id, render_block);
if let Some(editor_state) = &state.editor {
if editor_state.opened_patch != patch {
state.update_task = Some({
@@ -2254,30 +2239,30 @@ impl ContextEditor {
let block_ids = editor.insert_blocks(
[BlockProperties {
height: path_count as u32 + 1,
style: BlockStyle::Flex,
style: BlockStyle::Fixed,
render: render_block,
placement: BlockPlacement::Below(patch_start),
placement: BlockPlacement::Replace(patch_start..patch_end),
priority: 0,
}],
None,
cx,
);
let new_crease_ids = editor.insert_creases(
[Crease::new(
patch_start..patch_end,
header_placeholder.clone(),
fold_toggle("patch-header"),
|_, _, _| Empty.into_any_element(),
)],
cx,
);
// let new_crease_ids = editor.insert_creases(
// [Crease::new(
// patch_start..patch_end,
// header_placeholder.clone(),
// fold_toggle("patch-header"),
// |_, _, _| Empty.into_any_element(),
// )],
// cx,
// );
self.patches.insert(
range.clone(),
PatchViewState {
footer_block_id: block_ids[0],
crease_id: new_crease_ids[0],
block_id: block_ids[0],
// crease_id: new_crease_ids[0],
editor: None,
update_task: None,
},
@@ -2287,8 +2272,8 @@ impl ContextEditor {
}
if should_refold {
editor.unfold_ranges([patch_start..patch_end], true, false, cx);
editor.fold_ranges([(patch_start..patch_end, header_placeholder)], false, cx);
// editor.unfold_ranges([patch_start..patch_end], true, false, cx);
// editor.fold_ranges([(patch_start..patch_end, header_placeholder)], false, cx);
}
}
@@ -2325,7 +2310,7 @@ impl ContextEditor {
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
creases.push(
Crease::new(
Crease::inline(
start..end,
FoldPlaceholder {
render: render_fold_icon_button(
@@ -3100,7 +3085,7 @@ impl ContextEditor {
crease_title,
cx.view().downgrade(),
);
let crease = Crease::new(
let crease = Crease::inline(
anchor_before..anchor_after,
fold_placeholder,
render_quote_selection_output_toggle,
@@ -3295,7 +3280,7 @@ impl ContextEditor {
let buffer_row = MultiBufferRow(start.to_point(&buffer).row);
buffer_rows_to_fold.insert(buffer_row);
Crease::new(
Crease::inline(
start..end,
FoldPlaceholder {
constrain_width: false,
@@ -3446,33 +3431,13 @@ impl ContextEditor {
.unwrap_or_else(|| Cow::Borrowed(DEFAULT_TAB_TITLE))
}
fn render_patch_header(
&self,
range: Range<text::Anchor>,
_id: FoldId,
cx: &mut ViewContext<Self>,
) -> Option<AnyElement> {
let patch = self.context.read(cx).patch_for_range(&range, cx)?;
let theme = cx.theme().clone();
Some(
h_flex()
.px_1()
.py_0p5()
.border_b_1()
.border_color(theme.status().info_border)
.gap_1()
.child(Icon::new(IconName::Diff).size(IconSize::Small))
.child(Label::new(patch.title.clone()).size(LabelSize::Small))
.into_any(),
)
}
fn render_patch_footer(
fn render_patch(
&mut self,
range: Range<text::Anchor>,
max_width: Pixels,
gutter_width: Pixels,
id: BlockId,
selected: bool,
cx: &mut ViewContext<Self>,
) -> Option<AnyElement> {
let snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
@@ -3483,10 +3448,6 @@ impl ContextEditor {
.anchor_in_excerpt(excerpt_id, range.start)
.unwrap();
if !snapshot.intersects_fold(anchor) {
return None;
}
let patch = self.context.read(cx).patch_for_range(&range, cx)?;
let paths = patch
.paths()
@@ -3495,10 +3456,18 @@ impl ContextEditor {
Some(
v_flex()
.bg(cx.theme().colors().editor_background)
.border_1()
.border_color(if selected {
cx.theme().colors().border_focused
} else {
cx.theme().colors().border
})
.id(id)
.pl(gutter_width)
.w(max_width)
.py_2()
.ml(gutter_width)
.p_2()
.rounded_md()
.min_h(cx.line_height() * 3.)
.cursor(CursorStyle::PointingHand)
.on_click(cx.listener(move |this, _, cx| {
this.editor.update(cx, |editor, cx| {
@@ -3508,6 +3477,7 @@ impl ContextEditor {
});
this.focus_active_patch(cx);
}))
.child(Label::new(patch.title.clone()))
.children(paths.into_iter().map(|path| {
h_flex()
.pl_1()

View File

@@ -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();

View File

@@ -255,7 +255,7 @@ impl DisplayMap {
pub fn insert_creases(
&mut self,
creases: impl IntoIterator<Item = Crease>,
creases: impl IntoIterator<Item = Crease<Anchor>>,
cx: &mut ModelContext<Self>,
) -> Vec<CreaseId> {
let snapshot = self.buffer.read(cx).snapshot(cx);
@@ -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)
}
@@ -1054,19 +1054,27 @@ impl DisplaySnapshot {
.unwrap_or(false)
}
pub fn foldable_range(
&self,
buffer_row: MultiBufferRow,
) -> Option<(Range<Point>, FoldPlaceholder)> {
pub fn crease_for_buffer_row(&self, buffer_row: MultiBufferRow) -> Option<Crease<Point>> {
let start = MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot.line_len(buffer_row));
if let Some(crease) = self
.crease_snapshot
.query_row(buffer_row, &self.buffer_snapshot)
{
Some((
crease.range.to_point(&self.buffer_snapshot),
crease.placeholder.clone(),
))
match crease {
Crease::Inline {
range,
placeholder,
render_toggle,
render_trailer,
metadata,
} => Some(Crease::Inline {
range: range.to_point(&self.buffer_snapshot),
placeholder: placeholder.clone(),
render_toggle: render_toggle.clone(),
render_trailer: render_trailer.clone(),
metadata: metadata.clone(),
}),
}
} else if self.starts_indent(MultiBufferRow(start.row))
&& !self.is_line_folded(MultiBufferRow(start.row))
{
@@ -1103,7 +1111,13 @@ impl DisplaySnapshot {
.line_len(MultiBufferRow(row_before_line_breaks.row)),
);
Some((start..row_before_line_breaks, self.fold_placeholder.clone()))
Some(Crease::Inline {
range: start..row_before_line_breaks,
placeholder: self.fold_placeholder,
render_toggle: None,
render_trailer: None,
metadata: None,
})
} else {
None
}
@@ -1222,7 +1236,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 +2062,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]
@@ -2323,7 +2443,7 @@ pub mod tests {
snapshot.anchor_before(Point::new(2, 0))..snapshot.anchor_after(Point::new(3, 3));
map.crease_map.insert(
[Crease::new(
[Crease::inline(
range,
FoldPlaceholder::test(),
|_row, _status, _toggle, _cx| div(),

View File

@@ -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)
}
}
}

View File

@@ -2,7 +2,7 @@ use collections::HashMap;
use gpui::{AnyElement, IntoElement};
use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToPoint};
use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, ops::Range, sync::Arc};
use std::{cmp::Ordering, fmt::Debug, ops::Range, sync::Arc};
use sum_tree::{Bias, SeekTarget, SumTree};
use text::Point;
use ui::{IconName, SharedString, WindowContext};
@@ -45,15 +45,15 @@ impl CreaseSnapshot {
&'a self,
row: MultiBufferRow,
snapshot: &'a MultiBufferSnapshot,
) -> Option<&'a Crease> {
) -> Option<&'a Crease<Anchor>> {
let start = snapshot.anchor_before(Point::new(row.0, 0));
let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
cursor.seek(&start, Bias::Left, snapshot);
while let Some(item) = cursor.item() {
match Ord::cmp(&item.crease.range.start.to_point(snapshot).row, &row.0) {
match Ord::cmp(&item.crease.range().start.to_point(snapshot).row, &row.0) {
Ordering::Less => cursor.next(snapshot),
Ordering::Equal => {
if item.crease.range.start.is_valid(snapshot) {
if item.crease.range().start.is_valid(snapshot) {
return Some(&item.crease);
} else {
cursor.next(snapshot);
@@ -69,7 +69,7 @@ impl CreaseSnapshot {
&'a self,
range: Range<MultiBufferRow>,
snapshot: &'a MultiBufferSnapshot,
) -> impl 'a + Iterator<Item = &'a Crease> {
) -> impl 'a + Iterator<Item = &'a Crease<Anchor>> {
let start = snapshot.anchor_before(Point::new(range.start.0, 0));
let mut cursor = self.creases.cursor::<ItemSummary>(snapshot);
cursor.seek(&start, Bias::Left, snapshot);
@@ -77,8 +77,9 @@ impl CreaseSnapshot {
std::iter::from_fn(move || {
while let Some(item) = cursor.item() {
cursor.next(snapshot);
let crease_start = item.crease.range.start.to_point(snapshot);
let crease_end = item.crease.range.end.to_point(snapshot);
let crease_range = item.crease.range();
let crease_start = crease_range.start.to_point(snapshot);
let crease_end = crease_range.end.to_point(snapshot);
if crease_end.row > range.end.0 {
continue;
}
@@ -99,8 +100,9 @@ impl CreaseSnapshot {
cursor.next(snapshot);
while let Some(item) = cursor.item() {
let start_point = item.crease.range.start.to_point(snapshot);
let end_point = item.crease.range.end.to_point(snapshot);
let crease_range = item.crease.range();
let start_point = crease_range.start.to_point(snapshot);
let end_point = crease_range.end.to_point(snapshot);
results.push((item.id, start_point..end_point));
cursor.next(snapshot);
}
@@ -123,12 +125,14 @@ type RenderTrailerFn =
Arc<dyn Send + Sync + Fn(MultiBufferRow, bool, &mut WindowContext) -> AnyElement>;
#[derive(Clone)]
pub struct Crease {
pub range: Range<Anchor>,
pub placeholder: FoldPlaceholder,
pub render_toggle: RenderToggleFn,
pub render_trailer: RenderTrailerFn,
pub metadata: Option<CreaseMetadata>,
pub enum Crease<T> {
Inline {
range: Range<T>,
placeholder: FoldPlaceholder,
render_toggle: RenderToggleFn,
render_trailer: RenderTrailerFn,
metadata: Option<CreaseMetadata>,
},
}
/// Metadata about a [`Crease`], that is used for serialization.
@@ -138,9 +142,9 @@ pub struct CreaseMetadata {
pub label: SharedString,
}
impl Crease {
pub fn new<RenderToggle, ToggleElement, RenderTrailer, TrailerElement>(
range: Range<Anchor>,
impl<T> Crease<T> {
pub fn inline<RenderToggle, ToggleElement, RenderTrailer, TrailerElement>(
range: Range<T>,
placeholder: FoldPlaceholder,
render_toggle: RenderToggle,
render_trailer: RenderTrailer,
@@ -164,7 +168,7 @@ impl Crease {
+ 'static,
TrailerElement: IntoElement,
{
Crease {
Crease::Inline {
range,
placeholder,
render_toggle: Arc::new(move |row, folded, toggle, cx| {
@@ -177,24 +181,52 @@ impl Crease {
}
}
pub fn with_metadata(mut self, metadata: CreaseMetadata) -> Self {
self.metadata = Some(metadata);
self
pub fn with_metadata(self, metadata: CreaseMetadata) -> Self {
match self {
Crease::Inline {
range,
placeholder,
render_toggle,
render_trailer,
..
} => Crease::Inline {
range,
placeholder,
render_toggle,
render_trailer,
metadata: Some(metadata),
},
}
}
pub fn range(&self) -> &Range<T> {
match self {
Crease::Inline { range, .. } => range,
}
}
}
impl std::fmt::Debug for Crease {
impl<T> std::fmt::Debug for Crease<T>
where
T: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Crease")
.field("range", &self.range)
.finish()
match self {
Crease::Inline {
range, metadata, ..
} => f
.debug_struct("Crease::Inline")
.field("range", range)
.field("metadata", metadata)
.finish_non_exhaustive(),
}
}
}
#[derive(Clone, Debug)]
struct CreaseItem {
id: CreaseId,
crease: Crease,
crease: Crease<Anchor>,
}
impl CreaseMap {
@@ -204,7 +236,7 @@ impl CreaseMap {
pub fn insert(
&mut self,
creases: impl IntoIterator<Item = Crease>,
creases: impl IntoIterator<Item = Crease<Anchor>>,
snapshot: &MultiBufferSnapshot,
) -> Vec<CreaseId> {
let mut new_ids = Vec::new();
@@ -212,11 +244,12 @@ impl CreaseMap {
let mut new_creases = SumTree::new(snapshot);
let mut cursor = self.snapshot.creases.cursor::<ItemSummary>(snapshot);
for crease in creases {
new_creases.append(cursor.slice(&crease.range, Bias::Left, snapshot), snapshot);
let crease_range = crease.range().clone();
new_creases.append(cursor.slice(&crease_range, Bias::Left, snapshot), snapshot);
let id = self.next_id;
self.next_id.0 += 1;
self.id_to_range.insert(id, crease.range.clone());
self.id_to_range.insert(id, crease_range);
new_creases.push(CreaseItem { crease, id }, snapshot);
new_ids.push(id);
}
@@ -293,7 +326,7 @@ impl sum_tree::Item for CreaseItem {
fn summary(&self, _cx: &MultiBufferSnapshot) -> Self::Summary {
ItemSummary {
range: self.crease.range.clone(),
range: self.crease.range().clone(),
}
}
}
@@ -326,13 +359,13 @@ mod test {
// Insert creases
let creases = [
Crease::new(
Crease::inline(
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
FoldPlaceholder::test(),
|_row, _folded, _toggle, _cx| div(),
|_row, _folded, _cx| div(),
),
Crease::new(
Crease::inline(
snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
FoldPlaceholder::test(),
|_row, _folded, _toggle, _cx| div(),
@@ -372,19 +405,19 @@ mod test {
let mut crease_map = CreaseMap::new(&snapshot);
let creases = [
Crease::new(
Crease::inline(
snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_after(Point::new(1, 5)),
FoldPlaceholder::test(),
|_row, _folded, _toggle, _cx| div(),
|_row, _folded, _cx| div(),
),
Crease::new(
Crease::inline(
snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_after(Point::new(3, 5)),
FoldPlaceholder::test(),
|_row, _folded, _toggle, _cx| div(),
|_row, _folded, _cx| div(),
),
Crease::new(
Crease::inline(
snapshot.anchor_before(Point::new(5, 0))..snapshot.anchor_after(Point::new(5, 5)),
FoldPlaceholder::test(),
|_row, _folded, _toggle, _cx| div(),
@@ -402,12 +435,12 @@ mod test {
let range = MultiBufferRow(2)..MultiBufferRow(5);
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
assert_eq!(creases.len(), 1);
assert_eq!(creases[0].range.start.to_point(&snapshot).row, 3);
assert_eq!(creases[0].range().start.to_point(&snapshot).row, 3);
let range = MultiBufferRow(0)..MultiBufferRow(2);
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();
assert_eq!(creases.len(), 1);
assert_eq!(creases[0].range.start.to_point(&snapshot).row, 1);
assert_eq!(creases[0].range().start.to_point(&snapshot).row, 1);
let range = MultiBufferRow(6)..MultiBufferRow(7);
let creases: Vec<_> = crease_snapshot.creases_in_range(range, &snapshot).collect();

View File

@@ -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;
@@ -3471,8 +3473,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,
@@ -3485,18 +3487,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(
@@ -3509,7 +3513,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)
@@ -4025,7 +4028,7 @@ impl Editor {
}
}
(selection.clone(), enclosing)
(selection, enclosing)
})
}
@@ -6703,7 +6706,7 @@ impl Editor {
buffer.edit([(range, text)], None, cx);
}
});
this.fold_ranges(refold_ranges, true, cx);
this.fold_creases(refold_ranges, true, cx);
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select(new_selections);
})
@@ -6797,7 +6800,7 @@ impl Editor {
buffer.edit([(range, text)], None, cx);
}
});
this.fold_ranges(refold_ranges, true, cx);
this.fold_creases(refold_ranges, true, cx);
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
});
}
@@ -10700,7 +10703,7 @@ impl Editor {
}
pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
let mut fold_ranges = Vec::new();
let mut to_fold = Vec::new();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all_adjusted(cx);
@@ -10712,12 +10715,10 @@ impl Editor {
let mut found = false;
let mut row = range.start.row;
while row <= range.end.row {
if let Some((foldable_range, fold_text)) =
{ display_map.foldable_range(MultiBufferRow(row)) }
{
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
found = true;
row = foldable_range.end.row + 1;
fold_ranges.push((foldable_range, fold_text));
row = crease.range().end.row + 1;
to_fold.push(crease);
} else {
row += 1
}
@@ -10728,11 +10729,9 @@ impl Editor {
}
for row in (0..=range.start.row).rev() {
if let Some((foldable_range, fold_text)) =
display_map.foldable_range(MultiBufferRow(row))
{
if foldable_range.end.row >= buffer_start_row {
fold_ranges.push((foldable_range, fold_text));
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
if crease.range().end.row >= buffer_start_row {
to_fold.push(crease);
if row <= range.start.row {
break;
}
@@ -10741,26 +10740,29 @@ impl Editor {
}
}
self.fold_ranges(fold_ranges, true, cx);
self.fold_creases(to_fold, true, cx);
}
fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
let fold_at_level = fold_at.level;
let snapshot = self.buffer.read(cx).snapshot(cx);
let mut fold_ranges = Vec::new();
let mut to_fold = Vec::new();
let mut stack = vec![(0, snapshot.max_buffer_row().0, 1)];
while let Some((mut start_row, end_row, current_level)) = stack.pop() {
while start_row < end_row {
match self.snapshot(cx).foldable_range(MultiBufferRow(start_row)) {
Some(foldable_range) => {
let nested_start_row = foldable_range.0.start.row + 1;
let nested_end_row = foldable_range.0.end.row;
match self
.snapshot(cx)
.crease_for_buffer_row(MultiBufferRow(start_row))
{
Some(crease) => {
let nested_start_row = crease.range().start.row + 1;
let nested_end_row = crease.range().end.row;
if current_level < fold_at_level {
stack.push((nested_start_row, nested_end_row, current_level + 1));
} else if current_level == fold_at_level {
fold_ranges.push(foldable_range);
to_fold.push(crease);
}
start_row = nested_end_row + 1;
@@ -10770,7 +10772,7 @@ impl Editor {
}
}
self.fold_ranges(fold_ranges, true, cx);
self.fold_creases(to_fold, true, cx);
}
pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
@@ -10778,16 +10780,18 @@ impl Editor {
let snapshot = self.buffer.read(cx).snapshot(cx);
for row in 0..snapshot.max_buffer_row().0 {
if let Some(foldable_range) = self.snapshot(cx).foldable_range(MultiBufferRow(row)) {
if let Some(foldable_range) =
self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
{
fold_ranges.push(foldable_range);
}
}
self.fold_ranges(fold_ranges, true, cx);
self.fold_creases(fold_ranges, true, cx);
}
pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
let mut fold_ranges = Vec::new();
let mut to_fold = Vec::new();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all_adjusted(cx);
@@ -10798,11 +10802,9 @@ impl Editor {
if range.start.row != range.end.row {
let mut found = false;
for row in range.start.row..=range.end.row {
if let Some((foldable_range, fold_text)) =
{ display_map.foldable_range(MultiBufferRow(row)) }
{
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
found = true;
fold_ranges.push((foldable_range, fold_text));
to_fold.push(crease);
}
}
if found {
@@ -10811,11 +10813,9 @@ impl Editor {
}
for row in (0..=range.start.row).rev() {
if let Some((foldable_range, fold_text)) =
display_map.foldable_range(MultiBufferRow(row))
{
if foldable_range.end.row >= buffer_start_row {
fold_ranges.push((foldable_range, fold_text));
if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
if crease.range().end.row >= buffer_start_row {
to_fold.push(crease);
} else {
break;
}
@@ -10823,21 +10823,21 @@ impl Editor {
}
}
self.fold_ranges(fold_ranges, true, cx);
self.fold_creases(to_fold, true, cx);
}
pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
let buffer_row = fold_at.buffer_row;
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
let autoscroll = self
.selections
.all::<Point>(cx)
.iter()
.any(|selection| fold_range.overlaps(&selection.range()));
.any(|selection| crease.range().overlaps(&selection.range()));
self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
self.fold_creases([crease], autoscroll, cx);
}
}
@@ -10924,12 +10924,12 @@ impl Editor {
(s.start..s.end, display_map.fold_placeholder.clone())
}
});
self.fold_ranges(ranges, true, cx);
self.fold_creases(ranges, true, cx);
}
pub fn fold_ranges<T: ToOffset + Clone>(
pub fn fold_creases<T: ToOffset + Clone>(
&mut self,
ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
ranges: impl IntoIterator<Item = Crease<T>>,
auto_scroll: bool,
cx: &mut ViewContext<Self>,
) {
@@ -11099,7 +11099,7 @@ impl Editor {
pub fn insert_creases(
&mut self,
creases: impl IntoIterator<Item = Crease>,
creases: impl IntoIterator<Item = Crease<Anchor>>,
cx: &mut ViewContext<Self>,
) -> Vec<CreaseId> {
self.display_map
@@ -13583,24 +13583,22 @@ impl EditorSnapshot {
.crease_snapshot
.query_row(buffer_row, &self.buffer_snapshot)
{
let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
if folded {
editor.update(cx, |editor, cx| {
editor.fold_at(&crate::FoldAt { buffer_row }, cx)
});
} else {
editor.update(cx, |editor, cx| {
editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
match crease {
Crease::Inline { render_toggle, .. } => {
let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
if folded {
editor.update(cx, |editor, cx| {
editor.fold_at(&crate::FoldAt { buffer_row }, cx)
});
} else {
editor.update(cx, |editor, cx| {
editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
});
}
});
Some((render_toggle)(buffer_row, folded, toggle_callback, cx))
}
});
Some((crease.render_toggle)(
buffer_row,
folded,
toggle_callback,
cx,
))
}
} else if folded
|| (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
{
@@ -13627,10 +13625,10 @@ impl EditorSnapshot {
cx: &mut WindowContext,
) -> Option<AnyElement> {
let folded = self.is_line_folded(buffer_row);
let crease = self
let Crease::Inline { render_trailer, .. } = self
.crease_snapshot
.query_row(buffer_row, &self.buffer_snapshot)?;
Some((crease.render_trailer)(buffer_row, folded, cx))
Some((render_trailer)(buffer_row, folded, cx))
}
}

View File

@@ -592,7 +592,7 @@ fn test_clone(cx: &mut TestAppContext) {
_ = editor.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
editor.fold_ranges(
editor.fold_creases(
[
(Point::new(1, 0)..Point::new(2, 0), FoldPlaceholder::test()),
(Point::new(3, 0)..Point::new(4, 0), FoldPlaceholder::test()),
@@ -1279,7 +1279,7 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
assert_eq!('α'.len_utf8(), 2);
_ = view.update(cx, |view, cx| {
view.fold_ranges(
view.fold_creases(
vec![
(Point::new(0, 6)..Point::new(0, 12), FoldPlaceholder::test()),
(Point::new(1, 2)..Point::new(1, 4), FoldPlaceholder::test()),
@@ -3871,7 +3871,7 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
build_editor(buffer, cx)
});
_ = view.update(cx, |view, cx| {
view.fold_ranges(
view.fold_creases(
vec![
(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
@@ -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, |_| {});
@@ -4700,7 +4770,7 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) {
build_editor(buffer, cx)
});
_ = view.update(cx, |view, cx| {
view.fold_ranges(
view.fold_creases(
vec![
(Point::new(0, 2)..Point::new(1, 2), FoldPlaceholder::test()),
(Point::new(2, 3)..Point::new(4, 1), FoldPlaceholder::test()),
@@ -5381,7 +5451,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
// Ensure that we keep expanding the selection if the larger selection starts or ends within
// a fold.
editor.update(cx, |view, cx| {
view.fold_ranges(
view.fold_creases(
vec![
(
Point::new(0, 21)..Point::new(0, 24),
@@ -12928,7 +12998,7 @@ fn test_crease_insertion_and_rendering(cx: &mut TestAppContext) {
callback: Arc<dyn Fn(bool, &mut WindowContext) + Send + Sync>,
}
let crease = Crease::new(
let crease = Crease::inline(
range,
FoldPlaceholder::test(),
{

View File

@@ -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::{
@@ -812,6 +812,7 @@ impl EditorElement {
&self,
start_anchor: Anchor,
end_anchor: Anchor,
local_selections: &[Selection<Point>],
snapshot: &EditorSnapshot,
start_row: DisplayRow,
end_row: DisplayRow,
@@ -826,13 +827,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;
@@ -995,6 +992,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,
@@ -1014,7 +1012,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;
}
@@ -2067,14 +2068,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]
@@ -2084,6 +2085,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 {
@@ -2093,6 +2106,7 @@ impl EditorElement {
line_height,
em_width,
block_id,
selected,
max_width: text_hitbox.size.width.max(*scroll_width),
editor_style: &self.style,
}))
@@ -2430,6 +2444,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
@@ -2466,6 +2481,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);
@@ -2510,6 +2526,7 @@ impl EditorElement {
editor_width,
scroll_width,
&mut resized_blocks,
selections,
cx,
);
@@ -2555,6 +2572,7 @@ impl EditorElement {
editor_width,
scroll_width,
&mut resized_blocks,
selections,
cx,
);
@@ -2583,6 +2601,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>,
@@ -2590,6 +2609,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,
@@ -5100,9 +5120,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,
@@ -5175,6 +5205,7 @@ impl Element for EditorElement {
gutter_dimensions.full_width(),
line_height,
&line_layouts,
&local_selections,
cx,
)
});
@@ -5314,9 +5345,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,
@@ -5333,6 +5366,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,

View File

@@ -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> {
@@ -316,15 +343,14 @@ impl SelectionsCollection {
#[cfg(any(test, feature = "test-support"))]
pub fn display_ranges(&self, cx: &mut AppContext) -> Vec<Range<DisplayPoint>> {
let display_map = self.display_map(cx);
self.disjoint_anchors()
.iter()
.chain(self.pending_anchor().as_ref())
.map(|s| {
if s.reversed {
s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
self.all_display(cx)
.1
.into_iter()
.map(|selection| {
if selection.reversed {
selection.end..selection.start
} else {
s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
selection.start..selection.end
}
})
.collect()
@@ -538,9 +564,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 +676,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 +826,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 +871,77 @@ 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.clone());
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]
}))
.into_iter();
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,
}
})
}

View File

@@ -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)