diff --git a/assets/icons/expand_down.svg b/assets/icons/expand_down.svg new file mode 100644 index 0000000000..a17b9e285c --- /dev/null +++ b/assets/icons/expand_down.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/expand_up.svg b/assets/icons/expand_up.svg new file mode 100644 index 0000000000..30f9af92e3 --- /dev/null +++ b/assets/icons/expand_up.svg @@ -0,0 +1,4 @@ + + + + diff --git a/crates/assistant/src/inline_assistant.rs b/crates/assistant/src/inline_assistant.rs index 7bf10d4a67..fb7343c18b 100644 --- a/crates/assistant/src/inline_assistant.rs +++ b/crates/assistant/src/inline_assistant.rs @@ -1246,7 +1246,7 @@ impl InlineAssistant { }); enum DeletedLines {} - let mut editor = Editor::for_multibuffer(multi_buffer, None, true, window, cx); + let mut editor = Editor::for_multibuffer(multi_buffer, None, window, cx); editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx); editor.set_show_wrap_guides(false, cx); editor.set_show_gutter(false, cx); @@ -1693,7 +1693,6 @@ impl PromptEditor { }, prompt_buffer, None, - false, window, cx, ); diff --git a/crates/assistant/src/terminal_inline_assistant.rs b/crates/assistant/src/terminal_inline_assistant.rs index ba9a73b58e..1acbd5f414 100644 --- a/crates/assistant/src/terminal_inline_assistant.rs +++ b/crates/assistant/src/terminal_inline_assistant.rs @@ -720,7 +720,6 @@ impl PromptEditor { }, prompt_buffer, None, - false, window, cx, ); diff --git a/crates/assistant2/src/active_thread.rs b/crates/assistant2/src/active_thread.rs index 2d9dbf5364..66b8f1f129 100644 --- a/crates/assistant2/src/active_thread.rs +++ b/crates/assistant2/src/active_thread.rs @@ -396,7 +396,6 @@ impl ActiveThread { editor::EditorMode::AutoHeight { max_lines: 8 }, buffer, None, - false, window, cx, ); diff --git a/crates/assistant2/src/assistant_panel.rs b/crates/assistant2/src/assistant_panel.rs index 266ccb96ff..486931a9c3 100644 --- a/crates/assistant2/src/assistant_panel.rs +++ b/crates/assistant2/src/assistant_panel.rs @@ -456,13 +456,8 @@ impl AssistantPanel { workspace.add_item_to_active_pane( Box::new(cx.new(|cx| { - let mut editor = Editor::for_multibuffer( - buffer, - Some(project.clone()), - true, - window, - cx, - ); + let mut editor = + Editor::for_multibuffer(buffer, Some(project.clone()), window, cx); editor.set_breadcrumb_header(thread_summary); editor })), diff --git a/crates/assistant2/src/inline_assistant.rs b/crates/assistant2/src/inline_assistant.rs index 74b8e49b8b..e76834da73 100644 --- a/crates/assistant2/src/inline_assistant.rs +++ b/crates/assistant2/src/inline_assistant.rs @@ -1341,7 +1341,7 @@ impl InlineAssistant { }); enum DeletedLines {} - let mut editor = Editor::for_multibuffer(multi_buffer, None, true, window, cx); + let mut editor = Editor::for_multibuffer(multi_buffer, None, window, cx); editor.set_soft_wrap_mode(language::language_settings::SoftWrap::None, cx); editor.set_show_wrap_guides(false, cx); editor.set_show_gutter(false, cx); diff --git a/crates/assistant2/src/inline_prompt_editor.rs b/crates/assistant2/src/inline_prompt_editor.rs index d11a02be01..a2256574f2 100644 --- a/crates/assistant2/src/inline_prompt_editor.rs +++ b/crates/assistant2/src/inline_prompt_editor.rs @@ -843,7 +843,6 @@ impl PromptEditor { }, prompt_buffer, None, - false, window, cx, ); @@ -1001,7 +1000,6 @@ impl PromptEditor { }, prompt_buffer, None, - false, window, cx, ); diff --git a/crates/auto_update_ui/src/auto_update_ui.rs b/crates/auto_update_ui/src/auto_update_ui.rs index adf55f1773..0ef89da85e 100644 --- a/crates/auto_update_ui/src/auto_update_ui.rs +++ b/crates/auto_update_ui/src/auto_update_ui.rs @@ -93,7 +93,7 @@ fn view_release_notes_locally( let tab_description = SharedString::from(body.title.to_string()); let editor = cx.new(|cx| { - Editor::for_multibuffer(buffer, Some(project), true, window, cx) + Editor::for_multibuffer(buffer, Some(project), window, cx) }); let workspace_handle = workspace.weak_handle(); let markdown_preview: Entity = diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index dea74f80a2..fa5caf0116 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -313,9 +313,8 @@ async fn test_basic_following( result }); let multibuffer_editor_a = workspace_a.update_in(cx_a, |workspace, window, cx| { - let editor = cx.new(|cx| { - Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), true, window, cx) - }); + let editor = cx + .new(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), window, cx)); workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx); editor }); diff --git a/crates/copilot/src/copilot_completion_provider.rs b/crates/copilot/src/copilot_completion_provider.rs index 00e00f7670..95640f8527 100644 --- a/crates/copilot/src/copilot_completion_provider.rs +++ b/crates/copilot/src/copilot_completion_provider.rs @@ -745,8 +745,8 @@ mod tests { ); multibuffer }); - let editor = cx - .add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, true, window, cx)); + let editor = + cx.add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, window, cx)); editor .update(cx, |editor, window, cx| { use gpui::Focusable; @@ -781,7 +781,7 @@ mod tests { assert!(editor.has_active_inline_completion()); assert_eq!( editor.display_text(cx), - "\n\n\na = 1\nb = 2 + a\n\n\n\n\n\nc = 3\nd = 4\n\n" + "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n" ); assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n"); }); @@ -803,7 +803,7 @@ mod tests { assert!(!editor.has_active_inline_completion()); assert_eq!( editor.display_text(cx), - "\n\n\na = 1\nb = 2\n\n\n\n\n\nc = 3\nd = 4\n\n" + "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n" ); assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n"); @@ -812,7 +812,7 @@ mod tests { assert!(!editor.has_active_inline_completion()); assert_eq!( editor.display_text(cx), - "\n\n\na = 1\nb = 2\n\n\n\n\n\nc = 3\nd = 4 \n\n" + "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n" ); assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n"); }); @@ -823,7 +823,7 @@ mod tests { assert!(editor.has_active_inline_completion()); assert_eq!( editor.display_text(cx), - "\n\n\na = 1\nb = 2\n\n\n\n\n\nc = 3\nd = 4 + c\n\n" + "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n" ); assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n"); }); @@ -997,8 +997,8 @@ mod tests { ); multibuffer }); - let editor = cx - .add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, true, window, cx)); + let editor = + cx.add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, window, cx)); editor .update(cx, |editor, window, cx| { use gpui::Focusable; diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 169946ce0e..99b2dc91a1 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -198,13 +198,8 @@ impl ProjectDiagnosticsEditor { let excerpts = cx.new(|cx| MultiBuffer::new(project_handle.read(cx).capability())); let editor = cx.new(|cx| { - let mut editor = Editor::for_multibuffer( - excerpts.clone(), - Some(project_handle.clone()), - true, - window, - cx, - ); + let mut editor = + Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), window, cx); editor.set_vertical_scroll_margin(5, cx); editor.disable_inline_diagnostics(); editor diff --git a/crates/diagnostics/src/diagnostics_tests.rs b/crates/diagnostics/src/diagnostics_tests.rs index 550aea56a3..e6c1132154 100644 --- a/crates/diagnostics/src/diagnostics_tests.rs +++ b/crates/diagnostics/src/diagnostics_tests.rs @@ -169,10 +169,10 @@ async fn test_diagnostics(cx: &mut TestAppContext) { editor_blocks(&editor, cx), [ (DisplayRow(0), FILE_HEADER.into()), - (DisplayRow(3), DIAGNOSTIC_HEADER.into()), - (DisplayRow(16), EXCERPT_HEADER.into()), - (DisplayRow(18), DIAGNOSTIC_HEADER.into()), - (DisplayRow(27), EXCERPT_HEADER.into()), + (DisplayRow(2), DIAGNOSTIC_HEADER.into()), + (DisplayRow(15), EXCERPT_HEADER.into()), + (DisplayRow(16), DIAGNOSTIC_HEADER.into()), + (DisplayRow(25), EXCERPT_HEADER.into()), ] ); assert_eq!( @@ -186,7 +186,6 @@ 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 @@ -198,7 +197,6 @@ 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 @@ -210,13 +208,11 @@ 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 ) ); @@ -224,7 +220,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) { editor.update(cx, |editor, cx| { assert_eq!( editor.selections.display_ranges(cx), - [DisplayPoint::new(DisplayRow(13), 6)..DisplayPoint::new(DisplayRow(13), 6)] + [DisplayPoint::new(DisplayRow(12), 6)..DisplayPoint::new(DisplayRow(12), 6)] ); }); @@ -260,12 +256,12 @@ async fn test_diagnostics(cx: &mut TestAppContext) { editor_blocks(&editor, cx), [ (DisplayRow(0), FILE_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()), + (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()), ] ); @@ -280,7 +276,6 @@ 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", @@ -292,8 +287,6 @@ 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 @@ -309,7 +302,6 @@ 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 @@ -317,13 +309,11 @@ 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 ) ); @@ -331,7 +321,7 @@ async fn test_diagnostics(cx: &mut TestAppContext) { editor.update(cx, |editor, cx| { assert_eq!( editor.selections.display_ranges(cx), - [DisplayPoint::new(DisplayRow(22), 6)..DisplayPoint::new(DisplayRow(22), 6)] + [DisplayPoint::new(DisplayRow(19), 6)..DisplayPoint::new(DisplayRow(19), 6)] ); }); @@ -380,14 +370,14 @@ async fn test_diagnostics(cx: &mut TestAppContext) { editor_blocks(&editor, cx), [ (DisplayRow(0), FILE_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()), + (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()), ] ); @@ -402,7 +392,6 @@ 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", @@ -410,7 +399,6 @@ 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 @@ -422,8 +410,6 @@ 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 @@ -439,7 +425,6 @@ 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 @@ -447,13 +432,11 @@ 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 ) ); } @@ -535,7 +518,7 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { editor_blocks(&editor, cx), [ (DisplayRow(0), FILE_HEADER.into()), - (DisplayRow(3), DIAGNOSTIC_HEADER.into()), + (DisplayRow(2), DIAGNOSTIC_HEADER.into()), ] ); assert_eq!( @@ -546,9 +529,8 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { // diagnostic group 1 "\n", // primary message "\n", // padding - "\n", // expand "a();\n", // - "b();", "\n", // expand + "b();", ) ); @@ -584,9 +566,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { editor_blocks(&editor, cx), [ (DisplayRow(0), FILE_HEADER.into()), - (DisplayRow(3), DIAGNOSTIC_HEADER.into()), - (DisplayRow(7), EXCERPT_HEADER.into()), - (DisplayRow(9), DIAGNOSTIC_HEADER.into()), + (DisplayRow(2), DIAGNOSTIC_HEADER.into()), + (DisplayRow(6), EXCERPT_HEADER.into()), + (DisplayRow(7), DIAGNOSTIC_HEADER.into()), ] ); assert_eq!( @@ -597,10 +579,8 @@ 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 @@ -608,7 +588,6 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { "a();\n", // context "b();\n", // "c();", // context - "\n", // expand ) ); @@ -655,9 +634,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { editor_blocks(&editor, cx), [ (DisplayRow(0), FILE_HEADER.into()), - (DisplayRow(3), DIAGNOSTIC_HEADER.into()), - (DisplayRow(8), EXCERPT_HEADER.into()), - (DisplayRow(10), DIAGNOSTIC_HEADER.into()), + (DisplayRow(2), DIAGNOSTIC_HEADER.into()), + (DisplayRow(7), EXCERPT_HEADER.into()), + (DisplayRow(8), DIAGNOSTIC_HEADER.into()), ] ); assert_eq!( @@ -668,11 +647,9 @@ 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 @@ -680,7 +657,6 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { "b();\n", // context "c();\n", // "d();", // context - "\n", // expand ) ); @@ -716,9 +692,9 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { editor_blocks(&editor, cx), [ (DisplayRow(0), FILE_HEADER.into()), - (DisplayRow(3), DIAGNOSTIC_HEADER.into()), - (DisplayRow(8), EXCERPT_HEADER.into()), - (DisplayRow(10), DIAGNOSTIC_HEADER.into()), + (DisplayRow(2), DIAGNOSTIC_HEADER.into()), + (DisplayRow(7), EXCERPT_HEADER.into()), + (DisplayRow(8), DIAGNOSTIC_HEADER.into()), ] ); assert_eq!( @@ -729,11 +705,9 @@ 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 @@ -741,7 +715,6 @@ async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) { "c();\n", // context "d();\n", // "e();", // context - "\n", // expand ) ); } diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 11615f2f0a..04ec72206a 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -118,10 +118,8 @@ impl DisplayMap { font: Font, font_size: Pixels, wrap_width: Option, - show_excerpt_controls: bool, buffer_header_height: u32, excerpt_header_height: u32, - excerpt_footer_height: u32, fold_placeholder: FoldPlaceholder, cx: &mut Context, ) -> Self { @@ -134,13 +132,7 @@ impl DisplayMap { let (fold_map, snapshot) = FoldMap::new(snapshot); let (tab_map, snapshot) = TabMap::new(snapshot, tab_size); let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx); - let block_map = BlockMap::new( - snapshot, - show_excerpt_controls, - buffer_header_height, - excerpt_header_height, - excerpt_footer_height, - ); + let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height); cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach(); @@ -555,10 +547,6 @@ impl DisplayMap { pub fn is_rewrapping(&self, cx: &gpui::App) -> bool { self.wrap_map.read(cx).is_rewrapping() } - - pub fn show_excerpt_controls(&self) -> bool { - self.block_map.show_excerpt_controls() - } } #[derive(Debug, Default)] @@ -1102,8 +1090,8 @@ impl DisplaySnapshot { .map(|(row, block)| (DisplayRow(row), block)) } - pub fn sticky_header_excerpt(&self, row: DisplayRow) -> Option> { - self.block_snapshot.sticky_header_excerpt(row.0) + pub fn sticky_header_excerpt(&self, row: f32) -> Option> { + self.block_snapshot.sticky_header_excerpt(row) } pub fn block_for_id(&self, id: BlockId) -> Option { @@ -1301,10 +1289,6 @@ impl DisplaySnapshot { self.block_snapshot.buffer_header_height } - pub fn excerpt_footer_height(&self) -> u32 { - self.block_snapshot.excerpt_footer_height - } - pub fn excerpt_header_height(&self) -> u32 { self.block_snapshot.excerpt_header_height } @@ -1514,10 +1498,8 @@ pub mod tests { font, font_size, wrap_width, - true, buffer_start_excerpt_header_height, excerpt_header_height, - 0, FoldPlaceholder::test(), cx, ) @@ -1764,10 +1746,8 @@ pub mod tests { font("Helvetica"), font_size, wrap_width, - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ) @@ -1875,10 +1855,8 @@ pub mod tests { font("Helvetica"), font_size, None, - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ) @@ -1938,8 +1916,6 @@ pub mod tests { font("Helvetica"), font_size, None, - true, - 1, 1, 1, FoldPlaceholder::test(), @@ -2032,8 +2008,6 @@ pub mod tests { font("Helvetica"), font_size, None, - true, - 1, 1, 1, FoldPlaceholder::test(), @@ -2134,10 +2108,8 @@ pub mod tests { font("Courier"), px(16.0), None, - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ) @@ -2239,10 +2211,8 @@ pub mod tests { font("Courier"), px(16.0), None, - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ) @@ -2328,10 +2298,8 @@ pub mod tests { font("Courier"), px(16.0), None, - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ) @@ -2472,10 +2440,8 @@ pub mod tests { font("Courier"), font_size, Some(px(40.0)), - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ) @@ -2556,8 +2522,6 @@ pub mod tests { font("Courier"), font_size, None, - true, - 1, 1, 1, FoldPlaceholder::test(), @@ -2682,10 +2646,8 @@ pub mod tests { font("Helvetica"), font_size, None, - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ); @@ -2721,10 +2683,8 @@ pub mod tests { font("Helvetica"), font_size, None, - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ) @@ -2798,10 +2758,8 @@ pub mod tests { font("Helvetica"), font_size, None, - true, 1, 1, - 0, FoldPlaceholder::test(), cx, ) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index ab8b87b0ad..69e6df8666 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -37,10 +37,8 @@ pub struct BlockMap { custom_blocks: Vec>, custom_blocks_by_id: TreeMap>, transforms: RefCell>, - show_excerpt_controls: bool, buffer_header_height: u32, excerpt_header_height: u32, - excerpt_footer_height: u32, pub(super) folded_buffers: HashSet, } @@ -58,7 +56,6 @@ pub struct BlockSnapshot { custom_blocks_by_id: TreeMap>, pub(super) buffer_header_height: u32, pub(super) excerpt_header_height: u32, - pub(super) excerpt_footer_height: u32, } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -285,14 +282,12 @@ pub enum Block { first_excerpt: ExcerptInfo, prev_excerpt: Option, height: u32, - show_excerpt_controls: bool, }, ExcerptBoundary { prev_excerpt: Option, next_excerpt: Option, height: u32, starts_new_buffer: bool, - show_excerpt_controls: bool, }, } @@ -362,13 +357,11 @@ impl Debug for Block { first_excerpt, prev_excerpt, height, - show_excerpt_controls, } => f .debug_struct("FoldedBuffer") .field("first_excerpt", &first_excerpt) .field("prev_excerpt", prev_excerpt) .field("height", height) - .field("show_excerpt_controls", show_excerpt_controls) .finish(), Self::ExcerptBoundary { starts_new_buffer, @@ -413,10 +406,8 @@ pub struct BlockRows<'a> { impl BlockMap { pub fn new( wrap_snapshot: WrapSnapshot, - show_excerpt_controls: bool, buffer_header_height: u32, excerpt_header_height: u32, - excerpt_footer_height: u32, ) -> Self { let row_count = wrap_snapshot.max_point().row() + 1; let mut transforms = SumTree::default(); @@ -428,10 +419,8 @@ impl BlockMap { folded_buffers: HashSet::default(), transforms: RefCell::new(transforms), wrap_snapshot: RefCell::new(wrap_snapshot.clone()), - show_excerpt_controls, buffer_header_height, excerpt_header_height, - excerpt_footer_height, }; map.sync( &wrap_snapshot, @@ -454,7 +443,6 @@ impl BlockMap { custom_blocks_by_id: self.custom_blocks_by_id.clone(), buffer_header_height: self.buffer_header_height, excerpt_header_height: self.excerpt_header_height, - excerpt_footer_height: self.excerpt_footer_height, }, } } @@ -650,8 +638,6 @@ impl BlockMap { if buffer.show_headers() { blocks_in_edit.extend(BlockMap::header_and_footer_blocks( - self.show_excerpt_controls, - self.excerpt_footer_height, self.buffer_header_height, self.excerpt_header_height, buffer, @@ -722,13 +708,7 @@ impl BlockMap { } } - pub fn show_excerpt_controls(&self) -> bool { - self.show_excerpt_controls - } - fn header_and_footer_blocks<'a, R, T>( - show_excerpt_controls: bool, - excerpt_footer_height: u32, buffer_header_height: u32, excerpt_header_height: u32, buffer: &'a multi_buffer::MultiBufferSnapshot, @@ -774,11 +754,6 @@ impl BlockMap { .filter(|prev| !folded_buffers.contains(&prev.buffer_id)); let mut height = 0; - if prev_excerpt.is_some() { - if show_excerpt_controls { - height += excerpt_footer_height; - } - } if let Some(new_buffer_id) = new_buffer_id { let first_excerpt = excerpt_boundary.next.clone().unwrap(); @@ -812,7 +787,6 @@ impl BlockMap { Block::FoldedBuffer { prev_excerpt, height: height + buffer_header_height, - show_excerpt_controls, first_excerpt, }, )); @@ -822,9 +796,6 @@ impl BlockMap { if excerpt_boundary.next.is_some() { if new_buffer_id.is_some() { height += buffer_header_height; - if show_excerpt_controls { - height += excerpt_header_height; - } } else { height += excerpt_header_height; } @@ -845,7 +816,6 @@ impl BlockMap { next_excerpt: excerpt_boundary.next, height, starts_new_buffer: new_buffer_id.is_some(), - show_excerpt_controls, }, )) }) @@ -1432,7 +1402,8 @@ impl BlockSnapshot { }) } - pub fn sticky_header_excerpt(&self, top_row: u32) -> Option> { + pub fn sticky_header_excerpt(&self, position: f32) -> Option> { + let top_row = position as u32; let mut cursor = self.transforms.cursor::(&()); cursor.seek(&BlockRow(top_row), Bias::Left, &()); @@ -1445,19 +1416,13 @@ impl BlockSnapshot { 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 - }; + let matches_start = (start as f32) < position; 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, }); } @@ -1467,7 +1432,6 @@ impl BlockSnapshot { return prev_excerpt.as_ref().map(|excerpt| StickyHeaderExcerpt { excerpt, next_buffer_row, - next_excerpt_controls_present: *show_excerpt_controls, }); } Some(Block::FoldedBuffer { @@ -1476,7 +1440,6 @@ impl BlockSnapshot { }) if top_row <= start => { return Some(StickyHeaderExcerpt { next_buffer_row: Some(end), - next_excerpt_controls_present: false, excerpt, }); } @@ -1785,7 +1748,6 @@ impl BlockChunks<'_> { pub struct StickyHeaderExcerpt<'a> { pub excerpt: &'a ExcerptInfo, - pub next_excerpt_controls_present: bool, pub next_buffer_row: Option, } @@ -2066,7 +2028,7 @@ mod tests { let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap()); let (wrap_map, wraps_snapshot) = cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx)); - let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1); + let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1); let mut writer = block_map.write(wraps_snapshot.clone(), Default::default()); let block_ids = writer.insert(vec![ @@ -2279,14 +2241,11 @@ mod tests { let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap()); let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx); - let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1); + let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1); let snapshot = block_map.read(wraps_snapshot, Default::default()); // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline. - assert_eq!( - snapshot.text(), - "\n\nBuff\ner 1\n\n\n\nBuff\ner 2\n\n\n\nBuff\ner 3\n" - ); + assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3"); let blocks: Vec<_> = snapshot .blocks_in_range(0..u32::MAX) @@ -2295,10 +2254,9 @@ mod tests { assert_eq!( blocks, vec![ - (0..2, BlockId::ExcerptBoundary(Some(excerpt_ids[0]))), // path, header - (4..7, BlockId::ExcerptBoundary(Some(excerpt_ids[1]))), // footer, path, header - (9..12, BlockId::ExcerptBoundary(Some(excerpt_ids[2]))), // footer, path, header - (14..15, BlockId::ExcerptBoundary(None)), // footer + (0..1, BlockId::ExcerptBoundary(Some(excerpt_ids[0]))), // path, header + (3..4, BlockId::ExcerptBoundary(Some(excerpt_ids[1]))), // path, header + (6..7, BlockId::ExcerptBoundary(Some(excerpt_ids[2]))), // path, header ] ); } @@ -2317,7 +2275,7 @@ mod tests { let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap()); let (_wrap_map, wraps_snapshot) = cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx)); - let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0); + let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1); let mut writer = block_map.write(wraps_snapshot.clone(), Default::default()); let block_ids = writer.insert(vec![ @@ -2420,7 +2378,7 @@ mod tests { let (_, wraps_snapshot) = cx.update(|cx| { WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx) }); - let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0); + let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1); let mut writer = block_map.write(wraps_snapshot.clone(), Default::default()); writer.insert(vec![ @@ -2464,7 +2422,7 @@ mod tests { let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size); let (wrap_map, wraps_snapshot) = cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx)); - let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0); + let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1); let mut writer = block_map.write(wraps_snapshot.clone(), Default::default()); let replace_block_id = writer.insert(vec![BlockProperties { @@ -2631,12 +2589,12 @@ mod tests { let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap()); let (_, wrap_snapshot) = cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx)); - let mut block_map = BlockMap::new(wrap_snapshot.clone(), true, 2, 1, 1); + let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1); let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default()); assert_eq!( blocks_snapshot.text(), - "\n\n\n111\n\n\n\n\n222\n\n\n333\n\n\n444\n\n\n\n\n555\n\n\n666\n" + "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666" ); assert_eq!( blocks_snapshot @@ -2644,30 +2602,21 @@ mod tests { .map(|i| i.buffer_row) .collect::>(), vec![ - None, None, None, Some(0), None, None, - None, - None, Some(1), None, - None, Some(2), None, - None, Some(3), None, None, - None, - None, Some(4), None, - None, Some(5), - None, ] ); @@ -2715,7 +2664,7 @@ mod tests { let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default()); assert_eq!( blocks_snapshot.text(), - "\n\n\n111\n\n\n\n\n\n222\n\n\n\n333\n\n\n444\n\n\n\n\n\n\n555\n\n\n666\n\n" + "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n" ); assert_eq!( blocks_snapshot @@ -2723,35 +2672,26 @@ mod tests { .map(|i| i.buffer_row) .collect::>(), vec![ - None, None, None, Some(0), None, None, None, - None, - None, Some(1), None, None, - None, Some(2), None, - None, Some(3), None, None, None, None, - None, - None, Some(4), None, - None, Some(5), None, - None, ] ); @@ -2793,7 +2733,7 @@ mod tests { ); assert_eq!( blocks_snapshot.text(), - "\n\n\n\n\n\n222\n\n\n\n333\n\n\n444\n\n\n\n\n\n\n555\n\n\n666\n\n" + "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n" ); assert_eq!( blocks_snapshot @@ -2806,27 +2746,20 @@ mod tests { None, None, None, - None, Some(1), None, None, - None, Some(2), None, - None, Some(3), None, None, None, None, - None, - None, Some(4), None, - None, Some(5), None, - None, ] ); @@ -2862,7 +2795,7 @@ mod tests { .count(), "Should have two folded blocks, producing headers" ); - assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n\n555\n\n\n666\n\n"); + assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n"); assert_eq!( blocks_snapshot .row_infos(BlockRow(0)) @@ -2876,13 +2809,10 @@ mod tests { None, None, None, - None, Some(4), None, - None, Some(5), None, - None, ] ); @@ -2917,7 +2847,7 @@ mod tests { ); assert_eq!( blocks_snapshot.text(), - "\n\n\n\n111\n\n\n\n\n\n\n\n555\n\n\n666\n\n", + "\n\n\n111\n\n\n\n\n\n555\n\n666\n", "Should have extra newline for 111 buffer, due to a new block added when it was folded" ); assert_eq!( @@ -2929,21 +2859,16 @@ mod tests { None, None, None, - None, Some(0), None, None, None, None, None, - None, - None, Some(4), None, - None, Some(5), None, - None, ] ); @@ -2974,7 +2899,7 @@ mod tests { assert_eq!( blocks_snapshot.text(), - "\n\n\n\n111\n\n\n\n\n", + "\n\n\n111\n\n\n\n", "Should have a single, first buffer left after folding" ); assert_eq!( @@ -2982,18 +2907,7 @@ mod tests { .row_infos(BlockRow(0)) .map(|i| i.buffer_row) .collect::>(), - vec![ - None, - None, - None, - None, - Some(0), - None, - None, - None, - None, - None, - ] + vec![None, None, None, Some(0), None, None, None, None,] ); } @@ -3020,10 +2934,10 @@ mod tests { let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap()); let (_, wrap_snapshot) = cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx)); - let mut block_map = BlockMap::new(wrap_snapshot.clone(), true, 2, 1, 1); + let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1); let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default()); - assert_eq!(blocks_snapshot.text(), "\n\n\n111\n"); + assert_eq!(blocks_snapshot.text(), "\n\n111"); let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default()); buffer.read_with(cx, |buffer, cx| { @@ -3077,11 +2991,9 @@ mod tests { let font_size = px(14.0); let buffer_start_header_height = rng.gen_range(1..=5); let excerpt_header_height = rng.gen_range(1..=5); - let excerpt_footer_height = rng.gen_range(1..=5); log::info!("Wrap width: {:?}", wrap_width); log::info!("Excerpt Header Height: {:?}", excerpt_header_height); - log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height); let is_singleton = rng.gen(); let buffer = if is_singleton { let len = rng.gen_range(0..10); @@ -3108,10 +3020,8 @@ mod tests { cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx)); let mut block_map = BlockMap::new( wraps_snapshot, - true, buffer_start_header_height, excerpt_header_height, - excerpt_footer_height, ); for _ in 0..operations { @@ -3329,8 +3239,6 @@ mod tests { // Note that this needs to be synced with the related section in BlockMap::sync expected_blocks.extend(BlockMap::header_and_footer_blocks( - true, - excerpt_footer_height, buffer_start_header_height, excerpt_header_height, &buffer_snapshot, diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index f52d5b70f3..8396c05b90 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -983,6 +983,7 @@ impl Iterator for WrapRows<'_> { buffer_row: None, multibuffer_row: None, diff_status, + expand_info: None, } } else { buffer_row diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 998955b4ba..c043491f00 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -196,7 +196,6 @@ use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState}; pub const FILE_HEADER_HEIGHT: u32 = 2; pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1; -pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1; pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); const MAX_LINE_LEN: usize = 1024; @@ -1092,7 +1091,6 @@ impl Editor { EditorMode::SingleLine { auto_width: false }, buffer, None, - false, window, cx, ) @@ -1101,7 +1099,7 @@ impl Editor { pub fn multi_line(window: &mut Window, cx: &mut Context) -> Self { let buffer = cx.new(|cx| Buffer::local("", cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new(EditorMode::Full, buffer, None, false, window, cx) + Self::new(EditorMode::Full, buffer, None, window, cx) } pub fn auto_width(window: &mut Window, cx: &mut Context) -> Self { @@ -1111,7 +1109,6 @@ impl Editor { EditorMode::SingleLine { auto_width: true }, buffer, None, - false, window, cx, ) @@ -1124,7 +1121,6 @@ impl Editor { EditorMode::AutoHeight { max_lines }, buffer, None, - false, window, cx, ) @@ -1137,33 +1133,23 @@ impl Editor { cx: &mut Context, ) -> Self { let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); - Self::new(EditorMode::Full, buffer, project, false, window, cx) + Self::new(EditorMode::Full, buffer, project, window, cx) } pub fn for_multibuffer( buffer: Entity, project: Option>, - show_excerpt_controls: bool, window: &mut Window, cx: &mut Context, ) -> Self { - Self::new( - EditorMode::Full, - buffer, - project, - show_excerpt_controls, - window, - cx, - ) + Self::new(EditorMode::Full, buffer, project, window, cx) } pub fn clone(&self, window: &mut Window, cx: &mut Context) -> Self { - let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls(); let mut clone = Self::new( self.mode, self.buffer.clone(), self.project.clone(), - show_excerpt_controls, window, cx, ); @@ -1183,7 +1169,6 @@ impl Editor { mode: EditorMode, buffer: Entity, project: Option>, - show_excerpt_controls: bool, window: &mut Window, cx: &mut Context, ) -> Self { @@ -1228,10 +1213,8 @@ impl Editor { style.font(), font_size, None, - show_excerpt_controls, FILE_HEADER_HEIGHT, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, - MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT, fold_placeholder, cx, ) @@ -4693,8 +4676,8 @@ impl Editor { workspace.update_in(&mut cx, |workspace, window, cx| { let project = workspace.project().clone(); - let editor = cx - .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx)); + let editor = + cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx)); workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx); editor.update(cx, |editor, cx| { editor.highlight_background::( @@ -12381,7 +12364,6 @@ impl Editor { Editor::for_multibuffer( excerpt_buffer, Some(workspace.project().clone()), - true, window, cx, ) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 318e6ede48..90c9842359 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -7676,7 +7676,6 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) { EditorMode::Full, multi_buffer, Some(project.clone()), - true, window, cx, ) @@ -13501,7 +13500,6 @@ async fn test_mutlibuffer_in_navigation_history(cx: &mut TestAppContext) { EditorMode::Full, multi_buffer, Some(project.clone()), - true, window, cx, ) @@ -13984,9 +13982,8 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) { multibuffer }); - let editor = cx.add_window(|window, cx| { - Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx) - }); + let editor = + cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx)); editor .update(cx, |editor, _window, cx| { for (buffer, diff_base) in [ @@ -14105,9 +14102,8 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) { multibuffer }); - let editor = cx.add_window(|window, cx| { - Editor::new(EditorMode::Full, multi_buffer, None, true, window, cx) - }); + let editor = + cx.add_window(|window, cx| Editor::new(EditorMode::Full, multi_buffer, None, window, cx)); editor .update(cx, |editor, _window, cx| { let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx)); @@ -15663,14 +15659,7 @@ async fn test_display_diff_hunks(cx: &mut TestAppContext) { }); let editor = cx.add_window(|window, cx| { - Editor::new( - EditorMode::Full, - multibuffer, - Some(project), - true, - window, - cx, - ) + Editor::new(EditorMode::Full, multibuffer, Some(project), window, cx) }); cx.run_until_parked(); @@ -15689,9 +15678,9 @@ async fn test_display_diff_hunks(cx: &mut TestAppContext) { assert_eq!( hunks, [ - DisplayRow(3)..DisplayRow(5), - DisplayRow(10)..DisplayRow(12), - DisplayRow(17)..DisplayRow(19), + DisplayRow(2)..DisplayRow(4), + DisplayRow(7)..DisplayRow(9), + DisplayRow(12)..DisplayRow(14), ] ); } @@ -16122,7 +16111,6 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) { EditorMode::Full, multi_buffer, Some(project.clone()), - true, window, cx, ) @@ -16277,7 +16265,6 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { EditorMode::Full, multi_buffer.clone(), Some(project.clone()), - true, window, cx, ) @@ -16285,7 +16272,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\naaaa\nbbbb\ncccc\n\n\n\nffff\ngggg\n\n\n\njjjj\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n", + "\n\naaaa\nbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555", ); multi_buffer_editor.update(cx, |editor, cx| { @@ -16293,7 +16280,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n", + "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555", "After folding the first buffer, its text should not be displayed" ); @@ -16302,7 +16289,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n", + "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555", "After folding the second buffer, its text should not be displayed" ); @@ -16327,7 +16314,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n", + "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n", "After unfolding the second buffer, its text should be displayed" ); @@ -16349,7 +16336,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nB\n\n\n\n\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n", + "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n", "After unfolding the first buffer, its and 2nd buffer's text should be displayed" ); @@ -16358,7 +16345,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nB\n\n\n\n\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\n\nqqqq\nrrrr\n\n\n\nuuuu\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n\n1111\n2222\n\n\n\n5555\n", + "\n\nB\n\n\n\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555", "After unfolding the all buffers, all original text should be displayed" ); } @@ -16444,13 +16431,12 @@ async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) { EditorMode::Full, multi_buffer, Some(project.clone()), - true, window, cx, ) }); - let full_text = "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n"; + let full_text = "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999"; assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), full_text, @@ -16461,7 +16447,7 @@ async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\n\n4444\n5555\n6666\n\n\n\n\n7777\n8888\n9999\n", + "\n\n\n\n4444\n5555\n6666\n\n\n7777\n8888\n9999", "After folding the first buffer, its text should not be displayed" ); @@ -16471,7 +16457,7 @@ async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) { assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\n\n\n\n7777\n8888\n9999\n", + "\n\n\n\n\n\n7777\n8888\n9999", "After folding the second buffer, its text should not be displayed" ); @@ -16489,7 +16475,7 @@ async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\n\n4444\n5555\n6666\n\n\n", + "\n\n\n\n4444\n5555\n6666\n\n", "After unfolding the second buffer, its text should be displayed" ); @@ -16498,7 +16484,7 @@ async fn test_folding_buffers_with_one_excerpt(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n1111\n2222\n3333\n\n\n\n\n4444\n5555\n6666\n\n\n", + "\n\n1111\n2222\n3333\n\n\n4444\n5555\n6666\n\n", "After unfolding the first buffer, its text should be displayed" ); @@ -16564,7 +16550,6 @@ async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut Test EditorMode::Full, multi_buffer, Some(project.clone()), - true, window, cx, ) @@ -16583,7 +16568,7 @@ async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut Test editor.change_selections(None, window, cx, |s| s.select_ranges(Some(highlight_range))); }); - let full_text = format!("\n\n\n{sample_text}\n"); + let full_text = format!("\n\n{sample_text}"); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), full_text, @@ -16612,14 +16597,7 @@ async fn test_multi_buffer_navigation_with_folded_buffers(cx: &mut TestAppContex ], cx, ); - let mut editor = Editor::new( - EditorMode::Full, - multi_buffer.clone(), - None, - true, - window, - cx, - ); + let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx); let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids(); // fold all but the second buffer, so that we test navigating between two @@ -16931,7 +16909,7 @@ async fn assert_highlighted_edits( ) { let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(text, cx); - Editor::new(EditorMode::Full, buffer, None, true, window, cx) + Editor::new(EditorMode::Full, buffer, None, window, cx) }); let cx = &mut VisualTestContext::from_window(*window, cx); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 6c02eb2c74..b55f656dc6 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -18,12 +18,12 @@ use crate::{ scroll::{axis_pair, scroll_amount::ScrollAmount, AxisPair}, BlockId, ChunkReplacement, CursorShape, CustomBlockId, DisplayDiffHunk, DisplayPoint, DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode, - EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GoToHunk, - GoToPreviousHunk, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor, - InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineHighlight, LineUp, - OpenExcerpts, PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, - Selection, SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, COLUMNAR_SELECTION_MODIFIERS, - CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, + EditorSettings, EditorSnapshot, EditorStyle, FocusedBlock, GoToHunk, GoToPreviousHunk, + GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor, InlayHintRefreshReason, + InlineCompletion, JumpData, LineDown, LineHighlight, LineUp, OpenExcerpts, PageDown, PageUp, + Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, Selection, SoftWrap, + StickyHeaderExcerpt, ToPoint, ToggleFold, COLUMNAR_SELECTION_MODIFIERS, CURSORS_VISIBLE_FOR, + FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, }; use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind}; @@ -33,10 +33,10 @@ use file_icons::FileIcons; use git::{blame::BlameEntry, status::FileStatus, Oid}; use gpui::{ anchored, deferred, div, fill, linear_color_stop, linear_gradient, outline, point, px, quad, - relative, size, solid_background, svg, transparent_black, Action, AnyElement, App, - AvailableSpace, Axis, Bounds, ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners, - CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Focusable as _, - FontId, GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Keystroke, Length, + relative, size, solid_background, transparent_black, Action, AnyElement, App, AvailableSpace, + Axis, Bounds, ClickEvent, ClipboardItem, ContentMask, Context, Corner, Corners, CursorStyle, + DispatchPhase, Edges, Element, ElementInputHandler, Entity, Focusable as _, FontId, + GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Keystroke, Length, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled, Subscription, TextRun, TextStyleRefinement, Window, @@ -52,8 +52,8 @@ use language::{ }; use lsp::DiagnosticSeverity; use multi_buffer::{ - Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, - RowInfo, + Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, ExpandInfo, MultiBufferPoint, + MultiBufferRow, RowInfo, }; use project::project_settings::{self, GitGutterSetting, ProjectSettings}; use settings::Settings; @@ -72,7 +72,7 @@ use sum_tree::Bias; use text::BufferId; use theme::{ActiveTheme, Appearance, BufferLineHeight, PlayerColor}; use ui::{ - h_flex, prelude::*, ButtonLike, ButtonStyle, ContextMenu, IconButtonShape, KeyBinding, Tooltip, + h_flex, prelude::*, ButtonLike, ContextMenu, IconButtonShape, KeyBinding, Tooltip, POPOVER_Y_PADDING, }; use unicode_segmentation::UnicodeSegmentation; @@ -1515,6 +1515,19 @@ impl EditorElement { } } + fn prepaint_expand_toggles( + &self, + expand_toggles: &mut [Option<(AnyElement, gpui::Point)>], + window: &mut Window, + cx: &mut App, + ) { + for (expand_toggle, origin) in expand_toggles.iter_mut().flatten() { + let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent); + expand_toggle.layout_as_root(available_space, window, cx); + expand_toggle.prepaint_as_root(*origin, available_space, window, cx); + } + } + fn prepaint_crease_trailers( &self, trailers: Vec>, @@ -2009,6 +2022,7 @@ impl EditorElement { &self, line_height: Pixels, range: Range, + row_infos: &[RowInfo], scroll_pixel_position: gpui::Point, gutter_dimensions: &GutterDimensions, gutter_hitbox: &Hitbox, @@ -2074,6 +2088,12 @@ impl EditorElement { } } let display_row = multibuffer_point.to_display_point(snapshot).row(); + if row_infos + .get((display_row - range.start).0 as usize) + .is_some_and(|row_info| row_info.expand_info.is_some()) + { + return None; + } let button = editor.render_run_indicator( &self.style, Some(display_row) == active_task_indicator_row, @@ -2098,6 +2118,82 @@ impl EditorElement { }) } + fn layout_excerpt_gutter( + &self, + gutter_hitbox: &Hitbox, + line_height: Pixels, + scroll_position: gpui::Point, + buffer_rows: &[RowInfo], + window: &mut Window, + cx: &mut App, + ) -> Vec)>> { + let editor_font_size = self.style.text.font_size.to_pixels(window.rem_size()) * 1.2; + + let icon_size = editor_font_size.round(); + let button_h_padding = ((icon_size - px(1.0)) / 2.0).round() - px(2.0); + + let scroll_top = scroll_position.y * line_height; + + let elements = buffer_rows + .into_iter() + .enumerate() + .map(|(ix, row_info)| { + let ExpandInfo { + excerpt_id, + direction, + } = row_info.expand_info?; + + let icon_name = match direction { + ExpandExcerptDirection::Up => IconName::ExpandUp, + ExpandExcerptDirection::Down => IconName::ExpandDown, + ExpandExcerptDirection::UpAndDown => IconName::ExpandVertical, + }; + + let editor = self.editor.clone(); + let max_row = self + .editor + .read(cx) + .buffer() + .read(cx) + .snapshot(cx) + .widest_line_number(); + let is_wide = max_row > 999 + && row_info + .buffer_row + .is_some_and(|row| row.ilog10() == max_row.ilog10()); + + let toggle = IconButton::new(("expand", ix), icon_name) + .icon_color(Color::Custom(cx.theme().colors().editor_line_number)) + .selected_icon_color(Color::Custom(cx.theme().colors().editor_foreground)) + .icon_size(IconSize::Custom(rems(editor_font_size / window.rem_size()))) + .width((icon_size + button_h_padding * 2).into()) + .when(is_wide, |el| { + el.width((icon_size + button_h_padding).into()) + }) + .on_click(move |_, _, cx| { + editor.update(cx, |editor, cx| { + editor.expand_excerpt(excerpt_id, direction, cx); + }); + }) + .tooltip(Tooltip::for_action_title( + "Expand excerpt", + &crate::actions::ExpandExcerpts::default(), + )) + .into_any_element(); + + let position = point( + px(1.), + ix as f32 * line_height - (scroll_top % line_height) + px(1.), + ); + let origin = gutter_hitbox.origin + position; + + Some((toggle, origin)) + }) + .collect(); + + elements + } + fn layout_code_actions_indicator( &self, line_height: Pixels, @@ -2315,6 +2411,9 @@ impl EditorElement { .into_iter() .enumerate() .map(|(ix, info)| { + if info.expand_info.is_some() { + return None; + } let row = info.multibuffer_row?; let display_row = DisplayRow(rows.start.0 + ix as u32); let active = active_rows.contains_key(&display_row); @@ -2337,6 +2436,9 @@ impl EditorElement { buffer_rows .into_iter() .map(|row_info| { + if row_info.expand_info.is_some() { + return None; + } if let Some(row) = row_info.multibuffer_row { snapshot.render_crease_trailer(row, window, cx) } else { @@ -2514,25 +2616,11 @@ impl EditorElement { Block::FoldedBuffer { first_excerpt, - prev_excerpt, - show_excerpt_controls, height, + .. } => { let selected = selected_buffer_ids.contains(&first_excerpt.buffer_id); - let mut result = v_flex().id(block_id).w_full(); - - if let Some(prev_excerpt) = prev_excerpt { - if *show_excerpt_controls { - result = result.child(self.render_expand_excerpt_control( - block_id, - ExpandExcerptDirection::Down, - prev_excerpt.id, - gutter_dimensions, - window, - cx, - )); - } - } + let result = v_flex().id(block_id).w_full(); let jump_data = header_jump_data(snapshot, block_row_start, *height, first_excerpt); result @@ -2540,6 +2628,7 @@ impl EditorElement { first_excerpt, true, selected, + false, jump_data, window, cx, @@ -2548,28 +2637,14 @@ impl EditorElement { } Block::ExcerptBoundary { - prev_excerpt, next_excerpt, - show_excerpt_controls, height, starts_new_buffer, + .. } => { let color = cx.theme().colors().clone(); let mut result = v_flex().id(block_id).w_full(); - if let Some(prev_excerpt) = prev_excerpt { - if *show_excerpt_controls { - result = result.child(self.render_expand_excerpt_control( - block_id, - ExpandExcerptDirection::Down, - prev_excerpt.id, - gutter_dimensions, - window, - cx, - )); - } - } - if let Some(next_excerpt) = next_excerpt { let jump_data = header_jump_data(snapshot, block_row_start, *height, next_excerpt); @@ -2582,6 +2657,7 @@ impl EditorElement { next_excerpt, false, selected, + false, jump_data, window, cx, @@ -2590,40 +2666,17 @@ impl EditorElement { result = result .child(div().h(FILE_HEADER_HEIGHT as f32 * window.line_height())); } - - if *show_excerpt_controls { - result = result.child(self.render_expand_excerpt_control( - block_id, - ExpandExcerptDirection::Up, - next_excerpt.id, - gutter_dimensions, - window, - cx, - )); - } } else { - if *show_excerpt_controls { - result = result.child( - h_flex() - .relative() - .child( - div() - .top(px(0.)) - .absolute() - .w_full() - .h_px() - .bg(color.border_variant), - ) - .child(self.render_expand_excerpt_control( - block_id, - ExpandExcerptDirection::Up, - next_excerpt.id, - gutter_dimensions, - window, - cx, - )), - ); - } + result = result.child( + h_flex().relative().child( + div() + .top(line_height / 2.) + .absolute() + .w_full() + .h_px() + .bg(color.border_variant), + ), + ); }; } @@ -2662,6 +2715,7 @@ impl EditorElement { for_excerpt: &ExcerptInfo, is_folded: bool, is_selected: bool, + _is_sticky: bool, jump_data: JumpData, window: &mut Window, cx: &mut App, @@ -2708,7 +2762,7 @@ impl EditorElement { .pl_0p5() .pr_5() .rounded_sm() - .shadow_md() + // .when(is_sticky, |el| el.shadow_md()) .border_1() .map(|div| { let border_color = if is_selected @@ -2847,95 +2901,6 @@ impl EditorElement { ) } - fn render_expand_excerpt_control( - &self, - block_id: BlockId, - direction: ExpandExcerptDirection, - excerpt_id: ExcerptId, - gutter_dimensions: &GutterDimensions, - window: &Window, - cx: &mut App, - ) -> impl IntoElement { - let color = cx.theme().colors().clone(); - let hover_color = color.border_variant.opacity(0.5); - let focus_handle = self.editor.focus_handle(cx).clone(); - - let icon_offset = - gutter_dimensions.width - (gutter_dimensions.left_padding + gutter_dimensions.margin); - let header_height = MULTI_BUFFER_EXCERPT_HEADER_HEIGHT as f32 * window.line_height(); - let group_name = if direction == ExpandExcerptDirection::Down { - "expand-down" - } else { - "expand-up" - }; - - let expand_area = |id: SharedString| { - h_flex() - .id(id) - .w_full() - .cursor_pointer() - .block_mouse_down() - .on_mouse_move(|_, _, cx| cx.stop_propagation()) - .hover(|style| style.bg(hover_color)) - .tooltip({ - let focus_handle = focus_handle.clone(); - move |window, cx| { - Tooltip::for_action_in( - "Expand Excerpt", - &ExpandExcerpts { lines: 0 }, - &focus_handle, - window, - cx, - ) - } - }) - }; - - expand_area( - format!( - "block-{}-{}", - block_id, - if direction == ExpandExcerptDirection::Down { - "down" - } else { - "up" - } - ) - .into(), - ) - .group(group_name) - .child( - h_flex() - .w(icon_offset) - .h(header_height) - .flex_none() - .justify_end() - .child( - ButtonLike::new("expand-icon") - .style(ButtonStyle::Transparent) - .child( - svg() - .path(if direction == ExpandExcerptDirection::Down { - IconName::ArrowDownFromLine.path() - } else { - IconName::ArrowUpFromLine.path() - }) - .size(IconSize::XSmall.rems()) - .text_color(cx.theme().colors().editor_line_number) - .group_hover(group_name, |style| { - style.text_color(cx.theme().colors().editor_active_line_number) - }), - ), - ), - ) - .on_click(window.listener_for(&self.editor, { - move |editor, _, _, cx| { - editor.expand_excerpt(excerpt_id, direction, cx); - cx.stop_propagation(); - } - })) - } - fn render_blocks( &self, rows: Range, @@ -3167,7 +3132,6 @@ impl EditorElement { &self, StickyHeaderExcerpt { excerpt, - next_excerpt_controls_present, next_buffer_row, }: StickyHeaderExcerpt<'_>, scroll_position: f32, @@ -3204,7 +3168,7 @@ impl EditorElement { .top_0(), ) .child( - self.render_buffer_header(excerpt, false, selected, jump_data, window, cx) + self.render_buffer_header(excerpt, false, selected, true, jump_data, window, cx) .into_any_element(), ) .into_any_element(); @@ -3214,11 +3178,7 @@ impl EditorElement { 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 max_row = next_buffer_row - FILE_HEADER_HEIGHT * 2; let offset = scroll_position - max_row as f32; @@ -4514,6 +4474,12 @@ impl EditorElement { } }); + window.with_element_namespace("expand_toggles", |window| { + for (expand_toggle, _) in layout.expand_toggles.iter_mut().flatten() { + expand_toggle.paint(window, cx); + } + }); + for test_indicator in layout.test_indicators.iter_mut() { test_indicator.paint(window, cx); } @@ -6891,6 +6857,18 @@ impl Element for EditorElement { cx, ); + let mut expand_toggles = + window.with_element_namespace("expand_toggles", |window| { + self.layout_excerpt_gutter( + &gutter_hitbox, + line_height, + scroll_position, + &row_infos, + window, + cx, + ) + }); + let mut crease_toggles = window.with_element_namespace("crease_toggles", |window| { self.layout_crease_toggles( @@ -6988,7 +6966,7 @@ impl Element for EditorElement { 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) + snapshot.sticky_header_excerpt(scroll_position.y) } else { None }; @@ -7305,7 +7283,14 @@ impl Element for EditorElement { .tasks .contains_key(&(buffer_id, row)); - if !has_test_indicator { + let has_expand_indicator = row_infos + .get( + (newest_selection_head.row() - start_row).0 + as usize, + ) + .is_some_and(|row_info| row_info.expand_info.is_some()); + + if !has_test_indicator && !has_expand_indicator { code_actions_indicator = self .layout_code_actions_indicator( line_height, @@ -7338,6 +7323,7 @@ impl Element for EditorElement { self.layout_run_indicators( line_height, start_row..end_row, + &row_infos, scroll_pixel_position, &gutter_dimensions, &gutter_hitbox, @@ -7400,6 +7386,10 @@ impl Element for EditorElement { ) }); + window.with_element_namespace("expand_toggles", |window| { + self.prepaint_expand_toggles(&mut expand_toggles, window, cx) + }); + let invisible_symbol_font_size = font_size / 2.; let tab_invisible = window .text_system() @@ -7501,6 +7491,7 @@ impl Element for EditorElement { tab_invisible, space_invisible, sticky_buffer_header, + expand_toggles, } }) }) @@ -7674,6 +7665,7 @@ pub struct EditorLayout { code_actions_indicator: Option, test_indicators: Vec, crease_toggles: Vec>, + expand_toggles: Vec)>>, diff_hunk_controls: Vec, crease_trailers: Vec>, inline_completion_popover: Option, @@ -8307,7 +8299,7 @@ mod tests { init_test(cx, |_| {}); let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); - Editor::new(EditorMode::Full, buffer, None, true, window, cx) + Editor::new(EditorMode::Full, buffer, None, window, cx) }); let editor = window.root(cx).unwrap(); @@ -8407,7 +8399,7 @@ mod tests { let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx); - Editor::new(EditorMode::Full, buffer, None, true, window, cx) + Editor::new(EditorMode::Full, buffer, None, window, cx) }); let cx = &mut VisualTestContext::from_window(*window, cx); let editor = window.root(cx).unwrap(); @@ -8470,90 +8462,6 @@ mod tests { state.active_rows.keys().cloned().collect::>(), vec![DisplayRow(0), DisplayRow(3), DisplayRow(5), DisplayRow(6)] ); - - // multi-buffer support - // in DisplayPoint coordinates, this is what we're dealing with: - // 0: [[file - // 1: header - // 2: section]] - // 3: aaaaaa - // 4: bbbbbb - // 5: cccccc - // 6: - // 7: [[footer]] - // 8: [[header]] - // 9: ffffff - // 10: gggggg - // 11: hhhhhh - // 12: - // 13: [[footer]] - // 14: [[file - // 15: header - // 16: section]] - // 17: bbbbbb - // 18: cccccc - // 19: dddddd - // 20: [[footer]] - let window = cx.add_window(|window, cx| { - let buffer = MultiBuffer::build_multi( - [ - ( - &(sample_text(8, 6, 'a') + "\n"), - vec![ - Point::new(0, 0)..Point::new(3, 0), - Point::new(4, 0)..Point::new(7, 0), - ], - ), - ( - &(sample_text(8, 6, 'a') + "\n"), - vec![Point::new(1, 0)..Point::new(3, 0)], - ), - ], - cx, - ); - Editor::new(EditorMode::Full, buffer, None, true, window, cx) - }); - let editor = window.root(cx).unwrap(); - let style = cx.update(|_, cx| editor.read(cx).style().unwrap().clone()); - let _state = window.update(cx, |editor, window, cx| { - editor.cursor_shape = CursorShape::Block; - editor.change_selections(None, window, cx, |s| { - s.select_display_ranges([ - DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(7), 0), - DisplayPoint::new(DisplayRow(10), 0)..DisplayPoint::new(DisplayRow(13), 0), - ]); - }); - }); - - let (_, state) = cx.draw( - point(px(500.), px(500.)), - size(px(500.), px(500.)), - |_, _| EditorElement::new(&editor, style), - ); - assert_eq!(state.selections.len(), 1); - let local_selections = &state.selections[0].1; - assert_eq!(local_selections.len(), 2); - - // moves cursor on excerpt boundary back a line - // and doesn't allow selection to bleed through - assert_eq!( - local_selections[0].range, - DisplayPoint::new(DisplayRow(4), 0)..DisplayPoint::new(DisplayRow(7), 0) - ); - assert_eq!( - local_selections[0].head, - DisplayPoint::new(DisplayRow(6), 0) - ); - // moves cursor on buffer boundary back two lines - // and doesn't allow selection to bleed through - assert_eq!( - local_selections[1].range, - DisplayPoint::new(DisplayRow(10), 0)..DisplayPoint::new(DisplayRow(13), 0) - ); - assert_eq!( - local_selections[1].head, - DisplayPoint::new(DisplayRow(12), 0) - ); } #[gpui::test] @@ -8562,7 +8470,7 @@ mod tests { let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple("", cx); - Editor::new(EditorMode::Full, buffer, None, true, window, cx) + Editor::new(EditorMode::Full, buffer, None, window, cx) }); let cx = &mut VisualTestContext::from_window(*window, cx); let editor = window.root(cx).unwrap(); @@ -8788,7 +8696,7 @@ mod tests { ); let window = cx.add_window(|window, cx| { let buffer = MultiBuffer::build_simple(input_text, cx); - Editor::new(editor_mode, buffer, None, true, window, cx) + Editor::new(editor_mode, buffer, None, window, cx) }); let cx = &mut VisualTestContext::from_window(*window, cx); let editor = window.root(cx).unwrap(); diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index cc0511d3a9..d5c14b0870 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -2618,7 +2618,7 @@ pub mod tests { cx.executor().run_until_parked(); let editor = cx.add_window(|window, cx| { - Editor::for_multibuffer(multibuffer, Some(project.clone()), true, window, cx) + Editor::for_multibuffer(multibuffer, Some(project.clone()), window, cx) }); let editor_edited = Arc::new(AtomicBool::new(false)); @@ -2830,7 +2830,6 @@ pub mod tests { "main hint #5".to_string(), "other hint(edited) #0".to_string(), "other hint(edited) #1".to_string(), - "other hint(edited) #2".to_string(), ]; assert_eq!( expected_hints, @@ -2921,7 +2920,7 @@ pub mod tests { cx.executor().run_until_parked(); let editor = cx.add_window(|window, cx| { - Editor::for_multibuffer(multibuffer, Some(project.clone()), true, window, cx) + Editor::for_multibuffer(multibuffer, Some(project.clone()), window, cx) }); let editor_edited = Arc::new(AtomicBool::new(false)); let fake_server = fake_servers.next().await.unwrap(); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 51653503fb..33ecf3b858 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -129,13 +129,8 @@ impl FollowableItem for Editor { }); cx.new(|cx| { - let mut editor = Editor::for_multibuffer( - multibuffer, - Some(project.clone()), - true, - window, - cx, - ); + let mut editor = + Editor::for_multibuffer(multibuffer, Some(project.clone()), window, cx); editor.remote_id = Some(remote_id); editor }) diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 47edb2cee1..13116cd866 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -893,8 +893,6 @@ mod tests { font, font_size, None, - true, - 1, 1, 1, FoldPlaceholder::test(), @@ -1110,138 +1108,136 @@ mod tests { font, px(14.0), None, - true, 0, 2, - 0, FoldPlaceholder::test(), cx, ) }); let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); - assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn"); + assert_eq!(snapshot.text(), "abc\ndefg\nhijkl\nmn"); let col_2_x = snapshot - .x_for_display_point(DisplayPoint::new(DisplayRow(2), 2), &text_layout_details); + .x_for_display_point(DisplayPoint::new(DisplayRow(0), 2), &text_layout_details); // Can't move up into the first excerpt's header assert_eq!( up( &snapshot, - DisplayPoint::new(DisplayRow(2), 2), + DisplayPoint::new(DisplayRow(0), 2), SelectionGoal::HorizontalPosition(col_2_x.0), false, &text_layout_details ), ( - DisplayPoint::new(DisplayRow(2), 0), + DisplayPoint::new(DisplayRow(0), 0), SelectionGoal::HorizontalPosition(col_2_x.0), ), ); assert_eq!( up( &snapshot, - DisplayPoint::new(DisplayRow(2), 0), + DisplayPoint::new(DisplayRow(0), 0), SelectionGoal::None, false, &text_layout_details ), ( - DisplayPoint::new(DisplayRow(2), 0), + DisplayPoint::new(DisplayRow(0), 0), SelectionGoal::HorizontalPosition(0.0), ), ); let col_4_x = snapshot - .x_for_display_point(DisplayPoint::new(DisplayRow(3), 4), &text_layout_details); + .x_for_display_point(DisplayPoint::new(DisplayRow(1), 4), &text_layout_details); // Move up and down within first excerpt assert_eq!( up( &snapshot, - DisplayPoint::new(DisplayRow(3), 4), + DisplayPoint::new(DisplayRow(1), 4), SelectionGoal::HorizontalPosition(col_4_x.0), false, &text_layout_details ), ( - DisplayPoint::new(DisplayRow(2), 3), + DisplayPoint::new(DisplayRow(0), 3), SelectionGoal::HorizontalPosition(col_4_x.0) ), ); assert_eq!( down( &snapshot, - DisplayPoint::new(DisplayRow(2), 3), + DisplayPoint::new(DisplayRow(0), 3), SelectionGoal::HorizontalPosition(col_4_x.0), false, &text_layout_details ), ( - DisplayPoint::new(DisplayRow(3), 4), + DisplayPoint::new(DisplayRow(1), 4), SelectionGoal::HorizontalPosition(col_4_x.0) ), ); let col_5_x = snapshot - .x_for_display_point(DisplayPoint::new(DisplayRow(6), 5), &text_layout_details); + .x_for_display_point(DisplayPoint::new(DisplayRow(2), 5), &text_layout_details); // Move up and down across second excerpt's header assert_eq!( up( &snapshot, - DisplayPoint::new(DisplayRow(6), 5), + DisplayPoint::new(DisplayRow(2), 5), SelectionGoal::HorizontalPosition(col_5_x.0), false, &text_layout_details ), ( - DisplayPoint::new(DisplayRow(3), 4), + DisplayPoint::new(DisplayRow(1), 4), SelectionGoal::HorizontalPosition(col_5_x.0) ), ); assert_eq!( down( &snapshot, - DisplayPoint::new(DisplayRow(3), 4), + DisplayPoint::new(DisplayRow(1), 4), SelectionGoal::HorizontalPosition(col_5_x.0), false, &text_layout_details ), ( - DisplayPoint::new(DisplayRow(6), 5), + DisplayPoint::new(DisplayRow(2), 5), SelectionGoal::HorizontalPosition(col_5_x.0) ), ); let max_point_x = snapshot - .x_for_display_point(DisplayPoint::new(DisplayRow(7), 2), &text_layout_details); + .x_for_display_point(DisplayPoint::new(DisplayRow(3), 2), &text_layout_details); // Can't move down off the end, and attempting to do so leaves the selection goal unchanged assert_eq!( down( &snapshot, - DisplayPoint::new(DisplayRow(7), 0), + DisplayPoint::new(DisplayRow(3), 0), SelectionGoal::HorizontalPosition(0.0), false, &text_layout_details ), ( - DisplayPoint::new(DisplayRow(7), 2), + DisplayPoint::new(DisplayRow(3), 2), SelectionGoal::HorizontalPosition(0.0) ), ); assert_eq!( down( &snapshot, - DisplayPoint::new(DisplayRow(7), 2), + DisplayPoint::new(DisplayRow(3), 2), SelectionGoal::HorizontalPosition(max_point_x.0), false, &text_layout_details ), ( - DisplayPoint::new(DisplayRow(7), 2), + DisplayPoint::new(DisplayRow(3), 2), SelectionGoal::HorizontalPosition(max_point_x.0) ), ); diff --git a/crates/editor/src/proposed_changes_editor.rs b/crates/editor/src/proposed_changes_editor.rs index b113dcf9df..7aa803cf35 100644 --- a/crates/editor/src/proposed_changes_editor.rs +++ b/crates/editor/src/proposed_changes_editor.rs @@ -62,8 +62,7 @@ impl ProposedChangesEditor { let (recalculate_diffs_tx, mut recalculate_diffs_rx) = mpsc::unbounded(); let mut this = Self { editor: cx.new(|cx| { - let mut editor = - Editor::for_multibuffer(multibuffer.clone(), project, true, window, cx); + let mut editor = Editor::for_multibuffer(multibuffer.clone(), project, window, cx); editor.set_expand_all_diff_hunks(cx); editor.set_completion_provider(None); editor.clear_code_action_providers(); diff --git a/crates/editor/src/rust_analyzer_ext.rs b/crates/editor/src/rust_analyzer_ext.rs index 0db5ab85e1..73e1093ae4 100644 --- a/crates/editor/src/rust_analyzer_ext.rs +++ b/crates/editor/src/rust_analyzer_ext.rs @@ -86,7 +86,7 @@ pub fn expand_macro_recursively( cx.new(|cx| MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)); workspace.add_item_to_active_pane( Box::new(cx.new(|cx| { - let mut editor = Editor::for_multibuffer(multibuffer, None, false, window, cx); + let mut editor = Editor::for_multibuffer(multibuffer, None, window, cx); editor.set_read_only(true); editor })), diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 35fb1b4c91..b494cce8ed 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -61,8 +61,6 @@ pub fn marked_display_snapshot( font, font_size, None, - true, - 1, 1, 1, FoldPlaceholder::test(), @@ -108,7 +106,7 @@ pub(crate) fn build_editor( window: &mut Window, cx: &mut Context, ) -> Editor { - Editor::new(EditorMode::Full, buffer, None, true, window, cx) + Editor::new(EditorMode::Full, buffer, None, window, cx) } pub(crate) fn build_editor_with_project( @@ -117,5 +115,5 @@ pub(crate) fn build_editor_with_project( window: &mut Window, cx: &mut Context, ) -> Editor { - Editor::new(EditorMode::Full, buffer, Some(project), true, window, cx) + Editor::new(EditorMode::Full, buffer, Some(project), window, cx) } diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 6a72409f12..659b00287d 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -358,7 +358,6 @@ pub(crate) fn commit_message_editor( EditorMode::AutoHeight { max_lines }, buffer, None, - false, window, cx, ); diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index db3302116e..d24c780734 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -141,13 +141,8 @@ impl ProjectDiff { let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite)); let editor = cx.new(|cx| { - let mut diff_display_editor = Editor::for_multibuffer( - multibuffer.clone(), - Some(project.clone()), - true, - window, - cx, - ); + let mut diff_display_editor = + Editor::for_multibuffer(multibuffer.clone(), Some(project.clone()), window, cx); diff_display_editor.disable_inline_diagnostics(); diff_display_editor.set_expand_all_diff_hunks(cx); diff_display_editor.register_addon(GitPanelAddon { diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 0c21946543..1967008ac2 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -360,12 +360,19 @@ impl ExcerptBoundary { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct ExpandInfo { + pub direction: ExpandExcerptDirection, + pub excerpt_id: ExcerptId, +} + #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct RowInfo { pub buffer_id: Option, pub buffer_row: Option, pub multibuffer_row: Option, pub diff_status: Option, + pub expand_info: Option, } /// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`]. @@ -438,6 +445,7 @@ pub struct DiffTransformSummary { pub struct MultiBufferRows<'a> { point: Point, is_empty: bool, + is_singleton: bool, cursor: MultiBufferCursor<'a, Point>, } @@ -4034,6 +4042,7 @@ impl MultiBufferSnapshot { let mut result = MultiBufferRows { point: Point::new(0, 0), is_empty: self.excerpts.is_empty(), + is_singleton: self.is_singleton(), cursor, }; result.seek(start_row); @@ -6216,6 +6225,22 @@ where self.cached_region.clone() } + fn is_at_start_of_excerpt(&mut self) -> bool { + if self.diff_transforms.start().1 > *self.excerpts.start() { + return false; + } else if self.diff_transforms.start().1 < *self.excerpts.start() { + return true; + } + + self.diff_transforms.prev(&()); + let prev_transform = self.diff_transforms.item(); + self.diff_transforms.next(&()); + + prev_transform.map_or(true, |next_transform| { + matches!(next_transform, DiffTransform::BufferContent { .. }) + }) + } + fn is_at_end_of_excerpt(&mut self) -> bool { if self.diff_transforms.end(&()).1 < self.excerpts.end(&()) { return false; @@ -7092,6 +7117,7 @@ impl Iterator for MultiBufferRows<'_> { buffer_row: Some(0), multibuffer_row: Some(MultiBufferRow(0)), diff_status: None, + expand_info: None, }); } @@ -7103,7 +7129,6 @@ impl Iterator for MultiBufferRows<'_> { } else { if self.point == self.cursor.diff_transforms.end(&()).0 .0 { let multibuffer_row = MultiBufferRow(self.point.row); - self.point += Point::new(1, 0); let last_excerpt = self .cursor .excerpts @@ -7115,11 +7140,43 @@ impl Iterator for MultiBufferRows<'_> { .end .to_point(&last_excerpt.buffer) .row; + + let first_row = last_excerpt + .range + .context + .start + .to_point(&last_excerpt.buffer) + .row; + + let expand_info = if self.is_singleton { + None + } else { + let needs_expand_up = first_row == last_row + && last_row > 0 + && !region.diff_hunk_status.is_some_and(|d| d.is_deleted()); + let needs_expand_down = last_row < last_excerpt.buffer.max_point().row; + + if needs_expand_up && needs_expand_down { + Some(ExpandExcerptDirection::UpAndDown) + } else if needs_expand_up { + Some(ExpandExcerptDirection::Up) + } else if needs_expand_down { + Some(ExpandExcerptDirection::Down) + } else { + None + } + .map(|direction| ExpandInfo { + direction, + excerpt_id: last_excerpt.id, + }) + }; + self.point += Point::new(1, 0); return Some(RowInfo { buffer_id: Some(last_excerpt.buffer_id), buffer_row: Some(last_row), multibuffer_row: Some(multibuffer_row), diff_status: None, + expand_info, }); } else { return None; @@ -7129,6 +7186,41 @@ impl Iterator for MultiBufferRows<'_> { let overshoot = self.point - region.range.start; let buffer_point = region.buffer_range.start + overshoot; + // dbg!( + // buffer_point.row, + // region.range.end.column, + // self.point.row, + // region.range.end.row, + // self.cursor.is_at_end_of_excerpt(), + // region.buffer.max_point().row + // ); + let expand_info = if self.is_singleton { + None + } else { + let needs_expand_up = self.point.row == region.range.start.row + && self.cursor.is_at_start_of_excerpt() + && buffer_point.row > 0; + let needs_expand_down = (region.excerpt.has_trailing_newline + && self.point.row + 1 == region.range.end.row + || !region.excerpt.has_trailing_newline && self.point.row == region.range.end.row) + && self.cursor.is_at_end_of_excerpt() + && buffer_point.row < region.buffer.max_point().row; + + if needs_expand_up && needs_expand_down { + Some(ExpandExcerptDirection::UpAndDown) + } else if needs_expand_up { + Some(ExpandExcerptDirection::Up) + } else if needs_expand_down { + Some(ExpandExcerptDirection::Down) + } else { + None + } + .map(|direction| ExpandInfo { + direction, + excerpt_id: region.excerpt.id, + }) + }; + let result = Some(RowInfo { buffer_id: Some(region.buffer.remote_id()), buffer_row: Some(buffer_point.row), @@ -7136,6 +7228,7 @@ impl Iterator for MultiBufferRows<'_> { diff_status: region .diff_hunk_status .filter(|_| self.point < region.range.end), + expand_info, }); self.point += Point::new(1, 0); result diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index af604cf93f..3b1677ccb0 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -29,7 +29,8 @@ fn test_empty_singleton(cx: &mut App) { buffer_id: Some(buffer_id), buffer_row: Some(0), multibuffer_row: Some(MultiBufferRow(0)), - diff_status: None + diff_status: None, + expand_info: None, }] ); } @@ -2118,6 +2119,7 @@ struct ReferenceRegion { range: Range, buffer_start: Option, status: Option, + excerpt_id: Option, } impl ReferenceMultibuffer { @@ -2274,6 +2276,7 @@ impl ReferenceMultibuffer { range: len..text.len(), buffer_start: Some(buffer.offset_to_point(offset)), status: None, + excerpt_id: Some(excerpt.id), }); // Add the deleted text for the hunk. @@ -2293,6 +2296,7 @@ impl ReferenceMultibuffer { base_buffer.offset_to_point(hunk.diff_base_byte_range.start), ), status: Some(DiffHunkStatus::deleted(hunk.secondary_status)), + excerpt_id: Some(excerpt.id), }); } @@ -2308,6 +2312,7 @@ impl ReferenceMultibuffer { range: len..text.len(), buffer_start: Some(buffer.offset_to_point(offset)), status: Some(DiffHunkStatus::added(hunk.secondary_status)), + excerpt_id: Some(excerpt.id), }); offset = hunk_range.end; } @@ -2322,6 +2327,7 @@ impl ReferenceMultibuffer { range: len..text.len(), buffer_start: Some(buffer.offset_to_point(offset)), status: None, + excerpt_id: Some(excerpt.id), }); } @@ -2332,6 +2338,7 @@ impl ReferenceMultibuffer { range: 0..1, buffer_start: Some(Point::new(0, 0)), status: None, + excerpt_id: None, }); } else { text.pop(); @@ -2345,12 +2352,58 @@ impl ReferenceMultibuffer { .map(|line| { let row_info = regions .iter() - .find(|region| region.range.contains(&ix)) - .map_or(RowInfo::default(), |region| { + .position(|region| region.range.contains(&ix)) + .map_or(RowInfo::default(), |region_ix| { + let region = ®ions[region_ix]; let buffer_row = region.buffer_start.map(|start_point| { start_point.row + text[region.range.start..ix].matches('\n').count() as u32 }); + let is_excerpt_start = region_ix == 0 + || ®ions[region_ix - 1].excerpt_id != ®ion.excerpt_id + || regions[region_ix - 1].range.is_empty(); + let mut is_excerpt_end = region_ix == regions.len() - 1 + || ®ions[region_ix + 1].excerpt_id != ®ion.excerpt_id; + let is_start = !text[region.range.start..ix].contains('\n'); + let mut is_end = if region.range.end > text.len() { + !text[ix..].contains('\n') + } else { + text[ix..region.range.end.min(text.len())] + .matches('\n') + .count() + == 1 + }; + if region_ix < regions.len() - 1 + && !text[ix..].contains("\n") + && region.status == Some(DiffHunkStatus::added_none()) + && regions[region_ix + 1].excerpt_id == region.excerpt_id + && regions[region_ix + 1].range.start == text.len() + { + is_end = true; + is_excerpt_end = true; + } + let mut expand_direction = None; + if let Some(buffer) = &self + .excerpts + .iter() + .find(|e| e.id == region.excerpt_id.unwrap()) + .map(|e| e.buffer.clone()) + { + let needs_expand_up = + is_excerpt_start && is_start && buffer_row.unwrap() > 0; + let needs_expand_down = is_excerpt_end + && is_end + && buffer.read(cx).max_point().row > buffer_row.unwrap(); + expand_direction = if needs_expand_up && needs_expand_down { + Some(ExpandExcerptDirection::UpAndDown) + } else if needs_expand_up { + Some(ExpandExcerptDirection::Up) + } else if needs_expand_down { + Some(ExpandExcerptDirection::Down) + } else { + None + }; + } RowInfo { buffer_id: region.buffer_id, diff_status: region.status, @@ -2358,6 +2411,12 @@ impl ReferenceMultibuffer { multibuffer_row: Some(MultiBufferRow( text[..ix].matches('\n').count() as u32 )), + expand_info: expand_direction.zip(region.excerpt_id).map( + |(direction, excerpt_id)| ExpandInfo { + direction, + excerpt_id, + }, + ), } }); ix += line.len() + 1; diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 29dcb07de2..a8f3c7fe09 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -1075,45 +1075,36 @@ impl OutlinePanel { }); } else { let mut offset = Point::default(); - let show_excerpt_controls = active_editor - .read(cx) - .display_map - .read(cx) - .show_excerpt_controls(); let expand_excerpt_control_height = 1.0; if let Some(buffer_id) = scroll_to_buffer { let current_folded = active_editor.read(cx).is_buffer_folded(buffer_id, cx); if current_folded { - if show_excerpt_controls { - let previous_buffer_id = self - .fs_entries - .iter() - .rev() - .filter_map(|entry| match entry { - FsEntry::File(file) => Some(file.buffer_id), - FsEntry::ExternalFile(external_file) => { - Some(external_file.buffer_id) - } - FsEntry::Directory(..) => None, - }) - .skip_while(|id| *id != buffer_id) - .nth(1); - if let Some(previous_buffer_id) = previous_buffer_id { - if !active_editor - .read(cx) - .is_buffer_folded(previous_buffer_id, cx) - { - offset.y += expand_excerpt_control_height; + let previous_buffer_id = self + .fs_entries + .iter() + .rev() + .filter_map(|entry| match entry { + FsEntry::File(file) => Some(file.buffer_id), + FsEntry::ExternalFile(external_file) => { + Some(external_file.buffer_id) } + FsEntry::Directory(..) => None, + }) + .skip_while(|id| *id != buffer_id) + .nth(1); + if let Some(previous_buffer_id) = previous_buffer_id { + if !active_editor + .read(cx) + .is_buffer_folded(previous_buffer_id, cx) + { + offset.y += expand_excerpt_control_height; } } } else { if multi_buffer_snapshot.as_singleton().is_none() { offset.y = -(active_editor.read(cx).file_header_size() as f32); } - if show_excerpt_controls { - offset.y -= expand_excerpt_control_height; - } + offset.y -= expand_excerpt_control_height; } } active_editor.update(cx, |editor, cx| { diff --git a/crates/repl/src/notebook/cell.rs b/crates/repl/src/notebook/cell.rs index b6ca2a50c3..8666fd89be 100644 --- a/crates/repl/src/notebook/cell.rs +++ b/crates/repl/src/notebook/cell.rs @@ -177,7 +177,6 @@ impl Cell { EditorMode::AutoHeight { max_lines: 1024 }, multi_buffer, None, - false, window, cx, ); diff --git a/crates/repl/src/outputs.rs b/crates/repl/src/outputs.rs index 39a9ab3107..2c7379c67d 100644 --- a/crates/repl/src/outputs.rs +++ b/crates/repl/src/outputs.rs @@ -185,13 +185,7 @@ impl Output { multi_buffer }); - Editor::for_multibuffer( - multibuffer, - None, - false, - window, - cx, - ) + Editor::for_multibuffer(multibuffer, None, window, cx) })); workspace .update(cx, |workspace, cx| { diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 9ff0daaa79..b78a35602e 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -2678,7 +2678,7 @@ mod tests { ], cx, ); - Editor::for_multibuffer(multibuffer, None, false, window, cx) + Editor::for_multibuffer(multibuffer, None, window, cx) }); let search_bar = cx.new_window_entity(|window, cx| { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index e702ac25b4..5a89efa1bd 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -739,8 +739,7 @@ impl ProjectSearchView { editor }); let results_editor = cx.new(|cx| { - let mut editor = - Editor::for_multibuffer(excerpts, Some(project.clone()), true, window, cx); + let mut editor = Editor::for_multibuffer(excerpts, Some(project.clone()), window, cx); editor.set_searchable(false); editor.set_in_project_search(true); editor @@ -2273,7 +2272,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n" + "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;" ); let match_background_color = cx.theme().colors().search_match_background; assert_eq!( @@ -2282,15 +2281,15 @@ pub mod tests { .update(cx, |editor, cx| editor.all_text_background_highlights(window, cx)), &[ ( - DisplayPoint::new(DisplayRow(3), 32)..DisplayPoint::new(DisplayRow(3), 35), + DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35), match_background_color ), ( - DisplayPoint::new(DisplayRow(3), 37)..DisplayPoint::new(DisplayRow(3), 40), + DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40), match_background_color ), ( - DisplayPoint::new(DisplayRow(8), 6)..DisplayPoint::new(DisplayRow(8), 9), + DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9), match_background_color ) ] @@ -2300,7 +2299,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(DisplayRow(3), 32)..DisplayPoint::new(DisplayRow(3), 35)] + [DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35)] ); search_view.select_match(Direction::Next, window, cx); @@ -2313,7 +2312,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(DisplayRow(3), 37)..DisplayPoint::new(DisplayRow(3), 40)] + [DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40)] ); search_view.select_match(Direction::Next, window, cx); }) @@ -2326,7 +2325,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(DisplayRow(8), 6)..DisplayPoint::new(DisplayRow(8), 9)] + [DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9)] ); search_view.select_match(Direction::Next, window, cx); }) @@ -2339,7 +2338,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(DisplayRow(3), 32)..DisplayPoint::new(DisplayRow(3), 35)] + [DisplayPoint::new(DisplayRow(2), 32)..DisplayPoint::new(DisplayRow(2), 35)] ); search_view.select_match(Direction::Prev, window, cx); }) @@ -2352,7 +2351,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(DisplayRow(8), 6)..DisplayPoint::new(DisplayRow(8), 9)] + [DisplayPoint::new(DisplayRow(5), 6)..DisplayPoint::new(DisplayRow(5), 9)] ); search_view.select_match(Direction::Prev, window, cx); }) @@ -2365,7 +2364,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.selections.display_ranges(cx)), - [DisplayPoint::new(DisplayRow(3), 37)..DisplayPoint::new(DisplayRow(3), 40)] + [DisplayPoint::new(DisplayRow(2), 37)..DisplayPoint::new(DisplayRow(2), 40)] ); }) .unwrap(); @@ -2538,7 +2537,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n", + "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;", "Search view results should match the query" ); assert!( @@ -2582,7 +2581,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n", + "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;", "Results should be unchanged after search view 2nd open in a row" ); assert!( @@ -2774,7 +2773,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n", + "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;", "Search view results should match the query" ); assert!( @@ -2829,7 +2828,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n", + "\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;", "Results of the first search view should not update too" ); assert!( @@ -2878,7 +2877,7 @@ pub mod tests { search_view_2 .results_editor .update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nconst FOUR: usize = one::ONE + three::THREE;\n", + "\n\nconst FOUR: usize = one::ONE + three::THREE;", "New search view with the updated query should have new search results" ); assert!( @@ -3023,7 +3022,7 @@ pub mod tests { search_view .results_editor .update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\nconst ONE: usize = 1;\n\n\n\n\nconst TWO: usize = one::ONE + one::ONE;\n", + "\n\nconst ONE: usize = 1;\n\n\nconst TWO: usize = one::ONE + one::ONE;", "New search in directory should have a filter that matches a certain directory" ); }) diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs index fce2a8906a..d184be8784 100644 --- a/crates/ui/src/components/icon.rs +++ b/crates/ui/src/components/icon.rs @@ -192,6 +192,8 @@ pub enum IconName { ExpandVertical, Exit, ExternalLink, + ExpandUp, + ExpandDown, Eye, File, FileCode, diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 76fead6f77..1278555788 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -1728,14 +1728,7 @@ async fn test_folded_multibuffer_excerpts(cx: &mut gpui::TestAppContext) { ], cx, ); - let mut editor = Editor::new( - EditorMode::Full, - multi_buffer.clone(), - None, - true, - window, - cx, - ); + let mut editor = Editor::new(EditorMode::Full, multi_buffer.clone(), None, window, cx); let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids(); // fold all but the second buffer, so that we test navigating between two diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 418568455c..e2238399f5 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1138,7 +1138,7 @@ fn open_log_file(workspace: &mut Workspace, window: &mut Window, cx: &mut Contex .new(|cx| MultiBuffer::singleton(buffer, cx).with_title("Log".into())); let editor = cx.new(|cx| { let mut editor = - Editor::for_multibuffer(buffer, Some(project), true, window, cx); + Editor::for_multibuffer(buffer, Some(project), window, cx); editor.set_read_only(true); editor.set_breadcrumb_header(format!( "Last {} lines in {}", @@ -1626,7 +1626,7 @@ fn open_telemetry_log_file( }); workspace.add_item_to_active_pane( Box::new(cx.new(|cx| { - let mut editor = Editor::for_multibuffer(buffer, Some(project), true, window, cx); + let mut editor = Editor::for_multibuffer(buffer, Some(project), window, cx); editor.set_read_only(true); editor.set_breadcrumb_header("Telemetry Log".into()); editor @@ -1665,13 +1665,8 @@ fn open_bundled_file( cx.new(|cx| MultiBuffer::singleton(buffer, cx).with_title(title.into())); workspace.add_item_to_active_pane( Box::new(cx.new(|cx| { - let mut editor = Editor::for_multibuffer( - buffer, - Some(project.clone()), - true, - window, - cx, - ); + let mut editor = + Editor::for_multibuffer(buffer, Some(project.clone()), window, cx); editor.set_read_only(true); editor.set_breadcrumb_header(title.into()); editor