Compare commits

...

26 Commits

Author SHA1 Message Date
Antonio Scandurra
967dd02414 WIP: start using replace blocks for patches 2024-10-24 16:33:15 +02:00
Antonio Scandurra
a32dd07e27 Fix BlockMap::buffer_rows now that we have replacement blocks 2024-10-24 12:41:26 +02:00
Antonio Scandurra
57333fdcf0 Ensure clipping BlockPoints right works correctly 2024-10-24 12:17:35 +02:00
Antonio Scandurra
dd5b4f23e6 Ensure replace blocks are at least one line tall
Otherwise it becomes impossible to address certain ranges in the buffer.
2024-10-24 11:46:14 +02:00
Antonio Scandurra
397c19c103 WIP: start on fixing point translation in BlockMap
SEED=437554 SIMPLE_TEXT=1 OPERATIONS=1 cargo test --package=editor random_blocks

Co-Authored-By: Richard <richard@zed.dev>
2024-10-23 17:08:18 +02:00
Antonio Scandurra
5f515089ad Fix BlockMap::longest_row 2024-10-23 14:48:09 +02:00
Antonio Scandurra
f8fe881631 Include zero-height blocks in expected blocks 2024-10-23 13:44:27 +02:00
Antonio Scandurra
10eed50765 Include replacements in expected blocks 2024-10-23 13:37:32 +02:00
Antonio Scandurra
6b44b11fc4 Fix randomized tests (passing with 20 operations, 1m iterations)
Now we need to uncomment other parts of the randomized test (e.g., buffer rows)
2024-10-23 12:47:57 +02:00
Max Brunsfeld
2bb7567cbb Re-enable non-singleton multibuffers in random blocks test
failure:RUST_LOG=info SIMPLE_TEXT=1 OPERATIONS=2 ITERATIONS=1 SEED=26086 ct -p editor random_blocks -- --nocapture
2024-10-22 17:06:32 -07:00
Max Brunsfeld
0d8b8492f0 Merge branch 'main' into block-placement-2 2024-10-22 16:34:56 -07:00
Richard Feldman
4bf61f892f Get text chunks assertions in BlockMap randomized tests passing
Co-authored-by: Richard <richard@zed.dev>
Co-authored-by: Antonio <antonio@zed.dev>
2024-10-22 16:20:15 -07:00
Antonio Scandurra
fc524ad02a WIP: failing test, see below
RUST_LOG=info SEED=22484 SIMPLE_TEXT=1 ITERATIONS=1 OPERATIONS=5 cargo test -p editor test_random_blocks -- --nocapture
2024-10-22 18:13:39 +02:00
Antonio Scandurra
607fb34124 Clarify how the expected text is constructed in randomized test
Co-Authored-By: Nathan <nathan@zed.dev>
2024-10-22 17:01:01 +02:00
Antonio Scandurra
a931c7ac06 Make BlockMap::chunks randomized tests pass again with OPERATIONS=1 2024-10-22 14:00:45 +02:00
Antonio Scandurra
9322069dce Fix some randomized test failures with OPERATIONS=1
I disabled all assertions but the ones that exercise `chunks`, as I'd like to
make sure that things work at a basic level. We should re-enable the remaining
assertions once basic features work correctly.
2024-10-22 13:42:30 +02:00
Antonio Scandurra
52732e75a0 Sort and deduplicate blocks correctly 2024-10-22 10:58:35 +02:00
Nathan Sobo
192aa78f94 Start on including replace blocks in randomized test
Simple failure with OPERATIONS=1

Co-Authored-By: Max <max@zed.dev>
2024-10-21 18:41:34 -06:00
Max Brunsfeld
c50b572faf Avoid subtraction overflow in BlockChunks::next
When the end of a block is after the max row, don't overflow.

Co-authored-by: Nathan <nathan@zed.dev>
2024-10-21 16:47:20 -07:00
Max Brunsfeld
2e54737a23 Get existing BlockMap randomized test passing
Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Richard <richard@zed.dev>
2024-10-21 16:15:22 -07:00
Antonio Scandurra
2e0ae8f1e1 Checkpoint
Co-Authored-By: Max <max@zed.dev>
Co-Authored-By: Richard <richard@zed.dev>
Co-Authored-By: Marshall <marshall@zed.dev>
Co-Authored-By: Nathan <nathan@zed.dev>
2024-10-21 20:11:12 +02:00
Antonio Scandurra
f92c892b49 WIP 2024-10-21 17:51:04 +02:00
Antonio Scandurra
1504f9d661 Seek input chunks when advancing to the next isomorphic transform
Notably, this makes our basic unit test for replacing lines pass!

Co-Authored-By: Richard <richard@zed.dev>
2024-10-21 17:35:19 +02:00
Antonio Scandurra
a5e6b222dd WIP: Introduce a new BlockPlacement::Replace variant
Co-Authored-By: Richard <richard@zed.dev>
2024-10-21 15:54:37 +02:00
Antonio Scandurra
23e3539f54 Checkpoint: compiles and tests pass 2024-10-21 14:24:01 +02:00
Antonio Scandurra
3303be678e WIP: Replace BlockDisposition with BlockPlacement
We still need to update users of this API and run tests.
2024-10-21 14:04:06 +02:00
13 changed files with 1114 additions and 639 deletions

View File

@@ -26,8 +26,8 @@ use collections::{BTreeSet, HashMap, HashSet};
use editor::{
actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt},
display_map::{
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, Crease,
CreaseMetadata, CustomBlockId, FoldId, RenderBlock, ToDisplayPoint,
BlockContext, BlockId, BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata,
CustomBlockId, FoldId, RenderBlock, ToDisplayPoint,
},
scroll::{Autoscroll, AutoscrollStrategy},
Anchor, Editor, EditorEvent, ProposedChangeLocation, ProposedChangesEditor, RowExt,
@@ -1446,8 +1446,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<()>>,
}
@@ -2009,13 +2009,12 @@ impl ContextEditor {
})
.map(|(command, error_message)| BlockProperties {
style: BlockStyle::Fixed,
position: Anchor {
height: 1,
placement: BlockPlacement::Below(Anchor {
buffer_id: Some(buffer_id),
excerpt_id,
text_anchor: command.source_range.start,
},
height: 1,
disposition: BlockDisposition::Below,
}),
render: slash_command_error_block_renderer(error_message),
priority: 0,
}),
@@ -2159,8 +2158,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);
}
}
@@ -2190,7 +2189,7 @@ impl ContextEditor {
let gutter_width = cx.gutter_dimensions.full_width();
let block_id = cx.block_id;
this.update(&mut **cx, |this, cx| {
this.render_patch_footer(
this.render_patch(
patch_range.clone(),
max_width,
gutter_width,
@@ -2204,26 +2203,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({
@@ -2242,32 +2224,31 @@ impl ContextEditor {
} else {
let block_ids = editor.insert_blocks(
[BlockProperties {
position: patch_start,
height: path_count as u32 + 1,
style: BlockStyle::Flex,
style: BlockStyle::Fixed,
render: render_block,
disposition: BlockDisposition::Below,
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,
},
@@ -2277,8 +2258,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);
}
}
@@ -2731,12 +2712,13 @@ impl ContextEditor {
})
};
let create_block_properties = |message: &Message| BlockProperties {
position: buffer
.anchor_in_excerpt(excerpt_id, message.anchor_range.start)
.unwrap(),
height: 2,
style: BlockStyle::Sticky,
disposition: BlockDisposition::Above,
placement: BlockPlacement::Above(
buffer
.anchor_in_excerpt(excerpt_id, message.anchor_range.start)
.unwrap(),
),
priority: usize::MAX,
render: render_block(MessageMetadata::from(message)),
};
@@ -3372,7 +3354,7 @@ impl ContextEditor {
let anchor = buffer.anchor_in_excerpt(excerpt_id, anchor).unwrap();
let image = render_image.clone();
anchor.is_valid(&buffer).then(|| BlockProperties {
position: anchor,
placement: BlockPlacement::Above(anchor),
height: MAX_HEIGHT_IN_LINES,
style: BlockStyle::Sticky,
render: Box::new(move |cx| {
@@ -3393,8 +3375,6 @@ impl ContextEditor {
)
.into_any_element()
}),
disposition: BlockDisposition::Above,
priority: 0,
})
})
@@ -3434,28 +3414,7 @@ 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,
@@ -3471,10 +3430,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()
@@ -3483,10 +3438,13 @@ impl ContextEditor {
Some(
v_flex()
.border_1()
.border_color(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| {
@@ -3496,6 +3454,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

@@ -9,7 +9,7 @@ use collections::{hash_map, HashMap, HashSet, VecDeque};
use editor::{
actions::{MoveDown, MoveUp, SelectAll},
display_map::{
BlockContext, BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
ToDisplayPoint,
},
Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorElement, EditorEvent, EditorMode,
@@ -446,15 +446,14 @@ impl InlineAssistant {
let assist_blocks = vec![
BlockProperties {
style: BlockStyle::Sticky,
position: range.start,
placement: BlockPlacement::Above(range.start),
height: prompt_editor_height,
render: build_assist_editor_renderer(prompt_editor),
disposition: BlockDisposition::Above,
priority: 0,
},
BlockProperties {
style: BlockStyle::Sticky,
position: range.end,
placement: BlockPlacement::Below(range.end),
height: 0,
render: Box::new(|cx| {
v_flex()
@@ -464,7 +463,6 @@ impl InlineAssistant {
.border_color(cx.theme().status().info_border)
.into_any_element()
}),
disposition: BlockDisposition::Below,
priority: 0,
},
];
@@ -1179,7 +1177,7 @@ impl InlineAssistant {
let height =
deleted_lines_editor.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1);
new_blocks.push(BlockProperties {
position: new_row,
placement: BlockPlacement::Above(new_row),
height,
style: BlockStyle::Flex,
render: Box::new(move |cx| {
@@ -1191,7 +1189,6 @@ impl InlineAssistant {
.child(deleted_lines_editor.clone())
.into_any_element()
}),
disposition: BlockDisposition::Above,
priority: 0,
});
}

View File

@@ -9,7 +9,7 @@ use anyhow::Result;
use collections::{BTreeSet, HashSet};
use editor::{
diagnostic_block_renderer,
display_map::{BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, RenderBlock},
display_map::{BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock},
highlight_diagnostic_message,
scroll::Autoscroll,
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
@@ -439,11 +439,10 @@ impl ProjectDiagnosticsEditor {
primary.message.split('\n').next().unwrap().to_string();
group_state.block_count += 1;
blocks_to_add.push(BlockProperties {
position: header_position,
placement: BlockPlacement::Above(header_position),
height: 2,
style: BlockStyle::Sticky,
render: diagnostic_header_renderer(primary),
disposition: BlockDisposition::Above,
priority: 0,
});
}
@@ -459,13 +458,15 @@ impl ProjectDiagnosticsEditor {
if !diagnostic.message.is_empty() {
group_state.block_count += 1;
blocks_to_add.push(BlockProperties {
position: (excerpt_id, entry.range.start),
placement: BlockPlacement::Below((
excerpt_id,
entry.range.start,
)),
height: diagnostic.message.matches('\n').count() as u32 + 1,
style: BlockStyle::Fixed,
render: diagnostic_block_renderer(
diagnostic, None, true, true,
),
disposition: BlockDisposition::Below,
priority: 0,
});
}
@@ -498,13 +499,24 @@ impl ProjectDiagnosticsEditor {
editor.remove_blocks(blocks_to_remove, None, cx);
let block_ids = editor.insert_blocks(
blocks_to_add.into_iter().flat_map(|block| {
let (excerpt_id, text_anchor) = block.position;
let placement = match block.placement {
BlockPlacement::Above((excerpt_id, text_anchor)) => BlockPlacement::Above(
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
),
BlockPlacement::Below((excerpt_id, text_anchor)) => BlockPlacement::Below(
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
),
BlockPlacement::Replace(_) => {
unreachable!(
"no Replace block should have been pushed to blocks_to_add"
)
}
};
Some(BlockProperties {
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
placement,
height: block.height,
style: block.style,
render: block.render,
disposition: block.disposition,
priority: 0,
})
}),

View File

@@ -29,8 +29,8 @@ use crate::{
hover_links::InlayHighlight, movement::TextLayoutDetails, EditorStyle, InlayId, RowExt,
};
pub use block_map::{
Block, BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,
BlockMap, BlockPoint, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
Block, BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap,
BlockPlacement, BlockPoint, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
};
use block_map::{BlockRow, BlockSnapshot};
use char_map::{CharMap, CharSnapshot};
@@ -1180,6 +1180,7 @@ impl ToDisplayPoint for Anchor {
pub mod tests {
use super::*;
use crate::{movement, test::marked_display_snapshot};
use block_map::BlockPlacement;
use gpui::{div, font, observe, px, AppContext, BorrowAppContext, Context, Element, Hsla};
use language::{
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
@@ -1293,24 +1294,22 @@ pub mod tests {
Bias::Left,
));
let disposition = if rng.gen() {
BlockDisposition::Above
let placement = if rng.gen() {
BlockPlacement::Above(position)
} else {
BlockDisposition::Below
BlockPlacement::Below(position)
};
let height = rng.gen_range(1..5);
log::info!(
"inserting block {:?} {:?} with height {}",
disposition,
position.to_point(&buffer),
"inserting block {:?} with height {}",
placement.as_ref().map(|p| p.to_point(&buffer)),
height
);
let priority = rng.gen_range(1..100);
BlockProperties {
placement,
style: BlockStyle::Fixed,
position,
height,
disposition,
render: Box::new(|_| div().into_any()),
priority,
}

File diff suppressed because it is too large Load Diff

View File

@@ -252,6 +252,7 @@ impl CharSnapshot {
};
TabChunks {
snapshot: self,
fold_chunks: self.fold_snapshot.chunks(
input_start..input_end,
language_aware,
@@ -492,6 +493,7 @@ impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
const SPACES: &str = " ";
pub struct TabChunks<'a> {
snapshot: &'a CharSnapshot,
fold_chunks: FoldChunks<'a>,
chunk: Chunk<'a>,
column: u32,
@@ -503,6 +505,37 @@ pub struct TabChunks<'a> {
inside_leading_tab: bool,
}
impl<'a> TabChunks<'a> {
pub(crate) fn seek(&mut self, range: Range<CharPoint>) {
let (input_start, expanded_char_column, to_next_stop) =
self.snapshot.to_fold_point(range.start, Bias::Left);
let input_column = input_start.column();
let input_start = input_start.to_offset(&self.snapshot.fold_snapshot);
let input_end = self
.snapshot
.to_fold_point(range.end, Bias::Right)
.0
.to_offset(&self.snapshot.fold_snapshot);
let to_next_stop = if range.start.0 + Point::new(0, to_next_stop) > range.end.0 {
range.end.column() - range.start.column()
} else {
to_next_stop
};
self.fold_chunks.seek(input_start..input_end);
self.input_column = input_column;
self.column = expanded_char_column;
self.output_position = range.start.0;
self.max_output_position = range.end.0;
self.chunk = Chunk {
text: &SPACES[0..(to_next_stop as usize)],
is_tab: true,
..Default::default()
};
self.inside_leading_tab = to_next_stop > 0;
}
}
impl<'a> Iterator for TabChunks<'a> {
type Item = Chunk<'a>;

View File

@@ -1100,6 +1100,17 @@ pub struct FoldBufferRows<'a> {
fold_point: FoldPoint,
}
impl<'a> FoldBufferRows<'a> {
pub(crate) fn seek(&mut self, row: u32) {
let fold_point = FoldPoint::new(row, 0);
self.cursor.seek(&fold_point, Bias::Left, &());
let overshoot = fold_point.0 - self.cursor.start().0 .0;
let inlay_point = InlayPoint(self.cursor.start().1 .0 + overshoot);
self.input_buffer_rows.seek(inlay_point.row());
self.fold_point = fold_point;
}
}
impl<'a> Iterator for FoldBufferRows<'a> {
type Item = Option<u32>;
@@ -1135,6 +1146,38 @@ pub struct FoldChunks<'a> {
max_output_offset: FoldOffset,
}
impl<'a> FoldChunks<'a> {
pub(crate) fn seek(&mut self, range: Range<FoldOffset>) {
self.transform_cursor.seek(&range.start, Bias::Right, &());
let inlay_start = {
let overshoot = range.start.0 - self.transform_cursor.start().0 .0;
self.transform_cursor.start().1 + InlayOffset(overshoot)
};
let transform_end = self.transform_cursor.end(&());
let inlay_end = if self
.transform_cursor
.item()
.map_or(true, |transform| transform.is_fold())
{
inlay_start
} else if range.end < transform_end.0 {
let overshoot = range.end.0 - self.transform_cursor.start().0 .0;
self.transform_cursor.start().1 + InlayOffset(overshoot)
} else {
transform_end.1
};
self.inlay_chunks.seek(inlay_start..inlay_end);
self.inlay_chunk = None;
self.inlay_offset = inlay_start;
self.output_offset = range.start;
self.max_output_offset = range.end;
}
}
impl<'a> Iterator for FoldChunks<'a> {
type Item = Chunk<'a>;

View File

@@ -56,6 +56,7 @@ pub struct WrapChunks<'a> {
output_position: WrapPoint,
max_output_row: u32,
transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>,
snapshot: &'a WrapSnapshot,
}
#[derive(Clone)]
@@ -68,6 +69,21 @@ pub struct WrapBufferRows<'a> {
transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>,
}
impl<'a> WrapBufferRows<'a> {
pub(crate) fn seek(&mut self, start_row: u32) {
self.transforms
.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
let mut input_row = self.transforms.start().1.row();
if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
input_row += start_row - self.transforms.start().0.row();
}
self.soft_wrapped = self.transforms.item().map_or(false, |t| !t.is_isomorphic());
self.input_buffer_rows.seek(input_row);
self.input_buffer_row = self.input_buffer_rows.next().unwrap();
self.output_row = start_row;
}
}
impl WrapMap {
pub fn new(
char_snapshot: CharSnapshot,
@@ -602,6 +618,7 @@ impl WrapSnapshot {
output_position: output_start,
max_output_row: rows.end,
transforms,
snapshot: self,
}
}
@@ -629,6 +646,67 @@ impl WrapSnapshot {
}
}
pub fn text_summary_for_range(&self, rows: Range<u32>) -> TextSummary {
let mut summary = TextSummary::default();
let start = WrapPoint::new(rows.start, 0);
let end = WrapPoint::new(rows.end, 0);
let mut cursor = self.transforms.cursor::<(WrapPoint, CharPoint)>(&());
cursor.seek(&start, Bias::Right, &());
if let Some(transform) = cursor.item() {
let start_in_transform = start.0 - cursor.start().0 .0;
let end_in_transform = cmp::min(end, cursor.end(&()).0).0 - cursor.start().0 .0;
if transform.is_isomorphic() {
let char_start = CharPoint(cursor.start().1 .0 + start_in_transform);
let char_end = CharPoint(cursor.start().1 .0 + end_in_transform);
summary += &self
.char_snapshot
.text_summary_for_range(char_start..char_end);
} else {
debug_assert_eq!(start_in_transform.row, end_in_transform.row);
let indent_len = end_in_transform.column - start_in_transform.column;
summary += &TextSummary {
lines: Point::new(0, indent_len),
first_line_chars: indent_len,
last_line_chars: indent_len,
longest_row: 0,
longest_row_chars: indent_len,
};
}
cursor.next(&());
}
if rows.end > cursor.start().0.row() {
summary += &cursor
.summary::<_, TransformSummary>(&WrapPoint::new(rows.end, 0), Bias::Right, &())
.output;
if let Some(transform) = cursor.item() {
let end_in_transform = end.0 - cursor.start().0 .0;
if transform.is_isomorphic() {
let char_start = cursor.start().1;
let char_end = CharPoint(char_start.0 + end_in_transform);
summary += &self
.char_snapshot
.text_summary_for_range(char_start..char_end);
} else {
debug_assert_eq!(end_in_transform, Point::new(1, 0));
summary += &TextSummary {
lines: Point::new(1, 0),
first_line_chars: 0,
last_line_chars: 0,
longest_row: 0,
longest_row_chars: 0,
};
}
}
}
summary
}
pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
let mut cursor = self.transforms.cursor::<WrapPoint>(&());
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
@@ -745,6 +823,21 @@ impl WrapSnapshot {
None
}
#[cfg(test)]
pub fn text(&self) -> String {
self.text_chunks(0).collect()
}
#[cfg(test)]
pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
self.chunks(
wrap_row..self.max_point().row() + 1,
false,
Highlights::default(),
)
.map(|h| h.text)
}
fn check_invariants(&self) {
#[cfg(test)]
{
@@ -791,6 +884,26 @@ impl WrapSnapshot {
}
}
impl<'a> WrapChunks<'a> {
pub(crate) fn seek(&mut self, rows: Range<u32>) {
let output_start = WrapPoint::new(rows.start, 0);
let output_end = WrapPoint::new(rows.end, 0);
self.transforms.seek(&output_start, Bias::Right, &());
let mut input_start = CharPoint(self.transforms.start().1 .0);
if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
input_start.0 += output_start.0 - self.transforms.start().0 .0;
}
let input_end = self
.snapshot
.to_char_point(output_end)
.min(self.snapshot.char_snapshot.max_point());
self.input_chunks.seek(input_start..input_end);
self.input_chunk = Chunk::default();
self.output_position = output_start;
self.max_output_row = rows.end;
}
}
impl<'a> Iterator for WrapChunks<'a> {
type Item = Chunk<'a>;
@@ -1336,19 +1449,6 @@ mod tests {
}
impl WrapSnapshot {
pub fn text(&self) -> String {
self.text_chunks(0).collect()
}
pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
self.chunks(
wrap_row..self.max_point().row() + 1,
false,
Highlights::default(),
)
.map(|h| h.text)
}
fn verify_chunks(&mut self, rng: &mut impl Rng) {
for _ in 0..5 {
let mut end_row = rng.gen_range(0..=self.max_point().row());

View File

@@ -10210,7 +10210,7 @@ impl Editor {
let block_id = this.insert_blocks(
[BlockProperties {
style: BlockStyle::Flex,
position: range.start,
placement: BlockPlacement::Below(range.start),
height: 1,
render: Box::new({
let rename_editor = rename_editor.clone();
@@ -10246,7 +10246,6 @@ impl Editor {
.into_any_element()
}
}),
disposition: BlockDisposition::Below,
priority: 0,
}],
Some(Autoscroll::fit()),
@@ -10531,10 +10530,11 @@ impl Editor {
let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
BlockProperties {
style: BlockStyle::Fixed,
position: buffer.anchor_after(entry.range.start),
placement: BlockPlacement::Below(
buffer.anchor_after(entry.range.start),
),
height: message_height,
render: diagnostic_block_renderer(diagnostic, None, true, true),
disposition: BlockDisposition::Below,
priority: 0,
}
}),

View File

@@ -3868,8 +3868,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
editor.insert_blocks(
[BlockProperties {
style: BlockStyle::Fixed,
position: snapshot.anchor_after(Point::new(2, 0)),
disposition: BlockDisposition::Below,
placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
height: 1,
render: Box::new(|_| div().into_any()),
priority: 0,

View File

@@ -2071,7 +2071,7 @@ impl EditorElement {
let mut element = match block {
Block::Custom(block) => {
let align_to = block
.position()
.start()
.to_point(&snapshot.buffer_snapshot)
.to_display_point(snapshot);
let anchor_x = text_x
@@ -6294,7 +6294,7 @@ fn compute_auto_height_layout(
mod tests {
use super::*;
use crate::{
display_map::{BlockDisposition, BlockProperties},
display_map::{BlockPlacement, BlockProperties},
editor_tests::{init_test, update_test_language_settings},
Editor, MultiBuffer,
};
@@ -6550,9 +6550,8 @@ mod tests {
editor.insert_blocks(
[BlockProperties {
style: BlockStyle::Fixed,
disposition: BlockDisposition::Above,
placement: BlockPlacement::Above(Anchor::min()),
height: 3,
position: Anchor::min(),
render: Box::new(|cx| div().h(3. * cx.line_height()).into_any()),
priority: 0,
}],

View File

@@ -17,7 +17,7 @@ use workspace::Item;
use crate::{
editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyDiffHunk,
BlockDisposition, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow,
BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, DiffRowHighlight, DisplayRow,
DisplaySnapshot, Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk, RevertFile,
RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
};
@@ -417,10 +417,9 @@ impl Editor {
};
BlockProperties {
position: hunk.multi_buffer_range.start,
placement: BlockPlacement::Above(hunk.multi_buffer_range.start),
height: 1,
style: BlockStyle::Sticky,
disposition: BlockDisposition::Above,
priority: 0,
render: Box::new({
let editor = cx.view().clone();
@@ -700,10 +699,9 @@ impl Editor {
let hunk = hunk.clone();
let height = editor_height.max(deleted_text_height);
BlockProperties {
position: hunk.multi_buffer_range.start,
placement: BlockPlacement::Above(hunk.multi_buffer_range.start),
height,
style: BlockStyle::Flex,
disposition: BlockDisposition::Above,
priority: 0,
render: Box::new(move |cx| {
let width = EditorElement::diff_hunk_strip_width(cx.line_height());

View File

@@ -8,7 +8,7 @@ use client::telemetry::Telemetry;
use collections::{HashMap, HashSet};
use editor::{
display_map::{
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, CustomBlockId,
BlockContext, BlockId, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId,
RenderBlock,
},
scroll::Autoscroll,
@@ -90,12 +90,11 @@ impl EditorBlock {
let invalidation_anchor = buffer.read(cx).read(cx).anchor_before(next_row_start);
let block = BlockProperties {
position: code_range.end,
placement: BlockPlacement::Below(code_range.end),
// Take up at least one height for status, allow the editor to determine the real height based on the content from render
height: 1,
style: BlockStyle::Sticky,
render: Self::create_output_area_renderer(execution_view.clone(), on_close.clone()),
disposition: BlockDisposition::Below,
priority: 0,
};