Allow editor blocks to replace ranges of text (#19531)

This PR adds the ability for editor blocks to replace lines of text, but
does not yet use that feature anywhere. We'll update assistant patches
to use replace blocks on another branch:
https://github.com/zed-industries/zed/tree/assistant-patch-replace-blocks

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Richard Feldman <richard@zed.dev>
Co-authored-by: Marshall Bowers <marshall@zed.dev>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-10-25 03:29:25 -07:00 committed by GitHub
parent 3617873431
commit 08a3c54bac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 1118 additions and 599 deletions

View file

@ -26,8 +26,8 @@ use collections::{BTreeSet, HashMap, HashSet};
use editor::{ use editor::{
actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt}, actions::{FoldAt, MoveToEndOfLine, Newline, ShowCompletions, UnfoldAt},
display_map::{ display_map::{
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, Crease, BlockContext, BlockId, BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata,
CreaseMetadata, CustomBlockId, FoldId, RenderBlock, ToDisplayPoint, CustomBlockId, FoldId, RenderBlock, ToDisplayPoint,
}, },
scroll::{Autoscroll, AutoscrollStrategy}, scroll::{Autoscroll, AutoscrollStrategy},
Anchor, Editor, EditorEvent, ProposedChangeLocation, ProposedChangesEditor, RowExt, Anchor, Editor, EditorEvent, ProposedChangeLocation, ProposedChangesEditor, RowExt,
@ -2009,13 +2009,12 @@ impl ContextEditor {
}) })
.map(|(command, error_message)| BlockProperties { .map(|(command, error_message)| BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
position: Anchor { height: 1,
placement: BlockPlacement::Below(Anchor {
buffer_id: Some(buffer_id), buffer_id: Some(buffer_id),
excerpt_id, excerpt_id,
text_anchor: command.source_range.start, text_anchor: command.source_range.start,
}, }),
height: 1,
disposition: BlockDisposition::Below,
render: slash_command_error_block_renderer(error_message), render: slash_command_error_block_renderer(error_message),
priority: 0, priority: 0,
}), }),
@ -2242,11 +2241,10 @@ impl ContextEditor {
} else { } else {
let block_ids = editor.insert_blocks( let block_ids = editor.insert_blocks(
[BlockProperties { [BlockProperties {
position: patch_start,
height: path_count as u32 + 1, height: path_count as u32 + 1,
style: BlockStyle::Flex, style: BlockStyle::Flex,
render: render_block, render: render_block,
disposition: BlockDisposition::Below, placement: BlockPlacement::Below(patch_start),
priority: 0, priority: 0,
}], }],
None, None,
@ -2731,12 +2729,13 @@ impl ContextEditor {
}) })
}; };
let create_block_properties = |message: &Message| BlockProperties { let create_block_properties = |message: &Message| BlockProperties {
position: buffer
.anchor_in_excerpt(excerpt_id, message.anchor_range.start)
.unwrap(),
height: 2, height: 2,
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
disposition: BlockDisposition::Above, placement: BlockPlacement::Above(
buffer
.anchor_in_excerpt(excerpt_id, message.anchor_range.start)
.unwrap(),
),
priority: usize::MAX, priority: usize::MAX,
render: render_block(MessageMetadata::from(message)), render: render_block(MessageMetadata::from(message)),
}; };
@ -3372,7 +3371,7 @@ impl ContextEditor {
let anchor = buffer.anchor_in_excerpt(excerpt_id, anchor).unwrap(); let anchor = buffer.anchor_in_excerpt(excerpt_id, anchor).unwrap();
let image = render_image.clone(); let image = render_image.clone();
anchor.is_valid(&buffer).then(|| BlockProperties { anchor.is_valid(&buffer).then(|| BlockProperties {
position: anchor, placement: BlockPlacement::Above(anchor),
height: MAX_HEIGHT_IN_LINES, height: MAX_HEIGHT_IN_LINES,
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Box::new(move |cx| { render: Box::new(move |cx| {
@ -3393,8 +3392,6 @@ impl ContextEditor {
) )
.into_any_element() .into_any_element()
}), }),
disposition: BlockDisposition::Above,
priority: 0, priority: 0,
}) })
}) })

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -252,6 +252,7 @@ impl CharSnapshot {
}; };
TabChunks { TabChunks {
snapshot: self,
fold_chunks: self.fold_snapshot.chunks( fold_chunks: self.fold_snapshot.chunks(
input_start..input_end, input_start..input_end,
language_aware, language_aware,
@ -492,6 +493,7 @@ impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
const SPACES: &str = " "; const SPACES: &str = " ";
pub struct TabChunks<'a> { pub struct TabChunks<'a> {
snapshot: &'a CharSnapshot,
fold_chunks: FoldChunks<'a>, fold_chunks: FoldChunks<'a>,
chunk: Chunk<'a>, chunk: Chunk<'a>,
column: u32, column: u32,
@ -503,6 +505,37 @@ pub struct TabChunks<'a> {
inside_leading_tab: bool, 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> { impl<'a> Iterator for TabChunks<'a> {
type Item = Chunk<'a>; type Item = Chunk<'a>;

View file

@ -1100,6 +1100,17 @@ pub struct FoldBufferRows<'a> {
fold_point: FoldPoint, 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> { impl<'a> Iterator for FoldBufferRows<'a> {
type Item = Option<u32>; type Item = Option<u32>;
@ -1135,6 +1146,38 @@ pub struct FoldChunks<'a> {
max_output_offset: FoldOffset, 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> { impl<'a> Iterator for FoldChunks<'a> {
type Item = Chunk<'a>; type Item = Chunk<'a>;

View file

@ -56,6 +56,7 @@ pub struct WrapChunks<'a> {
output_position: WrapPoint, output_position: WrapPoint,
max_output_row: u32, max_output_row: u32,
transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>, transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>,
snapshot: &'a WrapSnapshot,
} }
#[derive(Clone)] #[derive(Clone)]
@ -68,6 +69,21 @@ pub struct WrapBufferRows<'a> {
transforms: Cursor<'a, Transform, (WrapPoint, CharPoint)>, 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 { impl WrapMap {
pub fn new( pub fn new(
char_snapshot: CharSnapshot, char_snapshot: CharSnapshot,
@ -602,6 +618,7 @@ impl WrapSnapshot {
output_position: output_start, output_position: output_start,
max_output_row: rows.end, max_output_row: rows.end,
transforms, 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> { pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
let mut cursor = self.transforms.cursor::<WrapPoint>(&()); let mut cursor = self.transforms.cursor::<WrapPoint>(&());
cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &()); cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
@ -745,6 +823,21 @@ impl WrapSnapshot {
None 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) { fn check_invariants(&self) {
#[cfg(test)] #[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> { impl<'a> Iterator for WrapChunks<'a> {
type Item = Chunk<'a>; type Item = Chunk<'a>;
@ -1336,19 +1449,6 @@ mod tests {
} }
impl WrapSnapshot { 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) { fn verify_chunks(&mut self, rng: &mut impl Rng) {
for _ in 0..5 { for _ in 0..5 {
let mut end_row = rng.gen_range(0..=self.max_point().row()); 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( let block_id = this.insert_blocks(
[BlockProperties { [BlockProperties {
style: BlockStyle::Flex, style: BlockStyle::Flex,
position: range.start, placement: BlockPlacement::Below(range.start),
height: 1, height: 1,
render: Box::new({ render: Box::new({
let rename_editor = rename_editor.clone(); let rename_editor = rename_editor.clone();
@ -10246,7 +10246,6 @@ impl Editor {
.into_any_element() .into_any_element()
} }
}), }),
disposition: BlockDisposition::Below,
priority: 0, priority: 0,
}], }],
Some(Autoscroll::fit()), Some(Autoscroll::fit()),
@ -10531,10 +10530,11 @@ impl Editor {
let message_height = diagnostic.message.matches('\n').count() as u32 + 1; let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
position: buffer.anchor_after(entry.range.start), placement: BlockPlacement::Below(
buffer.anchor_after(entry.range.start),
),
height: message_height, height: message_height,
render: diagnostic_block_renderer(diagnostic, None, true, true), render: diagnostic_block_renderer(diagnostic, None, true, true),
disposition: BlockDisposition::Below,
priority: 0, priority: 0,
} }
}), }),

View file

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

View file

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

View file

@ -17,7 +17,7 @@ use workspace::Item;
use crate::{ use crate::{
editor_settings::CurrentLineHighlight, hunk_status, hunks_for_selections, ApplyDiffHunk, 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, DisplaySnapshot, Editor, EditorElement, ExpandAllHunkDiffs, GoToHunk, GoToPrevHunk, RevertFile,
RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff, RevertSelectedHunks, ToDisplayPoint, ToggleHunkDiff,
}; };
@ -417,10 +417,9 @@ impl Editor {
}; };
BlockProperties { BlockProperties {
position: hunk.multi_buffer_range.start, placement: BlockPlacement::Above(hunk.multi_buffer_range.start),
height: 1, height: 1,
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
disposition: BlockDisposition::Above,
priority: 0, priority: 0,
render: Box::new({ render: Box::new({
let editor = cx.view().clone(); let editor = cx.view().clone();
@ -700,10 +699,9 @@ impl Editor {
let hunk = hunk.clone(); let hunk = hunk.clone();
let height = editor_height.max(deleted_text_height); let height = editor_height.max(deleted_text_height);
BlockProperties { BlockProperties {
position: hunk.multi_buffer_range.start, placement: BlockPlacement::Above(hunk.multi_buffer_range.start),
height, height,
style: BlockStyle::Flex, style: BlockStyle::Flex,
disposition: BlockDisposition::Above,
priority: 0, priority: 0,
render: Box::new(move |cx| { render: Box::new(move |cx| {
let width = EditorElement::diff_hunk_strip_width(cx.line_height()); 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 collections::{HashMap, HashSet};
use editor::{ use editor::{
display_map::{ display_map::{
BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, CustomBlockId, BlockContext, BlockId, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId,
RenderBlock, RenderBlock,
}, },
scroll::Autoscroll, scroll::Autoscroll,
@ -90,12 +90,11 @@ impl EditorBlock {
let invalidation_anchor = buffer.read(cx).read(cx).anchor_before(next_row_start); let invalidation_anchor = buffer.read(cx).read(cx).anchor_before(next_row_start);
let block = BlockProperties { 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 // Take up at least one height for status, allow the editor to determine the real height based on the content from render
height: 1, height: 1,
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Self::create_output_area_renderer(execution_view.clone(), on_close.clone()), render: Self::create_output_area_renderer(execution_view.clone(), on_close.clone()),
disposition: BlockDisposition::Below,
priority: 0, priority: 0,
}; };