diff --git a/crates/agent/src/inline_assistant.rs b/crates/agent/src/inline_assistant.rs index b88a6e9c5d..45c8abdb7a 100644 --- a/crates/agent/src/inline_assistant.rs +++ b/crates/agent/src/inline_assistant.rs @@ -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() diff --git a/crates/assistant/src/inline_assistant.rs b/crates/assistant/src/inline_assistant.rs index 97c6d5785b..d5914292de 100644 --- a/crates/assistant/src/inline_assistant.rs +++ b/crates/assistant/src/inline_assistant.rs @@ -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() diff --git a/crates/assistant_context_editor/src/context_editor.rs b/crates/assistant_context_editor/src/context_editor.rs index 1c7396bd92..77fcbf0499 100644 --- a/crates/assistant_context_editor/src/context_editor.rs +++ b/crates/assistant_context_editor/src/context_editor.rs @@ -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( diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 46d67fdba7..fc336c7836 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -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" ) } }; diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index b2cbf6e080..896ee0be81 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -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, diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index fe427c3f56..3e8aaaaefb 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -84,6 +84,7 @@ pub type RenderBlock = Arc AnyElement pub enum BlockPlacement { Above(T), Below(T), + Near(T), Replace(RangeInclusive), } @@ -92,6 +93,7 @@ impl BlockPlacement { match self { BlockPlacement::Above(position) => position, BlockPlacement::Below(position) => position, + BlockPlacement::Near(position) => position, BlockPlacement::Replace(range) => range.start(), } } @@ -100,6 +102,7 @@ impl BlockPlacement { match self { BlockPlacement::Above(position) => position, BlockPlacement::Below(position) => position, + BlockPlacement::Near(position) => position, BlockPlacement::Replace(range) => range.end(), } } @@ -108,6 +111,7 @@ impl BlockPlacement { 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 BlockPlacement { 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 { 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> { @@ -165,6 +155,12 @@ impl BlockPlacement { 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 { pub struct CustomBlock { id: CustomBlockId, placement: BlockPlacement, - height: u32, + height: Option, style: BlockStyle, render: Arc>, priority: usize, @@ -201,7 +197,9 @@ pub struct CustomBlock { pub struct BlockProperties

{ pub placement: BlockPlacement

, - pub height: u32, + // None if the block takes up no space + // (e.g. a horizontal line) + pub height: Option, 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, 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> = 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::>(); @@ -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]; diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 0ebc42cf60..a8b5d135fc 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -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 _; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 9ef6a969aa..9148e28816 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -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::(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, } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index ae222321b3..8d986db7f8 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -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, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 4ec4ad12e6..30dc317c64 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -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)], - block_start_rows: &HashSet, + row_block_types: &HashMap, visible_display_row_range: Range, 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], + row_block_types: &HashMap, content_origin: gpui::Point, scroll_pixel_position: gpui::Point, inline_completion_popover_origin: Option>, @@ -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, + row_block_types: &mut HashMap, selections: &[Selection], selected_buffer_ids: &Vec, is_row_soft_wrapped: impl Copy + Fn(usize) -> bool, sticky_header_excerpt_id: Option, window: &mut Window, cx: &mut App, - ) -> (AnyElement, Size) { + ) -> (AnyElement, Size, 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], selected_buffer_ids: &Vec, is_row_soft_wrapped: impl Copy + Fn(usize) -> bool, sticky_header_excerpt_id: Option, window: &mut Window, cx: &mut App, - ) -> Result, HashMap> { + ) -> Result<(Vec, HashMap), HashMap> { let (fixed_blocks, non_fixed_blocks) = snapshot .blocks_in_range(rows.clone()) .partition::, _>(|(_, 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, - block_starts: &mut HashSet, hitbox: &Hitbox, line_height: Pixels, scroll_pixel_position: gpui::Point, @@ -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, element: AnyElement, available_space: Size, 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, }], diff --git a/crates/multi_buffer/src/anchor.rs b/crates/multi_buffer/src/anchor.rs index 3cb8b0302e..e59e8d1ab1 100644 --- a/crates/multi_buffer/src/anchor.rs +++ b/crates/multi_buffer/src/anchor.rs @@ -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; diff --git a/crates/repl/src/session.rs b/crates/repl/src/session.rs index 82d0f915f8..c5b93c5d38 100644 --- a/crates/repl/src/session.rs +++ b/crates/repl/src/session.rs @@ -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,