Introduce "Near" block type (#28032)

A "Near" block acts similarly to a "Below" block, but can (if it's
height is <= one line height) be shown on the end of the preceding line
instead of adding an entire blank line to the editor.

You can test it out by pasting this into `go_to_diagnostic_impl` and
then press `F8`
```
        let buffer = self.buffer.read(cx).snapshot(cx);
        let selection = self.selections.newest_anchor();

        self.display_map.update(cx, |display_map, cx| {
            display_map.insert_blocks(
                [BlockProperties {
                    placement: BlockPlacement::Near(selection.start),
                    height: Some(1),
                    style: BlockStyle::Flex,
                    render: Arc::new(|_| {
                        div()
                            .w(px(100.))
                            .h(px(16.))
                            .bg(gpui::hsla(0., 0., 1., 0.5))
                            .into_any_element()
                    }),
                    priority: 0,
                }],
                cx,
            )
        });
        return;
```

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
Conrad Irwin 2025-04-04 17:37:42 -06:00 committed by GitHub
parent 10821aae2c
commit d0e82b0538
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 246 additions and 191 deletions

View file

@ -621,14 +621,14 @@ impl InlineAssistant {
BlockProperties { BlockProperties {
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
placement: BlockPlacement::Above(range.start), placement: BlockPlacement::Above(range.start),
height: prompt_editor_height, height: Some(prompt_editor_height),
render: build_assist_editor_renderer(prompt_editor), render: build_assist_editor_renderer(prompt_editor),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
placement: BlockPlacement::Below(range.end), placement: BlockPlacement::Below(range.end),
height: 0, height: None,
render: Arc::new(|cx| { render: Arc::new(|cx| {
v_flex() v_flex()
.h_full() .h_full()
@ -1392,7 +1392,7 @@ impl InlineAssistant {
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 {
placement: BlockPlacement::Above(new_row), placement: BlockPlacement::Above(new_row),
height, height: Some(height),
style: BlockStyle::Flex, style: BlockStyle::Flex,
render: Arc::new(move |cx| { render: Arc::new(move |cx| {
div() div()

View file

@ -527,14 +527,14 @@ impl InlineAssistant {
BlockProperties { BlockProperties {
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
placement: BlockPlacement::Above(range.start), placement: BlockPlacement::Above(range.start),
height: prompt_editor_height, height: Some(prompt_editor_height),
render: build_assist_editor_renderer(prompt_editor), render: build_assist_editor_renderer(prompt_editor),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
placement: BlockPlacement::Below(range.end), placement: BlockPlacement::Below(range.end),
height: 0, height: None,
render: Arc::new(|cx| { render: Arc::new(|cx| {
v_flex() v_flex()
.h_full() .h_full()
@ -1301,7 +1301,7 @@ impl InlineAssistant {
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 {
placement: BlockPlacement::Above(new_row), placement: BlockPlacement::Above(new_row),
height, height: Some(height),
style: BlockStyle::Flex, style: BlockStyle::Flex,
render: Arc::new(move |cx| { render: Arc::new(move |cx| {
div() div()

View file

@ -1562,7 +1562,7 @@ impl ContextEditor {
}) })
}; };
let create_block_properties = |message: &Message| BlockProperties { let create_block_properties = |message: &Message| BlockProperties {
height: 2, height: Some(2),
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
placement: BlockPlacement::Above( placement: BlockPlacement::Above(
buffer buffer
@ -2111,7 +2111,7 @@ impl ContextEditor {
let image = render_image.clone(); let image = render_image.clone();
anchor.is_valid(&buffer).then(|| BlockProperties { anchor.is_valid(&buffer).then(|| BlockProperties {
placement: BlockPlacement::Above(anchor), placement: BlockPlacement::Above(anchor),
height: MAX_HEIGHT_IN_LINES, height: Some(MAX_HEIGHT_IN_LINES),
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Arc::new(move |cx| { render: Arc::new(move |cx| {
let image_size = size_for_image( let image_size = size_for_image(

View file

@ -535,7 +535,7 @@ impl ProjectDiagnosticsEditor {
group_state.block_count += 1; group_state.block_count += 1;
blocks_to_add.push(BlockProperties { blocks_to_add.push(BlockProperties {
placement: BlockPlacement::Above(header_position), placement: BlockPlacement::Above(header_position),
height: 2, height: Some(2),
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: diagnostic_header_renderer(primary), render: diagnostic_header_renderer(primary),
priority: 0, priority: 0,
@ -557,7 +557,9 @@ impl ProjectDiagnosticsEditor {
excerpt_id, excerpt_id,
entry.range.start, entry.range.start,
)), )),
height: diagnostic.message.matches('\n').count() as u32 + 1, height: Some(
diagnostic.message.matches('\n').count() as u32 + 1,
),
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
render: diagnostic_block_renderer(diagnostic, None, true), render: diagnostic_block_renderer(diagnostic, None, true),
priority: 0, priority: 0,
@ -613,9 +615,9 @@ impl ProjectDiagnosticsEditor {
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?, excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
) )
} }
BlockPlacement::Replace(_) => { BlockPlacement::Replace(_) | BlockPlacement::Near(_) => {
unreachable!( unreachable!(
"no Replace block should have been pushed to blocks_to_add" "no Near/Replace block should have been pushed to blocks_to_add"
) )
} }
}; };

View file

@ -255,7 +255,7 @@ impl DisplayMap {
BlockProperties { BlockProperties {
placement: BlockPlacement::Replace(start..=end), placement: BlockPlacement::Replace(start..=end),
render, render,
height, height: Some(height),
style, style,
priority, priority,
} }
@ -1591,7 +1591,7 @@ pub mod tests {
BlockProperties { BlockProperties {
placement, placement,
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
height, height: Some(height),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority, priority,
} }
@ -1953,7 +1953,7 @@ pub mod tests {
placement: BlockPlacement::Above( placement: BlockPlacement::Above(
buffer_snapshot.anchor_before(Point::new(0, 0)), buffer_snapshot.anchor_before(Point::new(0, 0)),
), ),
height: 2, height: Some(2),
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
@ -2148,7 +2148,7 @@ pub mod tests {
placement: BlockPlacement::Below( placement: BlockPlacement::Below(
buffer_snapshot.anchor_before(Point::new(1, 0)), buffer_snapshot.anchor_before(Point::new(1, 0)),
), ),
height: 1, height: Some(1),
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
@ -2157,7 +2157,7 @@ pub mod tests {
placement: BlockPlacement::Below( placement: BlockPlacement::Below(
buffer_snapshot.anchor_before(Point::new(2, 0)), buffer_snapshot.anchor_before(Point::new(2, 0)),
), ),
height: 0, height: None,
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
@ -2262,7 +2262,7 @@ pub mod tests {
placement: BlockPlacement::Below( placement: BlockPlacement::Below(
buffer_snapshot.anchor_before(Point::new(1, 0)), buffer_snapshot.anchor_before(Point::new(1, 0)),
), ),
height: 1, height: Some(1),
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
@ -2336,7 +2336,7 @@ pub mod tests {
buffer_snapshot.anchor_before(Point::new(1, 2)) buffer_snapshot.anchor_before(Point::new(1, 2))
..=buffer_snapshot.anchor_after(Point::new(2, 3)), ..=buffer_snapshot.anchor_after(Point::new(2, 3)),
), ),
height: 4, height: Some(4),
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,

View file

@ -84,6 +84,7 @@ pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement
pub enum BlockPlacement<T> { pub enum BlockPlacement<T> {
Above(T), Above(T),
Below(T), Below(T),
Near(T),
Replace(RangeInclusive<T>), Replace(RangeInclusive<T>),
} }
@ -92,6 +93,7 @@ impl<T> BlockPlacement<T> {
match self { match self {
BlockPlacement::Above(position) => position, BlockPlacement::Above(position) => position,
BlockPlacement::Below(position) => position, BlockPlacement::Below(position) => position,
BlockPlacement::Near(position) => position,
BlockPlacement::Replace(range) => range.start(), BlockPlacement::Replace(range) => range.start(),
} }
} }
@ -100,6 +102,7 @@ impl<T> BlockPlacement<T> {
match self { match self {
BlockPlacement::Above(position) => position, BlockPlacement::Above(position) => position,
BlockPlacement::Below(position) => position, BlockPlacement::Below(position) => position,
BlockPlacement::Near(position) => position,
BlockPlacement::Replace(range) => range.end(), BlockPlacement::Replace(range) => range.end(),
} }
} }
@ -108,6 +111,7 @@ impl<T> BlockPlacement<T> {
match self { match self {
BlockPlacement::Above(position) => BlockPlacement::Above(position), BlockPlacement::Above(position) => BlockPlacement::Above(position),
BlockPlacement::Below(position) => BlockPlacement::Below(position), BlockPlacement::Below(position) => BlockPlacement::Below(position),
BlockPlacement::Near(position) => BlockPlacement::Near(position),
BlockPlacement::Replace(range) => BlockPlacement::Replace(range.start()..=range.end()), BlockPlacement::Replace(range) => BlockPlacement::Replace(range.start()..=range.end()),
} }
} }
@ -116,44 +120,30 @@ impl<T> BlockPlacement<T> {
match self { match self {
BlockPlacement::Above(position) => BlockPlacement::Above(f(position)), BlockPlacement::Above(position) => BlockPlacement::Above(f(position)),
BlockPlacement::Below(position) => BlockPlacement::Below(f(position)), BlockPlacement::Below(position) => BlockPlacement::Below(f(position)),
BlockPlacement::Near(position) => BlockPlacement::Near(f(position)),
BlockPlacement::Replace(range) => { BlockPlacement::Replace(range) => {
let (start, end) = range.into_inner(); let (start, end) = range.into_inner();
BlockPlacement::Replace(f(start)..=f(end)) BlockPlacement::Replace(f(start)..=f(end))
} }
} }
} }
fn sort_order(&self) -> u8 {
match self {
BlockPlacement::Above(_) => 0,
BlockPlacement::Replace(_) => 1,
BlockPlacement::Near(_) => 2,
BlockPlacement::Below(_) => 3,
}
}
} }
impl BlockPlacement<Anchor> { impl BlockPlacement<Anchor> {
fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering { fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
match (self, other) { self.start()
(BlockPlacement::Above(anchor_a), BlockPlacement::Above(anchor_b)) .cmp(other.start(), buffer)
| (BlockPlacement::Below(anchor_a), BlockPlacement::Below(anchor_b)) => { .then_with(|| other.end().cmp(self.end(), buffer))
anchor_a.cmp(anchor_b, buffer) .then_with(|| self.sort_order().cmp(&other.sort_order()))
}
(BlockPlacement::Above(anchor_a), BlockPlacement::Below(anchor_b)) => {
anchor_a.cmp(anchor_b, buffer).then(Ordering::Less)
}
(BlockPlacement::Below(anchor_a), BlockPlacement::Above(anchor_b)) => {
anchor_a.cmp(anchor_b, buffer).then(Ordering::Greater)
}
(BlockPlacement::Above(anchor), BlockPlacement::Replace(range)) => {
anchor.cmp(range.start(), buffer).then(Ordering::Less)
}
(BlockPlacement::Replace(range), BlockPlacement::Above(anchor)) => {
range.start().cmp(anchor, buffer).then(Ordering::Greater)
}
(BlockPlacement::Below(anchor), BlockPlacement::Replace(range)) => {
anchor.cmp(range.start(), buffer).then(Ordering::Greater)
}
(BlockPlacement::Replace(range), BlockPlacement::Below(anchor)) => {
range.start().cmp(anchor, buffer).then(Ordering::Less)
}
(BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => range_a
.start()
.cmp(range_b.start(), buffer)
.then_with(|| range_b.end().cmp(range_a.end(), buffer)),
}
} }
fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> { fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> {
@ -165,6 +155,12 @@ impl BlockPlacement<Anchor> {
let wrap_row = WrapRow(wrap_snapshot.make_wrap_point(position, Bias::Left).row()); let wrap_row = WrapRow(wrap_snapshot.make_wrap_point(position, Bias::Left).row());
Some(BlockPlacement::Above(wrap_row)) Some(BlockPlacement::Above(wrap_row))
} }
BlockPlacement::Near(position) => {
let mut position = position.to_point(buffer_snapshot);
position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
let wrap_row = WrapRow(wrap_snapshot.make_wrap_point(position, Bias::Left).row());
Some(BlockPlacement::Near(wrap_row))
}
BlockPlacement::Below(position) => { BlockPlacement::Below(position) => {
let mut position = position.to_point(buffer_snapshot); let mut position = position.to_point(buffer_snapshot);
position.column = buffer_snapshot.line_len(MultiBufferRow(position.row)); position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
@ -193,7 +189,7 @@ impl BlockPlacement<Anchor> {
pub struct CustomBlock { pub struct CustomBlock {
id: CustomBlockId, id: CustomBlockId,
placement: BlockPlacement<Anchor>, placement: BlockPlacement<Anchor>,
height: u32, height: Option<u32>,
style: BlockStyle, style: BlockStyle,
render: Arc<Mutex<RenderBlock>>, render: Arc<Mutex<RenderBlock>>,
priority: usize, priority: usize,
@ -201,7 +197,9 @@ pub struct CustomBlock {
pub struct BlockProperties<P> { pub struct BlockProperties<P> {
pub placement: BlockPlacement<P>, pub placement: BlockPlacement<P>,
pub height: u32, // None if the block takes up no space
// (e.g. a horizontal line)
pub height: Option<u32>,
pub style: BlockStyle, pub style: BlockStyle,
pub render: RenderBlock, pub render: RenderBlock,
pub priority: usize, pub priority: usize,
@ -302,9 +300,16 @@ impl Block {
} }
} }
pub fn has_height(&self) -> bool {
match self {
Block::Custom(block) => block.height.is_some(),
Block::ExcerptBoundary { .. } | Block::FoldedBuffer { .. } => true,
}
}
pub fn height(&self) -> u32 { pub fn height(&self) -> u32 {
match self { match self {
Block::Custom(block) => block.height, Block::Custom(block) => block.height.unwrap_or(0),
Block::ExcerptBoundary { height, .. } | Block::FoldedBuffer { height, .. } => *height, Block::ExcerptBoundary { height, .. } | Block::FoldedBuffer { height, .. } => *height,
} }
} }
@ -324,9 +329,20 @@ impl Block {
} }
} }
pub fn place_near(&self) -> bool {
match self {
Block::Custom(block) => matches!(block.placement, BlockPlacement::Near(_)),
Block::FoldedBuffer { .. } => false,
Block::ExcerptBoundary { .. } => false,
}
}
fn place_below(&self) -> bool { fn place_below(&self) -> bool {
match self { match self {
Block::Custom(block) => matches!(block.placement, BlockPlacement::Below(_)), Block::Custom(block) => matches!(
block.placement,
BlockPlacement::Below(_) | BlockPlacement::Near(_)
),
Block::FoldedBuffer { .. } => false, Block::FoldedBuffer { .. } => false,
Block::ExcerptBoundary { .. } => false, Block::ExcerptBoundary { .. } => false,
} }
@ -669,7 +685,7 @@ impl BlockMap {
BlockPlacement::Above(position) => { BlockPlacement::Above(position) => {
rows_before_block = position.0 - new_transforms.summary().input_rows; rows_before_block = position.0 - new_transforms.summary().input_rows;
} }
BlockPlacement::Below(position) => { BlockPlacement::Near(position) | BlockPlacement::Below(position) => {
rows_before_block = (position.0 + 1) - new_transforms.summary().input_rows; rows_before_block = (position.0 + 1) - new_transforms.summary().input_rows;
} }
BlockPlacement::Replace(range) => { BlockPlacement::Replace(range) => {
@ -803,60 +819,39 @@ impl BlockMap {
fn sort_blocks(blocks: &mut Vec<(BlockPlacement<WrapRow>, Block)>) { fn sort_blocks(blocks: &mut Vec<(BlockPlacement<WrapRow>, Block)>) {
blocks.sort_unstable_by(|(placement_a, block_a), (placement_b, block_b)| { blocks.sort_unstable_by(|(placement_a, block_a), (placement_b, block_b)| {
let placement_comparison = match (placement_a, placement_b) { placement_a
(BlockPlacement::Above(row_a), BlockPlacement::Above(row_b)) .start()
| (BlockPlacement::Below(row_a), BlockPlacement::Below(row_b)) => row_a.cmp(row_b), .cmp(placement_b.start())
(BlockPlacement::Above(row_a), BlockPlacement::Below(row_b)) => { .then_with(|| placement_b.end().cmp(placement_a.end()))
row_a.cmp(row_b).then(Ordering::Less) .then_with(|| {
} if block_a.is_header() {
(BlockPlacement::Below(row_a), BlockPlacement::Above(row_b)) => { Ordering::Less
row_a.cmp(row_b).then(Ordering::Greater) } else if block_b.is_header() {
} Ordering::Greater
(BlockPlacement::Above(row), BlockPlacement::Replace(range)) => { } else {
row.cmp(range.start()).then(Ordering::Greater) Ordering::Equal
} }
(BlockPlacement::Replace(range), BlockPlacement::Above(row)) => { })
range.start().cmp(row).then(Ordering::Less) .then_with(|| placement_a.sort_order().cmp(&placement_b.sort_order()))
} .then_with(|| match (block_a, block_b) {
(BlockPlacement::Below(row), BlockPlacement::Replace(range)) => { (
row.cmp(range.start()).then(Ordering::Greater) Block::ExcerptBoundary {
} excerpt: excerpt_a, ..
(BlockPlacement::Replace(range), BlockPlacement::Below(row)) => { },
range.start().cmp(row).then(Ordering::Less) Block::ExcerptBoundary {
} excerpt: excerpt_b, ..
(BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => range_a },
.start() ) => Some(excerpt_a.id).cmp(&Some(excerpt_b.id)),
.cmp(range_b.start()) (Block::ExcerptBoundary { .. }, Block::Custom(_)) => Ordering::Less,
.then_with(|| range_b.end().cmp(range_a.end())) (Block::Custom(_), Block::ExcerptBoundary { .. }) => Ordering::Greater,
.then_with(|| { (Block::Custom(block_a), Block::Custom(block_b)) => block_a
if block_a.is_header() { .priority
Ordering::Less .cmp(&block_b.priority)
} else if block_b.is_header() { .then_with(|| block_a.id.cmp(&block_b.id)),
Ordering::Greater _ => {
} else { unreachable!()
Ordering::Equal }
} })
}),
};
placement_comparison.then_with(|| match (block_a, block_b) {
(
Block::ExcerptBoundary {
excerpt: excerpt_a, ..
},
Block::ExcerptBoundary {
excerpt: excerpt_b, ..
},
) => Some(excerpt_a.id).cmp(&Some(excerpt_b.id)),
(Block::ExcerptBoundary { .. }, Block::Custom(_)) => Ordering::Less,
(Block::Custom(_), Block::ExcerptBoundary { .. }) => Ordering::Greater,
(Block::Custom(block_a), Block::Custom(block_b)) => block_a
.priority
.cmp(&block_b.priority)
.then_with(|| block_a.id.cmp(&block_b.id)),
_ => {
unreachable!()
}
})
}); });
blocks.dedup_by(|right, left| match (left.0.clone(), right.0.clone()) { blocks.dedup_by(|right, left| match (left.0.clone(), right.0.clone()) {
(BlockPlacement::Replace(range), BlockPlacement::Above(row)) (BlockPlacement::Replace(range), BlockPlacement::Above(row))
@ -999,7 +994,7 @@ impl BlockMapWriter<'_> {
let mut previous_wrap_row_range: Option<Range<u32>> = None; let mut previous_wrap_row_range: Option<Range<u32>> = None;
for block in blocks { for block in blocks {
if let BlockPlacement::Replace(_) = &block.placement { if let BlockPlacement::Replace(_) = &block.placement {
debug_assert!(block.height > 0); debug_assert!(block.height.unwrap() > 0);
} }
let id = CustomBlockId(self.0.next_block_id.fetch_add(1, SeqCst)); let id = CustomBlockId(self.0.next_block_id.fetch_add(1, SeqCst));
@ -1064,11 +1059,11 @@ impl BlockMapWriter<'_> {
debug_assert!(new_height > 0); debug_assert!(new_height > 0);
} }
if block.height != new_height { if block.height != Some(new_height) {
let new_block = CustomBlock { let new_block = CustomBlock {
id: block.id, id: block.id,
placement: block.placement.clone(), placement: block.placement.clone(),
height: new_height, height: Some(new_height),
style: block.style, style: block.style,
render: block.render.clone(), render: block.render.clone(),
priority: block.priority, priority: block.priority,
@ -1965,21 +1960,21 @@ mod tests {
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
height: 2, height: Some(2),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))), placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
height: 3, height: Some(3),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
@ -1992,7 +1987,7 @@ mod tests {
.blocks_in_range(0..8) .blocks_in_range(0..8)
.map(|(start_row, block)| { .map(|(start_row, block)| {
let block = block.as_custom().unwrap(); let block = block.as_custom().unwrap();
(start_row..start_row + block.height, block.id) (start_row..start_row + block.height.unwrap(), block.id)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -2203,21 +2198,21 @@ mod tests {
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
height: 2, height: Some(2),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))), placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
height: 3, height: Some(3),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
@ -2307,14 +2302,14 @@ mod tests {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
height: 1, height: Some(1),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))), placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
height: 1, height: Some(1),
priority: 0, priority: 0,
}, },
]); ]);
@ -2352,7 +2347,7 @@ mod tests {
buffer_snapshot.anchor_after(Point::new(1, 3)) buffer_snapshot.anchor_after(Point::new(1, 3))
..=buffer_snapshot.anchor_before(Point::new(3, 1)), ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
), ),
height: 4, height: Some(4),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}])[0]; }])[0];
@ -2405,21 +2400,21 @@ mod tests {
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))), placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
@ -2433,21 +2428,21 @@ mod tests {
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))), placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
@ -2546,21 +2541,21 @@ mod tests {
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))), placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
@ -2569,14 +2564,14 @@ mod tests {
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))), placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}, },
@ -2623,7 +2618,7 @@ mod tests {
let excerpt_blocks_1 = writer.insert(vec![BlockProperties { let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}]); }]);
@ -2980,7 +2975,7 @@ mod tests {
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement, placement,
height, height: Some(height),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
} }
@ -3007,7 +3002,7 @@ mod tests {
for (block_properties, block_id) in block_properties.iter().zip(block_ids) { for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
log::info!( log::info!(
"inserted block {:?} with height {} and id {:?}", "inserted block {:?} with height {:?} and id {:?}",
block_properties block_properties
.placement .placement
.as_ref() .as_ref()
@ -3524,7 +3519,7 @@ mod tests {
let _block_id = writer.insert(vec![BlockProperties { let _block_id = writer.insert(vec![BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))), placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}])[0]; }])[0];

View file

@ -14,6 +14,7 @@ use std::{
fmt, iter, fmt, iter,
ops::{Add, AddAssign, Deref, DerefMut, Range, Sub}, ops::{Add, AddAssign, Deref, DerefMut, Range, Sub},
sync::Arc, sync::Arc,
usize,
}; };
use sum_tree::{Bias, Cursor, FilterCursor, SumTree, Summary, TreeMap}; use sum_tree::{Bias, Cursor, FilterCursor, SumTree, Summary, TreeMap};
use ui::IntoElement as _; use ui::IntoElement as _;

View file

@ -8717,7 +8717,7 @@ impl Editor {
let blocks = vec![BlockProperties { let blocks = vec![BlockProperties {
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
placement: BlockPlacement::Above(anchor), placement: BlockPlacement::Above(anchor),
height, height: Some(height),
render: Arc::new(move |cx| { render: Arc::new(move |cx| {
*cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions; *cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
cloned_prompt.clone().into_any_element() cloned_prompt.clone().into_any_element()
@ -12801,7 +12801,6 @@ impl Editor {
) { ) {
let buffer = self.buffer.read(cx).snapshot(cx); let buffer = self.buffer.read(cx).snapshot(cx);
let selection = self.selections.newest::<usize>(cx); let selection = self.selections.newest::<usize>(cx);
// If there is an active Diagnostic Popover jump to its diagnostic instead. // If there is an active Diagnostic Popover jump to its diagnostic instead.
if direction == Direction::Next { if direction == Direction::Next {
if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() { if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
@ -13806,7 +13805,7 @@ impl Editor {
[BlockProperties { [BlockProperties {
style: BlockStyle::Flex, style: BlockStyle::Flex,
placement: BlockPlacement::Below(range.start), placement: BlockPlacement::Below(range.start),
height: 1, height: Some(1),
render: Arc::new({ render: Arc::new({
let rename_editor = rename_editor.clone(); let rename_editor = rename_editor.clone();
move |cx: &mut BlockContext| { move |cx: &mut BlockContext| {
@ -14273,7 +14272,7 @@ impl Editor {
placement: BlockPlacement::Below( placement: BlockPlacement::Below(
buffer.anchor_after(entry.range.start), buffer.anchor_after(entry.range.start),
), ),
height: message_height, height: Some(message_height),
render: diagnostic_block_renderer(diagnostic, None, true), render: diagnostic_block_renderer(diagnostic, None, true),
priority: 0, priority: 0,
} }

View file

@ -4234,7 +4234,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
[BlockProperties { [BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))), placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
height: 1, height: Some(1),
render: Arc::new(|_| div().into_any()), render: Arc::new(|_| div().into_any()),
priority: 0, priority: 0,
}], }],
@ -4275,7 +4275,7 @@ async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
editor.insert_blocks( editor.insert_blocks(
[BlockProperties { [BlockProperties {
placement, placement,
height: 4, height: Some(4),
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Arc::new(|_| gpui::div().into_any_element()), render: Arc::new(|_| gpui::div().into_any_element()),
priority: 0, priority: 0,

View file

@ -27,7 +27,7 @@ use crate::{
}; };
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind}; use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
use client::ParticipantIndex; use client::ParticipantIndex;
use collections::{BTreeMap, HashMap, HashSet}; use collections::{BTreeMap, HashMap};
use feature_flags::{Debugger, FeatureFlagAppExt}; use feature_flags::{Debugger, FeatureFlagAppExt};
use file_icons::FileIcons; use file_icons::FileIcons;
use git::{Oid, blame::BlameEntry, status::FileStatus}; use git::{Oid, blame::BlameEntry, status::FileStatus};
@ -1219,7 +1219,7 @@ impl EditorElement {
&self, &self,
snapshot: &EditorSnapshot, snapshot: &EditorSnapshot,
selections: &[(PlayerColor, Vec<SelectionLayout>)], selections: &[(PlayerColor, Vec<SelectionLayout>)],
block_start_rows: &HashSet<DisplayRow>, row_block_types: &HashMap<DisplayRow, bool>,
visible_display_row_range: Range<DisplayRow>, visible_display_row_range: Range<DisplayRow>,
line_layouts: &[LineWithInvisibles], line_layouts: &[LineWithInvisibles],
text_hitbox: &Hitbox, text_hitbox: &Hitbox,
@ -1246,7 +1246,7 @@ impl EditorElement {
let in_range = visible_display_row_range.contains(&cursor_position.row()); let in_range = visible_display_row_range.contains(&cursor_position.row());
if (selection.is_local && !show_local_cursors) if (selection.is_local && !show_local_cursors)
|| !in_range || !in_range
|| block_start_rows.contains(&cursor_position.row()) || row_block_types.get(&cursor_position.row()) == Some(&true)
{ {
continue; continue;
} }
@ -1571,6 +1571,7 @@ impl EditorElement {
&self, &self,
line_layouts: &[LineWithInvisibles], line_layouts: &[LineWithInvisibles],
crease_trailers: &[Option<CreaseTrailerLayout>], crease_trailers: &[Option<CreaseTrailerLayout>],
row_block_types: &HashMap<DisplayRow, bool>,
content_origin: gpui::Point<Pixels>, content_origin: gpui::Point<Pixels>,
scroll_pixel_position: gpui::Point<Pixels>, scroll_pixel_position: gpui::Point<Pixels>,
inline_completion_popover_origin: Option<gpui::Point<Pixels>>, inline_completion_popover_origin: Option<gpui::Point<Pixels>>,
@ -1616,6 +1617,7 @@ impl EditorElement {
.map(|(point, diag)| (point.to_display_point(&snapshot), diag.clone())) .map(|(point, diag)| (point.to_display_point(&snapshot), diag.clone()))
.skip_while(|(point, _)| point.row() < start_row) .skip_while(|(point, _)| point.row() < start_row)
.take_while(|(point, _)| point.row() < end_row) .take_while(|(point, _)| point.row() < end_row)
.filter(|(point, _)| !row_block_types.contains_key(&point.row()))
.fold(HashMap::default(), |mut acc, (point, diagnostic)| { .fold(HashMap::default(), |mut acc, (point, diagnostic)| {
acc.entry(point.row()) acc.entry(point.row())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
@ -2614,34 +2616,41 @@ impl EditorElement {
editor_width: Pixels, editor_width: Pixels,
scroll_width: &mut Pixels, scroll_width: &mut Pixels,
resized_blocks: &mut HashMap<CustomBlockId, u32>, resized_blocks: &mut HashMap<CustomBlockId, u32>,
row_block_types: &mut HashMap<DisplayRow, bool>,
selections: &[Selection<Point>], selections: &[Selection<Point>],
selected_buffer_ids: &Vec<BufferId>, selected_buffer_ids: &Vec<BufferId>,
is_row_soft_wrapped: impl Copy + Fn(usize) -> bool, is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
sticky_header_excerpt_id: Option<ExcerptId>, sticky_header_excerpt_id: Option<ExcerptId>,
window: &mut Window, window: &mut Window,
cx: &mut App, cx: &mut App,
) -> (AnyElement, Size<Pixels>) { ) -> (AnyElement, Size<Pixels>, DisplayRow, Pixels) {
let mut x_position = None;
let mut element = match block { let mut element = match block {
Block::Custom(block) => { Block::Custom(block) => {
let block_start = block.start().to_point(&snapshot.buffer_snapshot); let block_start = block.start().to_point(&snapshot.buffer_snapshot);
let block_end = block.end().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 align_to = block_start.to_display_point(snapshot);
let anchor_x = text_x let x_and_width = |layout: &LineWithInvisibles| {
+ if rows.contains(&align_to.row()) { Some((
line_layouts[align_to.row().minus(rows.start) as usize] text_x + layout.x_for_index(align_to.column() as usize),
.x_for_index(align_to.column() as usize) text_x + layout.width,
} else { ))
layout_line( };
align_to.row(), x_position = if rows.contains(&align_to.row()) {
snapshot, x_and_width(&line_layouts[align_to.row().minus(rows.start) as usize])
&self.style, } else {
editor_width, x_and_width(&layout_line(
is_row_soft_wrapped, align_to.row(),
window, snapshot,
cx, &self.style,
) editor_width,
.x_for_index(align_to.column() as usize) is_row_soft_wrapped,
}; window,
cx,
))
};
let anchor_x = x_position.unwrap().0;
let selected = selections let selected = selections
.binary_search_by(|selection| { .binary_search_by(|selection| {
@ -2746,17 +2755,43 @@ impl EditorElement {
element.layout_as_root(size(available_width, quantized_height.into()), window, cx) element.layout_as_root(size(available_width, quantized_height.into()), window, cx)
}; };
let mut row = block_row_start;
let mut x_offset = px(0.);
let mut is_block = true;
if let BlockId::Custom(custom_block_id) = block_id { if let BlockId::Custom(custom_block_id) = block_id {
if block.height() > 0 { if block.has_height() {
let element_height_in_lines = let mut element_height_in_lines =
((final_size.height / line_height).ceil() as u32).max(1); ((final_size.height / line_height).ceil() as u32).max(1);
if block.place_near() && element_height_in_lines == 1 {
if let Some((x_target, line_width)) = x_position {
let margin = em_width * 2;
if line_width + final_size.width + margin
< editor_width + gutter_dimensions.full_width()
&& !row_block_types.contains_key(&(row - 1))
{
x_offset = line_width + margin;
row = row - 1;
is_block = false;
element_height_in_lines = 0;
} else {
let max_offset =
editor_width + gutter_dimensions.full_width() - final_size.width;
let min_offset = (x_target + em_width - final_size.width)
.max(gutter_dimensions.full_width());
x_offset = x_target.min(max_offset).max(min_offset);
}
}
};
if element_height_in_lines != block.height() { if element_height_in_lines != block.height() {
resized_blocks.insert(custom_block_id, element_height_in_lines); resized_blocks.insert(custom_block_id, element_height_in_lines);
} }
} }
} }
row_block_types.insert(row, is_block);
(element, final_size) (element, final_size, row, x_offset)
} }
fn render_buffer_header( fn render_buffer_header(
@ -2962,14 +2997,14 @@ impl EditorElement {
em_width: Pixels, em_width: Pixels,
text_x: Pixels, text_x: Pixels,
line_height: Pixels, line_height: Pixels,
line_layouts: &[LineWithInvisibles], line_layouts: &mut [LineWithInvisibles],
selections: &[Selection<Point>], selections: &[Selection<Point>],
selected_buffer_ids: &Vec<BufferId>, selected_buffer_ids: &Vec<BufferId>,
is_row_soft_wrapped: impl Copy + Fn(usize) -> bool, is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
sticky_header_excerpt_id: Option<ExcerptId>, sticky_header_excerpt_id: Option<ExcerptId>,
window: &mut Window, window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Result<Vec<BlockLayout>, HashMap<CustomBlockId, u32>> { ) -> Result<(Vec<BlockLayout>, HashMap<DisplayRow, bool>), HashMap<CustomBlockId, u32>> {
let (fixed_blocks, non_fixed_blocks) = snapshot let (fixed_blocks, non_fixed_blocks) = snapshot
.blocks_in_range(rows.clone()) .blocks_in_range(rows.clone())
.partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed); .partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed);
@ -2980,6 +3015,7 @@ impl EditorElement {
let mut fixed_block_max_width = Pixels::ZERO; let mut fixed_block_max_width = Pixels::ZERO;
let mut blocks = Vec::new(); let mut blocks = Vec::new();
let mut resized_blocks = HashMap::default(); let mut resized_blocks = HashMap::default();
let mut row_block_types = HashMap::default();
for (row, block) in fixed_blocks { for (row, block) in fixed_blocks {
let block_id = block.id(); let block_id = block.id();
@ -2988,7 +3024,7 @@ impl EditorElement {
focused_block = None; focused_block = None;
} }
let (element, element_size) = self.render_block( let (element, element_size, row, x_offset) = self.render_block(
block, block,
AvailableSpace::MinContent, AvailableSpace::MinContent,
block_id, block_id,
@ -3004,6 +3040,7 @@ impl EditorElement {
editor_width, editor_width,
scroll_width, scroll_width,
&mut resized_blocks, &mut resized_blocks,
&mut row_block_types,
selections, selections,
selected_buffer_ids, selected_buffer_ids,
is_row_soft_wrapped, is_row_soft_wrapped,
@ -3011,27 +3048,32 @@ impl EditorElement {
window, window,
cx, cx,
); );
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width); fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
blocks.push(BlockLayout { blocks.push(BlockLayout {
id: block_id, id: block_id,
x_offset,
row: Some(row), row: Some(row),
element, element,
available_space: size(AvailableSpace::MinContent, element_size.height.into()), available_space: size(AvailableSpace::MinContent, element_size.height.into()),
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
overlaps_gutter: true,
is_buffer_header: block.is_buffer_header(), is_buffer_header: block.is_buffer_header(),
}); });
} }
for (row, block) in non_fixed_blocks { for (row, block) in non_fixed_blocks {
let style = block.style(); let style = block.style();
let width = match style { let width = match (style, block.place_near()) {
BlockStyle::Sticky => hitbox.size.width, (_, true) => AvailableSpace::MinContent,
BlockStyle::Flex => hitbox (BlockStyle::Sticky, _) => hitbox.size.width.into(),
(BlockStyle::Flex, _) => hitbox
.size .size
.width .width
.max(fixed_block_max_width) .max(fixed_block_max_width)
.max(gutter_dimensions.width + *scroll_width), .max(gutter_dimensions.width + *scroll_width)
BlockStyle::Fixed => unreachable!(), .into(),
(BlockStyle::Fixed, _) => unreachable!(),
}; };
let block_id = block.id(); let block_id = block.id();
@ -3039,9 +3081,9 @@ impl EditorElement {
focused_block = None; focused_block = None;
} }
let (element, element_size) = self.render_block( let (element, element_size, row, x_offset) = self.render_block(
block, block,
width.into(), width,
block_id, block_id,
row, row,
snapshot, snapshot,
@ -3055,6 +3097,7 @@ impl EditorElement {
editor_width, editor_width,
scroll_width, scroll_width,
&mut resized_blocks, &mut resized_blocks,
&mut row_block_types,
selections, selections,
selected_buffer_ids, selected_buffer_ids,
is_row_soft_wrapped, is_row_soft_wrapped,
@ -3065,10 +3108,12 @@ impl EditorElement {
blocks.push(BlockLayout { blocks.push(BlockLayout {
id: block_id, id: block_id,
x_offset,
row: Some(row), row: Some(row),
element, element,
available_space: size(width.into(), element_size.height.into()), available_space: size(width, element_size.height.into()),
style, style,
overlaps_gutter: !block.place_near(),
is_buffer_header: block.is_buffer_header(), is_buffer_header: block.is_buffer_header(),
}); });
} }
@ -3090,7 +3135,7 @@ impl EditorElement {
BlockStyle::Sticky => AvailableSpace::Definite(hitbox.size.width), BlockStyle::Sticky => AvailableSpace::Definite(hitbox.size.width),
}; };
let (element, element_size) = self.render_block( let (element, element_size, _, x_offset) = self.render_block(
&block, &block,
width, width,
focused_block.id, focused_block.id,
@ -3106,6 +3151,7 @@ impl EditorElement {
editor_width, editor_width,
scroll_width, scroll_width,
&mut resized_blocks, &mut resized_blocks,
&mut row_block_types,
selections, selections,
selected_buffer_ids, selected_buffer_ids,
is_row_soft_wrapped, is_row_soft_wrapped,
@ -3116,10 +3162,12 @@ impl EditorElement {
blocks.push(BlockLayout { blocks.push(BlockLayout {
id: block.id(), id: block.id(),
x_offset,
row: None, row: None,
element, element,
available_space: size(width, element_size.height.into()), available_space: size(width, element_size.height.into()),
style, style,
overlaps_gutter: true,
is_buffer_header: block.is_buffer_header(), is_buffer_header: block.is_buffer_header(),
}); });
} }
@ -3129,18 +3177,15 @@ impl EditorElement {
if resized_blocks.is_empty() { if resized_blocks.is_empty() {
*scroll_width = (*scroll_width).max(fixed_block_max_width - gutter_dimensions.width); *scroll_width = (*scroll_width).max(fixed_block_max_width - gutter_dimensions.width);
Ok(blocks) Ok((blocks, row_block_types))
} else { } else {
Err(resized_blocks) Err(resized_blocks)
} }
} }
/// Returns true if any of the blocks changed size since the previous frame. This will trigger
/// a restart of rendering for the editor based on the new sizes.
fn layout_blocks( fn layout_blocks(
&self, &self,
blocks: &mut Vec<BlockLayout>, blocks: &mut Vec<BlockLayout>,
block_starts: &mut HashSet<DisplayRow>,
hitbox: &Hitbox, hitbox: &Hitbox,
line_height: Pixels, line_height: Pixels,
scroll_pixel_position: gpui::Point<Pixels>, scroll_pixel_position: gpui::Point<Pixels>,
@ -3149,10 +3194,9 @@ impl EditorElement {
) { ) {
for block in blocks { for block in blocks {
let mut origin = if let Some(row) = block.row { let mut origin = if let Some(row) = block.row {
block_starts.insert(row);
hitbox.origin hitbox.origin
+ point( + point(
Pixels::ZERO, block.x_offset,
row.as_f32() * line_height - scroll_pixel_position.y, row.as_f32() * line_height - scroll_pixel_position.y,
) )
} else { } else {
@ -5322,7 +5366,15 @@ impl EditorElement {
fn paint_blocks(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) { fn paint_blocks(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
for mut block in layout.blocks.drain(..) { for mut block in layout.blocks.drain(..) {
block.element.paint(window, cx); if block.overlaps_gutter {
block.element.paint(window, cx);
} else {
let mut bounds = layout.hitbox.bounds;
bounds.origin.x += layout.gutter_hitbox.bounds.size.width;
window.with_content_mask(Some(ContentMask { bounds }), |window| {
block.element.paint(window, cx);
})
}
} }
} }
@ -6955,7 +7007,7 @@ impl Element for EditorElement {
em_width, em_width,
gutter_dimensions.full_width(), gutter_dimensions.full_width(),
line_height, line_height,
&line_layouts, &mut line_layouts,
&local_selections, &local_selections,
&selected_buffer_ids, &selected_buffer_ids,
is_row_soft_wrapped, is_row_soft_wrapped,
@ -6964,7 +7016,7 @@ impl Element for EditorElement {
cx, cx,
) )
}); });
let mut blocks = match blocks { let (mut blocks, row_block_types) = match blocks {
Ok(blocks) => blocks, Ok(blocks) => blocks,
Err(resized_blocks) => { Err(resized_blocks) => {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
@ -7078,6 +7130,7 @@ impl Element for EditorElement {
let mut inline_diagnostics = self.layout_inline_diagnostics( let mut inline_diagnostics = self.layout_inline_diagnostics(
&line_layouts, &line_layouts,
&crease_trailers, &crease_trailers,
&row_block_types,
content_origin, content_origin,
scroll_pixel_position, scroll_pixel_position,
inline_completion_popover_origin, inline_completion_popover_origin,
@ -7093,7 +7146,9 @@ impl Element for EditorElement {
let mut inline_blame = None; let mut inline_blame = None;
if let Some(newest_selection_head) = newest_selection_head { if let Some(newest_selection_head) = newest_selection_head {
let display_row = newest_selection_head.row(); let display_row = newest_selection_head.row();
if (start_row..end_row).contains(&display_row) { if (start_row..end_row).contains(&display_row)
&& !row_block_types.contains_key(&display_row)
{
let line_ix = display_row.minus(start_row) as usize; let line_ix = display_row.minus(start_row) as usize;
let row_info = &row_infos[line_ix]; let row_info = &row_infos[line_ix];
let line_layout = &line_layouts[line_ix]; let line_layout = &line_layouts[line_ix];
@ -7161,12 +7216,9 @@ impl Element for EditorElement {
cx, cx,
); );
let mut block_start_rows = HashSet::default();
window.with_element_namespace("blocks", |window| { window.with_element_namespace("blocks", |window| {
self.layout_blocks( self.layout_blocks(
&mut blocks, &mut blocks,
&mut block_start_rows,
&hitbox, &hitbox,
line_height, line_height,
scroll_pixel_position, scroll_pixel_position,
@ -7184,7 +7236,7 @@ impl Element for EditorElement {
let visible_cursors = self.layout_visible_cursors( let visible_cursors = self.layout_visible_cursors(
&snapshot, &snapshot,
&selections, &selections,
&block_start_rows, &row_block_types,
start_row..end_row, start_row..end_row,
&line_layouts, &line_layouts,
&text_hitbox, &text_hitbox,
@ -8028,10 +8080,12 @@ impl PositionMap {
struct BlockLayout { struct BlockLayout {
id: BlockId, id: BlockId,
x_offset: Pixels,
row: Option<DisplayRow>, row: Option<DisplayRow>,
element: AnyElement, element: AnyElement,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
style: BlockStyle, style: BlockStyle,
overlaps_gutter: bool,
is_buffer_header: bool, is_buffer_header: bool,
} }
@ -8620,7 +8674,7 @@ mod tests {
[BlockProperties { [BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
placement: BlockPlacement::Above(Anchor::min()), placement: BlockPlacement::Above(Anchor::min()),
height: 3, height: Some(3),
render: Arc::new(|cx| div().h(3. * cx.window.line_height()).into_any()), render: Arc::new(|cx| div().h(3. * cx.window.line_height()).into_any()),
priority: 0, priority: 0,
}], }],

View file

@ -57,6 +57,10 @@ impl Anchor {
} }
pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering { pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
if self == other {
return Ordering::Equal;
}
let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id, snapshot); let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id, snapshot);
if excerpt_id_cmp.is_ne() { if excerpt_id_cmp.is_ne() {
return excerpt_id_cmp; return excerpt_id_cmp;

View file

@ -89,7 +89,7 @@ impl EditorBlock {
let block = BlockProperties { let block = BlockProperties {
placement: BlockPlacement::Below(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: Some(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()),
priority: 0, priority: 0,