Detailed changes
@@ -166,7 +166,7 @@ impl ProjectDiagnosticsEditor {
let excerpts = cx.new_model(|cx| MultiBuffer::new(project_handle.read(cx).capability()));
let editor = cx.new_view(|cx| {
let mut editor =
- Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), false, cx);
+ Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), true, cx);
editor.set_vertical_scroll_margin(5, cx);
editor
});
@@ -167,10 +167,10 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
- (DisplayRow(2), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(15), EXCERPT_HEADER.into()),
- (DisplayRow(16), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(25), EXCERPT_HEADER.into()),
+ (DisplayRow(3), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(16), EXCERPT_HEADER.into()),
+ (DisplayRow(18), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(27), EXCERPT_HEADER.into()),
]
);
assert_eq!(
@@ -184,6 +184,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
" let x = vec![];\n",
" let y = vec![];\n",
"\n", // supporting diagnostic
@@ -195,6 +196,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
" c(y);\n",
"\n", // supporting diagnostic
" d(x);\n",
+ "\n", // expand
"\n", // context ellipsis
// diagnostic group 2
"\n", // primary message
@@ -206,11 +208,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
" a(x);\n",
"\n", // supporting diagnostic
" b(y);\n",
+ "\n", // expand
"\n", // context ellipsis
" c(y);\n",
" d(x);\n",
"\n", // supporting diagnostic
- "}"
+ "}",
+ "\n", // expand
)
);
@@ -218,7 +222,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.display_ranges(cx),
- [DisplayPoint::new(DisplayRow(12), 6)..DisplayPoint::new(DisplayRow(12), 6)]
+ [DisplayPoint::new(DisplayRow(13), 6)..DisplayPoint::new(DisplayRow(13), 6)]
);
});
@@ -253,12 +257,12 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
- (DisplayRow(2), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(7), FILE_HEADER.into()),
- (DisplayRow(9), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(22), EXCERPT_HEADER.into()),
- (DisplayRow(23), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(32), EXCERPT_HEADER.into()),
+ (DisplayRow(3), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(8), FILE_HEADER.into()),
+ (DisplayRow(12), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(25), EXCERPT_HEADER.into()),
+ (DisplayRow(27), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(36), EXCERPT_HEADER.into()),
]
);
@@ -273,6 +277,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
"const a: i32 = 'a';\n",
"\n", // supporting diagnostic
"const b: i32 = c;\n",
@@ -284,6 +289,8 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
+ "\n", // expand
" let x = vec![];\n",
" let y = vec![];\n",
"\n", // supporting diagnostic
@@ -299,6 +306,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 2
"\n", // primary message
"\n", // filename
+ "\n", // expand
"fn main() {\n",
" let x = vec![];\n",
"\n", // supporting diagnostic
@@ -306,11 +314,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
" a(x);\n",
"\n", // supporting diagnostic
" b(y);\n",
+ "\n", // expand
"\n", // context ellipsis
" c(y);\n",
" d(x);\n",
"\n", // supporting diagnostic
- "}"
+ "}",
+ "\n", // expand
)
);
@@ -318,7 +328,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor.update(cx, |editor, cx| {
assert_eq!(
editor.selections.display_ranges(cx),
- [DisplayPoint::new(DisplayRow(19), 6)..DisplayPoint::new(DisplayRow(19), 6)]
+ [DisplayPoint::new(DisplayRow(22), 6)..DisplayPoint::new(DisplayRow(22), 6)]
);
});
@@ -366,14 +376,14 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
- (DisplayRow(2), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(7), EXCERPT_HEADER.into()),
- (DisplayRow(8), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(13), FILE_HEADER.into()),
- (DisplayRow(15), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(28), EXCERPT_HEADER.into()),
- (DisplayRow(29), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(38), EXCERPT_HEADER.into()),
+ (DisplayRow(3), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(8), EXCERPT_HEADER.into()),
+ (DisplayRow(10), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(15), FILE_HEADER.into()),
+ (DisplayRow(19), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(32), EXCERPT_HEADER.into()),
+ (DisplayRow(34), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(43), EXCERPT_HEADER.into()),
]
);
@@ -388,6 +398,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
"const a: i32 = 'a';\n",
"\n", // supporting diagnostic
"const b: i32 = c;\n",
@@ -395,6 +406,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 2
"\n", // primary message
"\n", // padding
+ "\n", // expand
"const a: i32 = 'a';\n",
"const b: i32 = c;\n",
"\n", // supporting diagnostic
@@ -406,6 +418,8 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
+ "\n", // expand
" let x = vec![];\n",
" let y = vec![];\n",
"\n", // supporting diagnostic
@@ -421,6 +435,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
// diagnostic group 2
"\n", // primary message
"\n", // filename
+ "\n", // expand
"fn main() {\n",
" let x = vec![];\n",
"\n", // supporting diagnostic
@@ -428,11 +443,13 @@ async fn test_diagnostics(cx: &mut TestAppContext) {
" a(x);\n",
"\n", // supporting diagnostic
" b(y);\n",
+ "\n", // expand
"\n", // context ellipsis
" c(y);\n",
" d(x);\n",
"\n", // supporting diagnostic
- "}"
+ "}",
+ "\n", // expand
)
);
}
@@ -513,7 +530,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
- (DisplayRow(2), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(3), DIAGNOSTIC_HEADER.into()),
]
);
assert_eq!(
@@ -524,8 +541,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
"a();\n", //
- "b();",
+ "b();", "\n", // expand
)
);
@@ -561,9 +579,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
- (DisplayRow(2), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(6), EXCERPT_HEADER.into()),
- (DisplayRow(7), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(3), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(7), EXCERPT_HEADER.into()),
+ (DisplayRow(9), DIAGNOSTIC_HEADER.into()),
]
);
assert_eq!(
@@ -574,8 +592,10 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
"a();\n", // location
"b();\n", //
+ "\n", // expand
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
@@ -583,6 +603,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
"a();\n", // context
"b();\n", //
"c();", // context
+ "\n", // expand
)
);
@@ -629,9 +650,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
- (DisplayRow(2), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(7), EXCERPT_HEADER.into()),
- (DisplayRow(8), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(3), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(8), EXCERPT_HEADER.into()),
+ (DisplayRow(10), DIAGNOSTIC_HEADER.into()),
]
);
assert_eq!(
@@ -642,9 +663,11 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
"a();\n", // location
"b();\n", //
"c();\n", // context
+ "\n", // expand
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
@@ -652,6 +675,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
"b();\n", // context
"c();\n", //
"d();", // context
+ "\n", // expand
)
);
@@ -687,9 +711,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
editor_blocks(&editor, cx),
[
(DisplayRow(0), FILE_HEADER.into()),
- (DisplayRow(2), DIAGNOSTIC_HEADER.into()),
- (DisplayRow(7), EXCERPT_HEADER.into()),
- (DisplayRow(8), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(3), DIAGNOSTIC_HEADER.into()),
+ (DisplayRow(8), EXCERPT_HEADER.into()),
+ (DisplayRow(10), DIAGNOSTIC_HEADER.into()),
]
);
assert_eq!(
@@ -700,9 +724,11 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
// diagnostic group 1
"\n", // primary message
"\n", // padding
+ "\n", // expand
"b();\n", // location
"c();\n", //
"d();\n", // context
+ "\n", // expand
"\n", // collapsed context
// diagnostic group 2
"\n", // primary message
@@ -710,6 +736,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
"c();\n", // context
"d();\n", //
"e();", // context
+ "\n", // expand
)
);
}
@@ -32,6 +32,7 @@ use crate::{
pub use block_map::{
Block, BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap,
BlockPlacement, BlockPoint, BlockProperties, BlockStyle, CustomBlockId, RenderBlock,
+ StickyHeaderExcerpt,
};
use block_map::{BlockRow, BlockSnapshot};
use collections::{HashMap, HashSet};
@@ -1105,6 +1106,10 @@ impl DisplaySnapshot {
.map(|(row, block)| (DisplayRow(row), block))
}
+ pub fn sticky_header_excerpt(&self, row: DisplayRow) -> Option<StickyHeaderExcerpt<'_>> {
+ self.block_snapshot.sticky_header_excerpt(row.0)
+ }
+
pub fn block_for_id(&self, id: BlockId) -> Option<Block> {
self.block_snapshot.block_for_id(id)
}
@@ -1411,6 +1411,66 @@ impl BlockSnapshot {
})
}
+ pub fn sticky_header_excerpt(&self, top_row: u32) -> Option<StickyHeaderExcerpt<'_>> {
+ let mut cursor = self.transforms.cursor::<BlockRow>(&());
+ cursor.seek(&BlockRow(top_row), Bias::Left, &());
+
+ while let Some(transform) = cursor.item() {
+ let start = cursor.start().0;
+ let end = cursor.end(&()).0;
+
+ match &transform.block {
+ Some(Block::ExcerptBoundary {
+ prev_excerpt,
+ next_excerpt,
+ starts_new_buffer,
+ show_excerpt_controls,
+ ..
+ }) => {
+ let matches_start = if *show_excerpt_controls && prev_excerpt.is_some() {
+ start < top_row
+ } else {
+ start <= top_row
+ };
+
+ if matches_start && top_row <= end {
+ return next_excerpt.as_ref().map(|excerpt| StickyHeaderExcerpt {
+ next_buffer_row: None,
+ next_excerpt_controls_present: *show_excerpt_controls,
+ excerpt,
+ });
+ }
+
+ let next_buffer_row = if *starts_new_buffer { Some(end) } else { None };
+
+ return prev_excerpt.as_ref().map(|excerpt| StickyHeaderExcerpt {
+ excerpt,
+ next_buffer_row,
+ next_excerpt_controls_present: *show_excerpt_controls,
+ });
+ }
+ Some(Block::FoldedBuffer {
+ prev_excerpt: Some(excerpt),
+ ..
+ }) if top_row <= start => {
+ return Some(StickyHeaderExcerpt {
+ next_buffer_row: Some(end),
+ next_excerpt_controls_present: false,
+ excerpt,
+ });
+ }
+ Some(Block::FoldedBuffer { .. }) | Some(Block::Custom(_)) | None => {}
+ }
+
+ // This is needed to iterate past None / FoldedBuffer / Custom blocks. For FoldedBuffer,
+ // if scrolled slightly past the header of a folded block, the next block is needed for
+ // the sticky header.
+ cursor.next(&());
+ }
+
+ None
+ }
+
pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
let buffer = self.wrap_snapshot.buffer_snapshot();
let wrap_point = match block_id {
@@ -1694,6 +1754,12 @@ impl<'a> BlockChunks<'a> {
}
}
+pub struct StickyHeaderExcerpt<'a> {
+ pub excerpt: &'a ExcerptInfo,
+ pub next_excerpt_controls_present: bool,
+ pub next_buffer_row: Option<u32>,
+}
+
impl<'a> Iterator for BlockChunks<'a> {
type Item = Chunk<'a>;
@@ -22,7 +22,7 @@ use crate::{
EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown,
HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, InlineCompletion, JumpData, LineDown,
LineUp, OpenExcerpts, PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection,
- SoftWrap, ToPoint, ToggleFold, CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT,
+ SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT,
GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
};
use client::ParticipantIndex;
@@ -30,14 +30,14 @@ use collections::{BTreeMap, HashMap, HashSet};
use file_icons::FileIcons;
use git::{blame::BlameEntry, diff::DiffHunkStatus, Oid};
use gpui::{
- anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
- transparent_black, Action, AnyElement, AvailableSpace, Axis, Bounds, ClickEvent, ClipboardItem,
- ContentMask, Corner, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler,
- Entity, FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Length,
- ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
- ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
- StatefulInteractiveElement, Style, Styled, Subscription, TextRun, TextStyleRefinement, View,
- ViewContext, WeakView, WindowContext,
+ anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, point, px, quad,
+ relative, size, svg, transparent_black, Action, AnyElement, AvailableSpace, Axis, Bounds,
+ ClickEvent, ClipboardItem, ContentMask, Corner, Corners, CursorStyle, DispatchPhase, Edges,
+ Element, ElementInputHandler, Entity, FontId, GlobalElementId, Hitbox, Hsla,
+ InteractiveElement, IntoElement, Length, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+ MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent,
+ ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled, Subscription,
+ TextRun, TextStyleRefinement, View, ViewContext, WeakView, WindowContext,
};
use itertools::Itertools;
use language::{
@@ -2210,9 +2210,9 @@ impl EditorElement {
resized_blocks: &mut HashMap<CustomBlockId, u32>,
selections: &[Selection<Point>],
is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
+ sticky_header_excerpt_id: Option<ExcerptId>,
cx: &mut WindowContext,
) -> (AnyElement, Size<Pixels>) {
- let header_padding = px(6.0);
let mut element = match block {
Block::Custom(block) => {
let block_start = block.start().to_point(&snapshot.buffer_snapshot);
@@ -2305,14 +2305,7 @@ impl EditorElement {
let jump_data = jump_data(snapshot, block_row_start, *height, first_excerpt, cx);
result
- .child(self.render_buffer_header(
- first_excerpt,
- header_padding,
- true,
- selected,
- jump_data,
- cx,
- ))
+ .child(self.render_buffer_header(first_excerpt, true, selected, jump_data, cx))
.into_any_element()
}
Block::ExcerptBoundary {
@@ -2347,14 +2340,19 @@ impl EditorElement {
if let Some(next_excerpt) = next_excerpt {
let jump_data = jump_data(snapshot, block_row_start, *height, next_excerpt, cx);
if *starts_new_buffer {
- result = result.child(self.render_buffer_header(
- next_excerpt,
- header_padding,
- false,
- false,
- jump_data,
- cx,
- ));
+ if sticky_header_excerpt_id != Some(next_excerpt.id) {
+ result = result.child(self.render_buffer_header(
+ next_excerpt,
+ false,
+ false,
+ jump_data,
+ cx,
+ ));
+ } else {
+ result =
+ result.child(div().h(FILE_HEADER_HEIGHT as f32 * cx.line_height()));
+ }
+
if *show_excerpt_controls {
result = result.child(
h_flex()
@@ -2507,7 +2505,6 @@ impl EditorElement {
fn render_buffer_header(
&self,
for_excerpt: &ExcerptInfo,
- header_padding: Pixels,
is_folded: bool,
is_selected: bool,
jump_data: JumpData,
@@ -2531,8 +2528,8 @@ impl EditorElement {
let focus_handle = self.editor.focus_handle(cx);
div()
- .px(header_padding)
- .pt(header_padding)
+ .px_2()
+ .pt_2()
.w_full()
.h(FILE_HEADER_HEIGHT as f32 * cx.line_height())
.child(
@@ -2686,6 +2683,7 @@ impl EditorElement {
line_layouts: &[LineWithInvisibles],
selections: &[Selection<Point>],
is_row_soft_wrapped: impl Copy + Fn(usize) -> bool,
+ sticky_header_excerpt_id: Option<ExcerptId>,
cx: &mut WindowContext,
) -> Result<Vec<BlockLayout>, HashMap<CustomBlockId, u32>> {
let (fixed_blocks, non_fixed_blocks) = snapshot
@@ -2724,6 +2722,7 @@ impl EditorElement {
&mut resized_blocks,
selections,
is_row_soft_wrapped,
+ sticky_header_excerpt_id,
cx,
);
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
@@ -2735,6 +2734,7 @@ impl EditorElement {
style: BlockStyle::Fixed,
});
}
+
for (row, block) in non_fixed_blocks {
let style = block.style();
let width = match style {
@@ -2770,6 +2770,7 @@ impl EditorElement {
&mut resized_blocks,
selections,
is_row_soft_wrapped,
+ sticky_header_excerpt_id,
cx,
);
@@ -2817,6 +2818,7 @@ impl EditorElement {
&mut resized_blocks,
selections,
is_row_soft_wrapped,
+ sticky_header_excerpt_id,
cx,
);
@@ -2883,6 +2885,71 @@ impl EditorElement {
}
}
+ fn layout_sticky_buffer_header(
+ &self,
+ StickyHeaderExcerpt {
+ excerpt,
+ next_excerpt_controls_present,
+ next_buffer_row,
+ }: StickyHeaderExcerpt<'_>,
+ scroll_position: f32,
+ line_height: Pixels,
+ snapshot: &EditorSnapshot,
+ hitbox: &Hitbox,
+ cx: &mut WindowContext,
+ ) -> AnyElement {
+ let jump_data = jump_data(snapshot, DisplayRow(0), FILE_HEADER_HEIGHT, excerpt, cx);
+
+ let editor_bg_color = cx.theme().colors().editor_background;
+
+ let mut header = v_flex()
+ .relative()
+ .child(
+ div()
+ .w(hitbox.bounds.size.width)
+ .h(FILE_HEADER_HEIGHT as f32 * line_height)
+ .bg(linear_gradient(
+ 0.,
+ linear_color_stop(editor_bg_color.opacity(0.), 0.),
+ linear_color_stop(editor_bg_color, 0.6),
+ ))
+ .absolute()
+ .top_0(),
+ )
+ .child(
+ self.render_buffer_header(excerpt, false, false, jump_data, cx)
+ .into_any_element(),
+ )
+ .into_any_element();
+
+ let mut origin = hitbox.origin;
+
+ if let Some(next_buffer_row) = next_buffer_row {
+ // Push up the sticky header when the excerpt is getting close to the top of the viewport
+
+ let mut max_row = next_buffer_row - FILE_HEADER_HEIGHT * 2;
+
+ if next_excerpt_controls_present {
+ max_row -= MULTI_BUFFER_EXCERPT_HEADER_HEIGHT;
+ }
+
+ let offset = scroll_position - max_row as f32;
+
+ if offset > 0.0 {
+ origin.y -= Pixels(offset) * line_height;
+ }
+ }
+
+ let size = size(
+ AvailableSpace::Definite(hitbox.size.width),
+ AvailableSpace::MinContent,
+ );
+
+ header.prepaint_as_root(origin, size, cx);
+
+ header
+ }
+
#[allow(clippy::too_many_arguments)]
fn layout_context_menu(
&self,
@@ -4945,11 +5012,14 @@ fn jump_data(
let excerpt_start_row = language::ToPoint::to_point(&jump_anchor, buffer).row;
jump_position.row - excerpt_start_row
};
- let line_offset_from_top = block_row_start.0 + height + offset_from_excerpt_start
- - snapshot
- .scroll_anchor
- .scroll_position(&snapshot.display_snapshot)
- .y as u32;
+ let line_offset_from_top = block_row_start.0
+ + height
+ + offset_from_excerpt_start.saturating_sub(
+ snapshot
+ .scroll_anchor
+ .scroll_position(&snapshot.display_snapshot)
+ .y as u32,
+ );
JumpData {
excerpt_id: for_excerpt.id,
anchor: jump_anchor,
@@ -6096,6 +6166,14 @@ impl Element for EditorElement {
let scroll_range_bounds = scrollbar_range_data.scroll_range;
let mut scroll_width = scroll_range_bounds.size.width;
+ let sticky_header_excerpt = if snapshot.buffer_snapshot.show_headers() {
+ snapshot.sticky_header_excerpt(start_row)
+ } else {
+ None
+ };
+ let sticky_header_excerpt_id =
+ sticky_header_excerpt.as_ref().map(|top| top.excerpt.id);
+
let blocks = cx.with_element_namespace("blocks", |cx| {
self.render_blocks(
start_row..end_row,
@@ -6111,6 +6189,7 @@ impl Element for EditorElement {
&line_layouts,
&local_selections,
is_row_soft_wrapped,
+ sticky_header_excerpt_id,
cx,
)
});
@@ -6124,6 +6203,19 @@ impl Element for EditorElement {
}
};
+ let sticky_buffer_header = sticky_header_excerpt.map(|sticky_header_excerpt| {
+ cx.with_element_namespace("blocks", |cx| {
+ self.layout_sticky_buffer_header(
+ sticky_header_excerpt,
+ scroll_position.y,
+ line_height,
+ &snapshot,
+ &hitbox,
+ cx,
+ )
+ })
+ });
+
let start_buffer_row =
MultiBufferRow(start_anchor.to_point(&snapshot.buffer_snapshot).row);
let end_buffer_row =
@@ -6251,6 +6343,7 @@ impl Element for EditorElement {
);
let mut block_start_rows = HashSet::default();
+
cx.with_element_namespace("blocks", |cx| {
self.layout_blocks(
&mut blocks,
@@ -6542,6 +6635,7 @@ impl Element for EditorElement {
crease_trailers,
tab_invisible,
space_invisible,
+ sticky_buffer_header,
}
})
})
@@ -6623,6 +6717,12 @@ impl Element for EditorElement {
});
}
+ cx.with_element_namespace("blocks", |cx| {
+ if let Some(mut sticky_header) = layout.sticky_buffer_header.take() {
+ sticky_header.paint(cx)
+ }
+ });
+
self.paint_scrollbars(layout, cx);
self.paint_inline_completion_popover(layout, cx);
self.paint_mouse_context_menu(layout, cx);
@@ -6730,6 +6830,7 @@ pub struct EditorLayout {
mouse_context_menu: Option<AnyElement>,
tab_invisible: ShapedLine,
space_invisible: ShapedLine,
+ sticky_buffer_header: Option<AnyElement>,
}
impl EditorLayout {