Detailed changes
@@ -621,14 +621,14 @@ impl InlineAssistant {
BlockProperties {
style: BlockStyle::Sticky,
placement: BlockPlacement::Above(range.start),
- height: prompt_editor_height,
+ height: Some(prompt_editor_height),
render: build_assist_editor_renderer(prompt_editor),
priority: 0,
},
BlockProperties {
style: BlockStyle::Sticky,
placement: BlockPlacement::Below(range.end),
- height: 0,
+ height: None,
render: Arc::new(|cx| {
v_flex()
.h_full()
@@ -1392,7 +1392,7 @@ impl InlineAssistant {
deleted_lines_editor.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1);
new_blocks.push(BlockProperties {
placement: BlockPlacement::Above(new_row),
- height,
+ height: Some(height),
style: BlockStyle::Flex,
render: Arc::new(move |cx| {
div()
@@ -527,14 +527,14 @@ impl InlineAssistant {
BlockProperties {
style: BlockStyle::Sticky,
placement: BlockPlacement::Above(range.start),
- height: prompt_editor_height,
+ height: Some(prompt_editor_height),
render: build_assist_editor_renderer(prompt_editor),
priority: 0,
},
BlockProperties {
style: BlockStyle::Sticky,
placement: BlockPlacement::Below(range.end),
- height: 0,
+ height: None,
render: Arc::new(|cx| {
v_flex()
.h_full()
@@ -1301,7 +1301,7 @@ impl InlineAssistant {
deleted_lines_editor.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1);
new_blocks.push(BlockProperties {
placement: BlockPlacement::Above(new_row),
- height,
+ height: Some(height),
style: BlockStyle::Flex,
render: Arc::new(move |cx| {
div()
@@ -1562,7 +1562,7 @@ impl ContextEditor {
})
};
let create_block_properties = |message: &Message| BlockProperties {
- height: 2,
+ height: Some(2),
style: BlockStyle::Sticky,
placement: BlockPlacement::Above(
buffer
@@ -2111,7 +2111,7 @@ impl ContextEditor {
let image = render_image.clone();
anchor.is_valid(&buffer).then(|| BlockProperties {
placement: BlockPlacement::Above(anchor),
- height: MAX_HEIGHT_IN_LINES,
+ height: Some(MAX_HEIGHT_IN_LINES),
style: BlockStyle::Sticky,
render: Arc::new(move |cx| {
let image_size = size_for_image(
@@ -535,7 +535,7 @@ impl ProjectDiagnosticsEditor {
group_state.block_count += 1;
blocks_to_add.push(BlockProperties {
placement: BlockPlacement::Above(header_position),
- height: 2,
+ height: Some(2),
style: BlockStyle::Sticky,
render: diagnostic_header_renderer(primary),
priority: 0,
@@ -557,7 +557,9 @@ impl ProjectDiagnosticsEditor {
excerpt_id,
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,
render: diagnostic_block_renderer(diagnostic, None, true),
priority: 0,
@@ -613,9 +615,9 @@ impl ProjectDiagnosticsEditor {
excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor)?,
)
}
- BlockPlacement::Replace(_) => {
+ BlockPlacement::Replace(_) | BlockPlacement::Near(_) => {
unreachable!(
- "no Replace block should have been pushed to blocks_to_add"
+ "no Near/Replace block should have been pushed to blocks_to_add"
)
}
};
@@ -255,7 +255,7 @@ impl DisplayMap {
BlockProperties {
placement: BlockPlacement::Replace(start..=end),
render,
- height,
+ height: Some(height),
style,
priority,
}
@@ -1591,7 +1591,7 @@ pub mod tests {
BlockProperties {
placement,
style: BlockStyle::Fixed,
- height,
+ height: Some(height),
render: Arc::new(|_| div().into_any()),
priority,
}
@@ -1953,7 +1953,7 @@ pub mod tests {
placement: BlockPlacement::Above(
buffer_snapshot.anchor_before(Point::new(0, 0)),
),
- height: 2,
+ height: Some(2),
style: BlockStyle::Sticky,
render: Arc::new(|_| div().into_any()),
priority: 0,
@@ -2148,7 +2148,7 @@ pub mod tests {
placement: BlockPlacement::Below(
buffer_snapshot.anchor_before(Point::new(1, 0)),
),
- height: 1,
+ height: Some(1),
style: BlockStyle::Sticky,
render: Arc::new(|_| div().into_any()),
priority: 0,
@@ -2157,7 +2157,7 @@ pub mod tests {
placement: BlockPlacement::Below(
buffer_snapshot.anchor_before(Point::new(2, 0)),
),
- height: 0,
+ height: None,
style: BlockStyle::Sticky,
render: Arc::new(|_| div().into_any()),
priority: 0,
@@ -2262,7 +2262,7 @@ pub mod tests {
placement: BlockPlacement::Below(
buffer_snapshot.anchor_before(Point::new(1, 0)),
),
- height: 1,
+ height: Some(1),
style: BlockStyle::Sticky,
render: Arc::new(|_| div().into_any()),
priority: 0,
@@ -2336,7 +2336,7 @@ pub mod tests {
buffer_snapshot.anchor_before(Point::new(1, 2))
..=buffer_snapshot.anchor_after(Point::new(2, 3)),
),
- height: 4,
+ height: Some(4),
style: BlockStyle::Fixed,
render: Arc::new(|_| div().into_any()),
priority: 0,
@@ -84,6 +84,7 @@ pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement
pub enum BlockPlacement<T> {
Above(T),
Below(T),
+ Near(T),
Replace(RangeInclusive<T>),
}
@@ -92,6 +93,7 @@ impl<T> BlockPlacement<T> {
match self {
BlockPlacement::Above(position) => position,
BlockPlacement::Below(position) => position,
+ BlockPlacement::Near(position) => position,
BlockPlacement::Replace(range) => range.start(),
}
}
@@ -100,6 +102,7 @@ impl<T> BlockPlacement<T> {
match self {
BlockPlacement::Above(position) => position,
BlockPlacement::Below(position) => position,
+ BlockPlacement::Near(position) => position,
BlockPlacement::Replace(range) => range.end(),
}
}
@@ -108,6 +111,7 @@ impl<T> BlockPlacement<T> {
match self {
BlockPlacement::Above(position) => BlockPlacement::Above(position),
BlockPlacement::Below(position) => BlockPlacement::Below(position),
+ BlockPlacement::Near(position) => BlockPlacement::Near(position),
BlockPlacement::Replace(range) => BlockPlacement::Replace(range.start()..=range.end()),
}
}
@@ -116,44 +120,30 @@ impl<T> BlockPlacement<T> {
match self {
BlockPlacement::Above(position) => BlockPlacement::Above(f(position)),
BlockPlacement::Below(position) => BlockPlacement::Below(f(position)),
+ BlockPlacement::Near(position) => BlockPlacement::Near(f(position)),
BlockPlacement::Replace(range) => {
let (start, end) = range.into_inner();
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> {
fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
- match (self, other) {
- (BlockPlacement::Above(anchor_a), BlockPlacement::Above(anchor_b))
- | (BlockPlacement::Below(anchor_a), BlockPlacement::Below(anchor_b)) => {
- anchor_a.cmp(anchor_b, buffer)
- }
- (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)),
- }
+ self.start()
+ .cmp(other.start(), buffer)
+ .then_with(|| other.end().cmp(self.end(), buffer))
+ .then_with(|| self.sort_order().cmp(&other.sort_order()))
}
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());
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) => {
let mut position = position.to_point(buffer_snapshot);
position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
@@ -193,7 +189,7 @@ impl BlockPlacement<Anchor> {
pub struct CustomBlock {
id: CustomBlockId,
placement: BlockPlacement<Anchor>,
- height: u32,
+ height: Option<u32>,
style: BlockStyle,
render: Arc<Mutex<RenderBlock>>,
priority: usize,
@@ -201,7 +197,9 @@ pub struct CustomBlock {
pub struct BlockProperties<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 render: RenderBlock,
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 {
match self {
- Block::Custom(block) => block.height,
+ Block::Custom(block) => block.height.unwrap_or(0),
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 {
match self {
- Block::Custom(block) => matches!(block.placement, BlockPlacement::Below(_)),
+ Block::Custom(block) => matches!(
+ block.placement,
+ BlockPlacement::Below(_) | BlockPlacement::Near(_)
+ ),
Block::FoldedBuffer { .. } => false,
Block::ExcerptBoundary { .. } => false,
}
@@ -669,7 +685,7 @@ impl BlockMap {
BlockPlacement::Above(position) => {
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;
}
BlockPlacement::Replace(range) => {
@@ -803,60 +819,39 @@ impl BlockMap {
fn sort_blocks(blocks: &mut Vec<(BlockPlacement<WrapRow>, Block)>) {
blocks.sort_unstable_by(|(placement_a, block_a), (placement_b, block_b)| {
- let placement_comparison = match (placement_a, placement_b) {
- (BlockPlacement::Above(row_a), BlockPlacement::Above(row_b))
- | (BlockPlacement::Below(row_a), BlockPlacement::Below(row_b)) => row_a.cmp(row_b),
- (BlockPlacement::Above(row_a), BlockPlacement::Below(row_b)) => {
- row_a.cmp(row_b).then(Ordering::Less)
- }
- (BlockPlacement::Below(row_a), BlockPlacement::Above(row_b)) => {
- row_a.cmp(row_b).then(Ordering::Greater)
- }
- (BlockPlacement::Above(row), BlockPlacement::Replace(range)) => {
- row.cmp(range.start()).then(Ordering::Greater)
- }
- (BlockPlacement::Replace(range), BlockPlacement::Above(row)) => {
- range.start().cmp(row).then(Ordering::Less)
- }
- (BlockPlacement::Below(row), BlockPlacement::Replace(range)) => {
- row.cmp(range.start()).then(Ordering::Greater)
- }
- (BlockPlacement::Replace(range), BlockPlacement::Below(row)) => {
- range.start().cmp(row).then(Ordering::Less)
- }
- (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => range_a
- .start()
- .cmp(range_b.start())
- .then_with(|| range_b.end().cmp(range_a.end()))
- .then_with(|| {
- if block_a.is_header() {
- Ordering::Less
- } else if block_b.is_header() {
- Ordering::Greater
- } else {
- 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!()
- }
- })
+ placement_a
+ .start()
+ .cmp(placement_b.start())
+ .then_with(|| placement_b.end().cmp(placement_a.end()))
+ .then_with(|| {
+ if block_a.is_header() {
+ Ordering::Less
+ } else if block_b.is_header() {
+ Ordering::Greater
+ } else {
+ Ordering::Equal
+ }
+ })
+ .then_with(|| placement_a.sort_order().cmp(&placement_b.sort_order()))
+ .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()) {
(BlockPlacement::Replace(range), BlockPlacement::Above(row))
@@ -999,7 +994,7 @@ impl BlockMapWriter<'_> {
let mut previous_wrap_row_range: Option<Range<u32>> = None;
for block in blocks {
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));
@@ -1064,11 +1059,11 @@ impl BlockMapWriter<'_> {
debug_assert!(new_height > 0);
}
- if block.height != new_height {
+ if block.height != Some(new_height) {
let new_block = CustomBlock {
id: block.id,
placement: block.placement.clone(),
- height: new_height,
+ height: Some(new_height),
style: block.style,
render: block.render.clone(),
priority: block.priority,
@@ -1965,21 +1960,21 @@ mod tests {
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
- height: 2,
+ height: Some(2),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
- height: 3,
+ height: Some(3),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
@@ -1992,7 +1987,7 @@ mod tests {
.blocks_in_range(0..8)
.map(|(start_row, block)| {
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<_>>();
@@ -2203,21 +2198,21 @@ mod tests {
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
- height: 2,
+ height: Some(2),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
- height: 3,
+ height: Some(3),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
@@ -2307,14 +2302,14 @@ mod tests {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
render: Arc::new(|_| div().into_any()),
- height: 1,
+ height: Some(1),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
render: Arc::new(|_| div().into_any()),
- height: 1,
+ height: Some(1),
priority: 0,
},
]);
@@ -2352,7 +2347,7 @@ mod tests {
buffer_snapshot.anchor_after(Point::new(1, 3))
..=buffer_snapshot.anchor_before(Point::new(3, 1)),
),
- height: 4,
+ height: Some(4),
render: Arc::new(|_| div().into_any()),
priority: 0,
}])[0];
@@ -2405,21 +2400,21 @@ mod tests {
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
@@ -2433,21 +2428,21 @@ mod tests {
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
@@ -2546,21 +2541,21 @@ mod tests {
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
@@ -2569,14 +2564,14 @@ mod tests {
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
},
@@ -2623,7 +2618,7 @@ mod tests {
let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
}]);
@@ -2980,7 +2975,7 @@ mod tests {
BlockProperties {
style: BlockStyle::Fixed,
placement,
- height,
+ height: Some(height),
render: Arc::new(|_| div().into_any()),
priority: 0,
}
@@ -3007,7 +3002,7 @@ mod tests {
for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
log::info!(
- "inserted block {:?} with height {} and id {:?}",
+ "inserted block {:?} with height {:?} and id {:?}",
block_properties
.placement
.as_ref()
@@ -3524,7 +3519,7 @@ mod tests {
let _block_id = writer.insert(vec![BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
}])[0];
@@ -14,6 +14,7 @@ use std::{
fmt, iter,
ops::{Add, AddAssign, Deref, DerefMut, Range, Sub},
sync::Arc,
+ usize,
};
use sum_tree::{Bias, Cursor, FilterCursor, SumTree, Summary, TreeMap};
use ui::IntoElement as _;
@@ -8717,7 +8717,7 @@ impl Editor {
let blocks = vec![BlockProperties {
style: BlockStyle::Sticky,
placement: BlockPlacement::Above(anchor),
- height,
+ height: Some(height),
render: Arc::new(move |cx| {
*cloned_prompt.read(cx).gutter_dimensions.lock() = *cx.gutter_dimensions;
cloned_prompt.clone().into_any_element()
@@ -12801,7 +12801,6 @@ impl Editor {
) {
let buffer = self.buffer.read(cx).snapshot(cx);
let selection = self.selections.newest::<usize>(cx);
-
// If there is an active Diagnostic Popover jump to its diagnostic instead.
if direction == Direction::Next {
if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
@@ -13806,7 +13805,7 @@ impl Editor {
[BlockProperties {
style: BlockStyle::Flex,
placement: BlockPlacement::Below(range.start),
- height: 1,
+ height: Some(1),
render: Arc::new({
let rename_editor = rename_editor.clone();
move |cx: &mut BlockContext| {
@@ -14273,7 +14272,7 @@ impl Editor {
placement: BlockPlacement::Below(
buffer.anchor_after(entry.range.start),
),
- height: message_height,
+ height: Some(message_height),
render: diagnostic_block_renderer(diagnostic, None, true),
priority: 0,
}
@@ -4234,7 +4234,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
[BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Below(snapshot.anchor_after(Point::new(2, 0))),
- height: 1,
+ height: Some(1),
render: Arc::new(|_| div().into_any()),
priority: 0,
}],
@@ -4275,7 +4275,7 @@ async fn test_selections_and_replace_blocks(cx: &mut TestAppContext) {
editor.insert_blocks(
[BlockProperties {
placement,
- height: 4,
+ height: Some(4),
style: BlockStyle::Sticky,
render: Arc::new(|_| gpui::div().into_any_element()),
priority: 0,
@@ -27,7 +27,7 @@ use crate::{
};
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
use client::ParticipantIndex;
-use collections::{BTreeMap, HashMap, HashSet};
+use collections::{BTreeMap, HashMap};
use feature_flags::{Debugger, FeatureFlagAppExt};
use file_icons::FileIcons;
use git::{Oid, blame::BlameEntry, status::FileStatus};
@@ -1219,7 +1219,7 @@ impl EditorElement {
&self,
snapshot: &EditorSnapshot,
selections: &[(PlayerColor, Vec<SelectionLayout>)],
- block_start_rows: &HashSet<DisplayRow>,
+ row_block_types: &HashMap<DisplayRow, bool>,
visible_display_row_range: Range<DisplayRow>,
line_layouts: &[LineWithInvisibles],
text_hitbox: &Hitbox,
@@ -1246,7 +1246,7 @@ impl EditorElement {
let in_range = visible_display_row_range.contains(&cursor_position.row());
if (selection.is_local && !show_local_cursors)
|| !in_range
- || block_start_rows.contains(&cursor_position.row())
+ || row_block_types.get(&cursor_position.row()) == Some(&true)
{
continue;
}
@@ -1571,6 +1571,7 @@ impl EditorElement {
&self,
line_layouts: &[LineWithInvisibles],
crease_trailers: &[Option<CreaseTrailerLayout>],
+ row_block_types: &HashMap<DisplayRow, bool>,
content_origin: gpui::Point<Pixels>,
scroll_pixel_position: 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()))
.skip_while(|(point, _)| point.row() < start_row)
.take_while(|(point, _)| point.row() < end_row)
+ .filter(|(point, _)| !row_block_types.contains_key(&point.row()))
.fold(HashMap::default(), |mut acc, (point, diagnostic)| {
acc.entry(point.row())
.or_insert_with(Vec::new)
@@ -2614,34 +2616,41 @@ impl EditorElement {
editor_width: Pixels,
scroll_width: &mut Pixels,
resized_blocks: &mut HashMap<CustomBlockId, u32>,
+ row_block_types: &mut HashMap<DisplayRow, bool>,
selections: &[Selection<Point>],
selected_buffer_ids: &Vec<BufferId>,
is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
sticky_header_excerpt_id: Option<ExcerptId>,
window: &mut Window,
cx: &mut App,
- ) -> (AnyElement, Size<Pixels>) {
+ ) -> (AnyElement, Size<Pixels>, DisplayRow, Pixels) {
+ let mut x_position = None;
let mut element = match block {
Block::Custom(block) => {
let block_start = block.start().to_point(&snapshot.buffer_snapshot);
let block_end = block.end().to_point(&snapshot.buffer_snapshot);
let align_to = block_start.to_display_point(snapshot);
- let anchor_x = text_x
- + if rows.contains(&align_to.row()) {
- line_layouts[align_to.row().minus(rows.start) as usize]
- .x_for_index(align_to.column() as usize)
- } else {
- layout_line(
- align_to.row(),
- snapshot,
- &self.style,
- editor_width,
- is_row_soft_wrapped,
- window,
- cx,
- )
- .x_for_index(align_to.column() as usize)
- };
+ let x_and_width = |layout: &LineWithInvisibles| {
+ Some((
+ text_x + layout.x_for_index(align_to.column() as usize),
+ text_x + layout.width,
+ ))
+ };
+ x_position = if rows.contains(&align_to.row()) {
+ x_and_width(&line_layouts[align_to.row().minus(rows.start) as usize])
+ } else {
+ x_and_width(&layout_line(
+ align_to.row(),
+ snapshot,
+ &self.style,
+ editor_width,
+ is_row_soft_wrapped,
+ window,
+ cx,
+ ))
+ };
+
+ let anchor_x = x_position.unwrap().0;
let selected = selections
.binary_search_by(|selection| {
@@ -2746,17 +2755,43 @@ impl EditorElement {
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 block.height() > 0 {
- let element_height_in_lines =
+ if block.has_height() {
+ let mut element_height_in_lines =
((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() {
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(
@@ -2962,14 +2997,14 @@ impl EditorElement {
em_width: Pixels,
text_x: Pixels,
line_height: Pixels,
- line_layouts: &[LineWithInvisibles],
+ line_layouts: &mut [LineWithInvisibles],
selections: &[Selection<Point>],
selected_buffer_ids: &Vec<BufferId>,
is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
sticky_header_excerpt_id: Option<ExcerptId>,
window: &mut Window,
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
.blocks_in_range(rows.clone())
.partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed);
@@ -2980,6 +3015,7 @@ impl EditorElement {
let mut fixed_block_max_width = Pixels::ZERO;
let mut blocks = Vec::new();
let mut resized_blocks = HashMap::default();
+ let mut row_block_types = HashMap::default();
for (row, block) in fixed_blocks {
let block_id = block.id();
@@ -2988,7 +3024,7 @@ impl EditorElement {
focused_block = None;
}
- let (element, element_size) = self.render_block(
+ let (element, element_size, row, x_offset) = self.render_block(
block,
AvailableSpace::MinContent,
block_id,
@@ -3004,6 +3040,7 @@ impl EditorElement {
editor_width,
scroll_width,
&mut resized_blocks,
+ &mut row_block_types,
selections,
selected_buffer_ids,
is_row_soft_wrapped,
@@ -3011,27 +3048,32 @@ impl EditorElement {
window,
cx,
);
+
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
blocks.push(BlockLayout {
id: block_id,
+ x_offset,
row: Some(row),
element,
available_space: size(AvailableSpace::MinContent, element_size.height.into()),
style: BlockStyle::Fixed,
+ overlaps_gutter: true,
is_buffer_header: block.is_buffer_header(),
});
}
for (row, block) in non_fixed_blocks {
let style = block.style();
- let width = match style {
- BlockStyle::Sticky => hitbox.size.width,
- BlockStyle::Flex => hitbox
+ let width = match (style, block.place_near()) {
+ (_, true) => AvailableSpace::MinContent,
+ (BlockStyle::Sticky, _) => hitbox.size.width.into(),
+ (BlockStyle::Flex, _) => hitbox
.size
.width
.max(fixed_block_max_width)
- .max(gutter_dimensions.width + *scroll_width),
- BlockStyle::Fixed => unreachable!(),
+ .max(gutter_dimensions.width + *scroll_width)
+ .into(),
+ (BlockStyle::Fixed, _) => unreachable!(),
};
let block_id = block.id();
@@ -3039,9 +3081,9 @@ impl EditorElement {
focused_block = None;
}
- let (element, element_size) = self.render_block(
+ let (element, element_size, row, x_offset) = self.render_block(
block,
- width.into(),
+ width,
block_id,
row,
snapshot,
@@ -3055,6 +3097,7 @@ impl EditorElement {
editor_width,
scroll_width,
&mut resized_blocks,
+ &mut row_block_types,
selections,
selected_buffer_ids,
is_row_soft_wrapped,
@@ -3065,10 +3108,12 @@ impl EditorElement {
blocks.push(BlockLayout {
id: block_id,
+ x_offset,
row: Some(row),
element,
- available_space: size(width.into(), element_size.height.into()),
+ available_space: size(width, element_size.height.into()),
style,
+ overlaps_gutter: !block.place_near(),
is_buffer_header: block.is_buffer_header(),
});
}
@@ -3090,7 +3135,7 @@ impl EditorElement {
BlockStyle::Sticky => AvailableSpace::Definite(hitbox.size.width),
};
- let (element, element_size) = self.render_block(
+ let (element, element_size, _, x_offset) = self.render_block(
&block,
width,
focused_block.id,
@@ -3106,6 +3151,7 @@ impl EditorElement {
editor_width,
scroll_width,
&mut resized_blocks,
+ &mut row_block_types,
selections,
selected_buffer_ids,
is_row_soft_wrapped,
@@ -3116,10 +3162,12 @@ impl EditorElement {
blocks.push(BlockLayout {
id: block.id(),
+ x_offset,
row: None,
element,
available_space: size(width, element_size.height.into()),
style,
+ overlaps_gutter: true,
is_buffer_header: block.is_buffer_header(),
});
}
@@ -3129,18 +3177,15 @@ impl EditorElement {
if resized_blocks.is_empty() {
*scroll_width = (*scroll_width).max(fixed_block_max_width - gutter_dimensions.width);
- Ok(blocks)
+ Ok((blocks, row_block_types))
} else {
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(
&self,
blocks: &mut Vec<BlockLayout>,
- block_starts: &mut HashSet<DisplayRow>,
hitbox: &Hitbox,
line_height: Pixels,
scroll_pixel_position: gpui::Point<Pixels>,
@@ -3149,10 +3194,9 @@ impl EditorElement {
) {
for block in blocks {
let mut origin = if let Some(row) = block.row {
- block_starts.insert(row);
hitbox.origin
+ point(
- Pixels::ZERO,
+ block.x_offset,
row.as_f32() * line_height - scroll_pixel_position.y,
)
} else {
@@ -5322,7 +5366,15 @@ impl EditorElement {
fn paint_blocks(&mut self, layout: &mut EditorLayout, window: &mut Window, cx: &mut App) {
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,
gutter_dimensions.full_width(),
line_height,
- &line_layouts,
+ &mut line_layouts,
&local_selections,
&selected_buffer_ids,
is_row_soft_wrapped,
@@ -6964,7 +7016,7 @@ impl Element for EditorElement {
cx,
)
});
- let mut blocks = match blocks {
+ let (mut blocks, row_block_types) = match blocks {
Ok(blocks) => blocks,
Err(resized_blocks) => {
self.editor.update(cx, |editor, cx| {
@@ -7078,6 +7130,7 @@ impl Element for EditorElement {
let mut inline_diagnostics = self.layout_inline_diagnostics(
&line_layouts,
&crease_trailers,
+ &row_block_types,
content_origin,
scroll_pixel_position,
inline_completion_popover_origin,
@@ -7093,7 +7146,9 @@ impl Element for EditorElement {
let mut inline_blame = None;
if let Some(newest_selection_head) = newest_selection_head {
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 row_info = &row_infos[line_ix];
let line_layout = &line_layouts[line_ix];
@@ -7161,12 +7216,9 @@ impl Element for EditorElement {
cx,
);
- let mut block_start_rows = HashSet::default();
-
window.with_element_namespace("blocks", |window| {
self.layout_blocks(
&mut blocks,
- &mut block_start_rows,
&hitbox,
line_height,
scroll_pixel_position,
@@ -7184,7 +7236,7 @@ impl Element for EditorElement {
let visible_cursors = self.layout_visible_cursors(
&snapshot,
&selections,
- &block_start_rows,
+ &row_block_types,
start_row..end_row,
&line_layouts,
&text_hitbox,
@@ -8028,10 +8080,12 @@ impl PositionMap {
struct BlockLayout {
id: BlockId,
+ x_offset: Pixels,
row: Option<DisplayRow>,
element: AnyElement,
available_space: Size<AvailableSpace>,
style: BlockStyle,
+ overlaps_gutter: bool,
is_buffer_header: bool,
}
@@ -8620,7 +8674,7 @@ mod tests {
[BlockProperties {
style: BlockStyle::Fixed,
placement: BlockPlacement::Above(Anchor::min()),
- height: 3,
+ height: Some(3),
render: Arc::new(|cx| div().h(3. * cx.window.line_height()).into_any()),
priority: 0,
}],
@@ -57,6 +57,10 @@ impl Anchor {
}
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);
if excerpt_id_cmp.is_ne() {
return excerpt_id_cmp;
@@ -89,7 +89,7 @@ impl EditorBlock {
let block = BlockProperties {
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,
+ height: Some(1),
style: BlockStyle::Sticky,
render: Self::create_output_area_renderer(execution_view.clone(), on_close.clone()),
priority: 0,