Clean up inline assist editor rendering (#15536)

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Max <max@zed.dev>
This commit is contained in:
Antonio Scandurra 2024-07-31 17:43:08 +02:00 committed by GitHub
parent 73d8370177
commit 5b1ea7eda0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 267 additions and 1777 deletions

2
Cargo.lock generated
View File

@ -3391,13 +3391,11 @@ dependencies = [
"ctor", "ctor",
"editor", "editor",
"env_logger", "env_logger",
"feature_flags",
"futures 0.3.28", "futures 0.3.28",
"gpui", "gpui",
"language", "language",
"log", "log",
"lsp", "lsp",
"multi_buffer",
"pretty_assertions", "pretty_assertions",
"project", "project",
"rand 0.8.5", "rand 0.8.5",

View File

@ -1917,18 +1917,20 @@ impl ContextEditor {
cx.update(|cx| { cx.update(|cx| {
for suggestion in suggestion_group.suggestions { for suggestion in suggestion_group.suggestions {
let description = suggestion.description.unwrap_or_else(|| "Delete".into()); let description = suggestion.description.unwrap_or_else(|| "Delete".into());
let range = { let range = {
let buffer = editor.read(cx).buffer().read(cx).read(cx); let multibuffer = editor.read(cx).buffer().read(cx).read(cx);
let (&excerpt_id, _, _) = buffer.as_singleton().unwrap(); let (&excerpt_id, _, _) = multibuffer.as_singleton().unwrap();
buffer multibuffer
.anchor_in_excerpt(excerpt_id, suggestion.range.start) .anchor_in_excerpt(excerpt_id, suggestion.range.start)
.unwrap() .unwrap()
..buffer ..multibuffer
.anchor_in_excerpt(excerpt_id, suggestion.range.end) .anchor_in_excerpt(excerpt_id, suggestion.range.end)
.unwrap() .unwrap()
}; };
InlineAssistant::update_global(cx, |assistant, cx| { InlineAssistant::update_global(cx, |assistant, cx| {
assist_ids.push(assistant.suggest_assist( let suggestion_id = assistant.suggest_assist(
&editor, &editor,
range, range,
description, description,
@ -1936,16 +1938,20 @@ impl ContextEditor {
Some(workspace.clone()), Some(workspace.clone()),
assistant_panel.upgrade().as_ref(), assistant_panel.upgrade().as_ref(),
cx, cx,
)); );
assist_ids.push(suggestion_id);
}); });
} }
// Scroll the editor to the suggested assist // Scroll the editor to the suggested assist
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
let anchor = { let multibuffer = editor.buffer().read(cx).snapshot(cx);
let buffer = editor.buffer().read(cx).read(cx); let (&excerpt_id, _, buffer) = multibuffer.as_singleton().unwrap();
let (&excerpt_id, _, _) = buffer.as_singleton().unwrap(); let anchor = if suggestion_group.context_range.start.to_offset(buffer) == 0
buffer {
Anchor::min()
} else {
multibuffer
.anchor_in_excerpt(excerpt_id, suggestion_group.context_range.start) .anchor_in_excerpt(excerpt_id, suggestion_group.context_range.start)
.unwrap() .unwrap()
}; };

View File

@ -331,11 +331,16 @@ impl InlineAssistant {
prompt_editor: &View<PromptEditor>, prompt_editor: &View<PromptEditor>,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> [CustomBlockId; 2] { ) -> [CustomBlockId; 2] {
let prompt_editor_height = prompt_editor.update(cx, |prompt_editor, cx| {
prompt_editor
.editor
.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1 + 2)
});
let assist_blocks = vec![ let assist_blocks = vec![
BlockProperties { BlockProperties {
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
position: range.start, position: range.start,
height: prompt_editor.read(cx).height_in_lines, height: prompt_editor_height,
render: build_assist_editor_renderer(prompt_editor), render: build_assist_editor_renderer(prompt_editor),
disposition: BlockDisposition::Above, disposition: BlockDisposition::Above,
}, },
@ -446,9 +451,6 @@ impl InlineAssistant {
PromptEditorEvent::DismissRequested => { PromptEditorEvent::DismissRequested => {
self.dismiss_assist(assist_id, cx); self.dismiss_assist(assist_id, cx);
} }
PromptEditorEvent::Resized { height_in_lines } => {
self.resize_assist(assist_id, *height_in_lines, cx);
}
} }
} }
@ -786,33 +788,6 @@ impl InlineAssistant {
}); });
} }
fn resize_assist(
&mut self,
assist_id: InlineAssistId,
height_in_lines: u8,
cx: &mut WindowContext,
) {
if let Some(assist) = self.assists.get_mut(&assist_id) {
if let Some(editor) = assist.editor.upgrade() {
if let Some(decorations) = assist.decorations.as_ref() {
let mut new_blocks = HashMap::default();
new_blocks.insert(
decorations.prompt_block_id,
(
Some(height_in_lines),
build_assist_editor_renderer(&decorations.prompt_editor),
),
);
editor.update(cx, |editor, cx| {
editor
.display_map
.update(cx, |map, cx| map.replace_blocks(new_blocks, cx))
});
}
}
}
}
fn unlink_assist_group( fn unlink_assist_group(
&mut self, &mut self,
assist_group_id: InlineAssistGroupId, assist_group_id: InlineAssistGroupId,
@ -1029,8 +1004,8 @@ impl InlineAssistant {
editor editor
}); });
let height = deleted_lines_editor let height =
.update(cx, |editor, cx| editor.max_point(cx).row().0 as u8 + 1); deleted_lines_editor.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1);
new_blocks.push(BlockProperties { new_blocks.push(BlockProperties {
position: new_row, position: new_row,
height, height,
@ -1194,13 +1169,11 @@ enum PromptEditorEvent {
ConfirmRequested, ConfirmRequested,
CancelRequested, CancelRequested,
DismissRequested, DismissRequested,
Resized { height_in_lines: u8 },
} }
struct PromptEditor { struct PromptEditor {
id: InlineAssistId, id: InlineAssistId,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
height_in_lines: u8,
editor: View<Editor>, editor: View<Editor>,
edited_since_done: bool, edited_since_done: bool,
gutter_dimensions: Arc<Mutex<GutterDimensions>>, gutter_dimensions: Arc<Mutex<GutterDimensions>>,
@ -1307,9 +1280,8 @@ impl Render for PromptEditor {
.bg(cx.theme().colors().editor_background) .bg(cx.theme().colors().editor_background)
.border_y_1() .border_y_1()
.border_color(cx.theme().status().info_border) .border_color(cx.theme().status().info_border)
.py_1p5() .size_full()
.h_full() .py(cx.line_height() / 2.)
.w_full()
.on_action(cx.listener(Self::confirm)) .on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::cancel)) .on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::move_up)) .on_action(cx.listener(Self::move_up))
@ -1427,7 +1399,6 @@ impl PromptEditor {
let mut this = Self { let mut this = Self {
id, id,
height_in_lines: 1,
editor: prompt_editor, editor: prompt_editor,
edited_since_done: false, edited_since_done: false,
gutter_dimensions, gutter_dimensions,
@ -1443,7 +1414,6 @@ impl PromptEditor {
_token_count_subscriptions: token_count_subscriptions, _token_count_subscriptions: token_count_subscriptions,
workspace, workspace,
}; };
this.count_lines(cx);
this.count_tokens(cx); this.count_tokens(cx);
this.subscribe_to_editor(cx); this.subscribe_to_editor(cx);
this this
@ -1451,8 +1421,6 @@ impl PromptEditor {
fn subscribe_to_editor(&mut self, cx: &mut ViewContext<Self>) { fn subscribe_to_editor(&mut self, cx: &mut ViewContext<Self>) {
self.editor_subscriptions.clear(); self.editor_subscriptions.clear();
self.editor_subscriptions
.push(cx.observe(&self.editor, Self::handle_prompt_editor_changed));
self.editor_subscriptions self.editor_subscriptions
.push(cx.subscribe(&self.editor, Self::handle_prompt_editor_events)); .push(cx.subscribe(&self.editor, Self::handle_prompt_editor_events));
} }
@ -1487,22 +1455,6 @@ impl PromptEditor {
self.editor.read(cx).text(cx) self.editor.read(cx).text(cx)
} }
fn count_lines(&mut self, cx: &mut ViewContext<Self>) {
let height_in_lines = cmp::max(
2, // Make the editor at least two lines tall, to account for padding and buttons.
cmp::min(
self.editor
.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1),
Self::MAX_LINES as u32,
),
) as u8;
if height_in_lines != self.height_in_lines {
self.height_in_lines = height_in_lines;
cx.emit(PromptEditorEvent::Resized { height_in_lines });
}
}
fn handle_parent_editor_event( fn handle_parent_editor_event(
&mut self, &mut self,
_: View<Editor>, _: View<Editor>,
@ -1545,10 +1497,6 @@ impl PromptEditor {
}) })
} }
fn handle_prompt_editor_changed(&mut self, _: View<Editor>, cx: &mut ViewContext<Self>) {
self.count_lines(cx);
}
fn handle_prompt_editor_events( fn handle_prompt_editor_events(
&mut self, &mut self,
_: View<Editor>, _: View<Editor>,

View File

@ -18,13 +18,11 @@ collections.workspace = true
ctor.workspace = true ctor.workspace = true
editor.workspace = true editor.workspace = true
env_logger.workspace = true env_logger.workspace = true
feature_flags.workspace = true
futures.workspace = true futures.workspace = true
gpui.workspace = true gpui.workspace = true
language.workspace = true language.workspace = true
log.workspace = true log.workspace = true
lsp.workspace = true lsp.workspace = true
multi_buffer.workspace = true
project.workspace = true project.workspace = true
rand.workspace = true rand.workspace = true
schemars.workspace = true schemars.workspace = true

View File

@ -4,7 +4,6 @@ mod toolbar_controls;
#[cfg(test)] #[cfg(test)]
mod diagnostics_tests; mod diagnostics_tests;
pub(crate) mod grouped_diagnostics;
use anyhow::Result; use anyhow::Result;
use collections::{BTreeSet, HashSet}; use collections::{BTreeSet, HashSet};
@ -15,7 +14,6 @@ use editor::{
scroll::Autoscroll, scroll::Autoscroll,
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
}; };
use feature_flags::FeatureFlagAppExt;
use futures::{ use futures::{
channel::mpsc::{self, UnboundedSender}, channel::mpsc::{self, UnboundedSender},
StreamExt as _, StreamExt as _,
@ -54,9 +52,6 @@ pub fn init(cx: &mut AppContext) {
ProjectDiagnosticsSettings::register(cx); ProjectDiagnosticsSettings::register(cx);
cx.observe_new_views(ProjectDiagnosticsEditor::register) cx.observe_new_views(ProjectDiagnosticsEditor::register)
.detach(); .detach();
if !cx.has_flag::<feature_flags::GroupedDiagnostics>() {
grouped_diagnostics::init(cx);
}
} }
struct ProjectDiagnosticsEditor { struct ProjectDiagnosticsEditor {
@ -469,7 +464,7 @@ impl ProjectDiagnosticsEditor {
group_state.block_count += 1; group_state.block_count += 1;
blocks_to_add.push(BlockProperties { blocks_to_add.push(BlockProperties {
position: (excerpt_id, entry.range.start), position: (excerpt_id, entry.range.start),
height: diagnostic.message.matches('\n').count() as u8 + 1, height: diagnostic.message.matches('\n').count() as u32 + 1,
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
render: diagnostic_block_renderer( render: diagnostic_block_renderer(
diagnostic, None, true, true, diagnostic, None, true, true,
@ -787,7 +782,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into(); let highlight_style: HighlightStyle = cx.theme().colors().text_accent.into();
h_flex() h_flex()
.id(DIAGNOSTIC_HEADER) .id(DIAGNOSTIC_HEADER)
.py_2() .h(2. * cx.line_height())
.pl_10() .pl_10()
.pr_5() .pr_5()
.w_full() .w_full()

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,11 @@
use crate::{grouped_diagnostics::GroupedDiagnosticsEditor, ProjectDiagnosticsEditor}; use crate::ProjectDiagnosticsEditor;
use futures::future::Either;
use gpui::{EventEmitter, ParentElement, Render, View, ViewContext, WeakView}; use gpui::{EventEmitter, ParentElement, Render, View, ViewContext, WeakView};
use ui::prelude::*; use ui::prelude::*;
use ui::{IconButton, IconName, Tooltip}; use ui::{IconButton, IconName, Tooltip};
use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView}; use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
pub struct ToolbarControls { pub struct ToolbarControls {
editor: Option<Either<WeakView<ProjectDiagnosticsEditor>, WeakView<GroupedDiagnosticsEditor>>>, editor: Option<WeakView<ProjectDiagnosticsEditor>>,
} }
impl Render for ToolbarControls { impl Render for ToolbarControls {
@ -16,32 +15,16 @@ impl Render for ToolbarControls {
let mut is_updating = false; let mut is_updating = false;
if let Some(editor) = self.editor() { if let Some(editor) = self.editor() {
match editor { let editor = editor.read(cx);
Either::Left(editor) => { include_warnings = editor.include_warnings;
let editor = editor.read(cx); has_stale_excerpts = !editor.paths_to_update.is_empty();
include_warnings = editor.include_warnings; is_updating = editor.update_paths_tx.len() > 0
has_stale_excerpts = !editor.paths_to_update.is_empty(); || editor
is_updating = editor.update_paths_tx.len() > 0 .project
|| editor .read(cx)
.project .language_servers_running_disk_based_diagnostics()
.read(cx) .next()
.language_servers_running_disk_based_diagnostics() .is_some();
.next()
.is_some();
}
Either::Right(editor) => {
let editor = editor.read(cx);
include_warnings = editor.include_warnings;
has_stale_excerpts = !editor.paths_to_update.is_empty();
is_updating = editor.update_paths_tx.len() > 0
|| editor
.project
.read(cx)
.language_servers_running_disk_based_diagnostics()
.next()
.is_some();
}
}
} }
let tooltip = if include_warnings { let tooltip = if include_warnings {
@ -59,18 +42,9 @@ impl Render for ToolbarControls {
.tooltip(move |cx| Tooltip::text("Update excerpts", cx)) .tooltip(move |cx| Tooltip::text("Update excerpts", cx))
.on_click(cx.listener(|this, _, cx| { .on_click(cx.listener(|this, _, cx| {
if let Some(editor) = this.editor() { if let Some(editor) = this.editor() {
match editor { editor.update(cx, |editor, _| {
Either::Left(editor) => { editor.enqueue_update_stale_excerpts(None);
editor.update(cx, |editor, _| { });
editor.enqueue_update_stale_excerpts(None);
});
}
Either::Right(editor) => {
editor.update(cx, |editor, _| {
editor.enqueue_update_stale_excerpts(None);
});
}
}
} }
})), })),
) )
@ -80,18 +54,9 @@ impl Render for ToolbarControls {
.tooltip(move |cx| Tooltip::text(tooltip, cx)) .tooltip(move |cx| Tooltip::text(tooltip, cx))
.on_click(cx.listener(|this, _, cx| { .on_click(cx.listener(|this, _, cx| {
if let Some(editor) = this.editor() { if let Some(editor) = this.editor() {
match editor { editor.update(cx, |editor, cx| {
Either::Left(editor) => { editor.toggle_warnings(&Default::default(), cx);
editor.update(cx, |editor, cx| { });
editor.toggle_warnings(&Default::default(), cx);
});
}
Either::Right(editor) => {
editor.update(cx, |editor, cx| {
editor.toggle_warnings(&Default::default(), cx);
});
}
}
} }
})), })),
) )
@ -108,10 +73,7 @@ impl ToolbarItemView for ToolbarControls {
) -> ToolbarItemLocation { ) -> ToolbarItemLocation {
if let Some(pane_item) = active_pane_item.as_ref() { if let Some(pane_item) = active_pane_item.as_ref() {
if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() { if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
self.editor = Some(Either::Left(editor.downgrade())); self.editor = Some(editor.downgrade());
ToolbarItemLocation::PrimaryRight
} else if let Some(editor) = pane_item.downcast::<GroupedDiagnosticsEditor>() {
self.editor = Some(Either::Right(editor.downgrade()));
ToolbarItemLocation::PrimaryRight ToolbarItemLocation::PrimaryRight
} else { } else {
ToolbarItemLocation::Hidden ToolbarItemLocation::Hidden
@ -127,12 +89,7 @@ impl ToolbarControls {
ToolbarControls { editor: None } ToolbarControls { editor: None }
} }
fn editor( fn editor(&self) -> Option<View<ProjectDiagnosticsEditor>> {
&self, self.editor.as_ref()?.upgrade()
) -> Option<Either<View<ProjectDiagnosticsEditor>, View<GroupedDiagnosticsEditor>>> {
Some(match self.editor.as_ref()? {
Either::Left(diagnostics) => Either::Left(diagnostics.upgrade()?),
Either::Right(grouped_diagnostics) => Either::Right(grouped_diagnostics.upgrade()?),
})
} }
} }

View File

@ -120,9 +120,9 @@ impl DisplayMap {
font_size: Pixels, font_size: Pixels,
wrap_width: Option<Pixels>, wrap_width: Option<Pixels>,
show_excerpt_controls: bool, show_excerpt_controls: bool,
buffer_header_height: u8, buffer_header_height: u32,
excerpt_header_height: u8, excerpt_header_height: u32,
excerpt_footer_height: u8, excerpt_footer_height: u32,
fold_placeholder: FoldPlaceholder, fold_placeholder: FoldPlaceholder,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Self { ) -> Self {
@ -286,44 +286,11 @@ impl DisplayMap {
block_map.insert(blocks) block_map.insert(blocks)
} }
pub fn replace_blocks( pub fn resize_blocks(
&mut self, &mut self,
heights_and_renderers: HashMap<CustomBlockId, (Option<u8>, RenderBlock)>, heights: HashMap<CustomBlockId, u32>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
//
// Note: previous implementation of `replace_blocks` simply called
// `self.block_map.replace(styles)` which just modified the render by replacing
// the `RenderBlock` with the new one.
//
// ```rust
// for block in &self.blocks {
// if let Some(render) = renderers.remove(&block.id) {
// *block.render.lock() = render;
// }
// }
// ```
//
// If height changes however, we need to update the tree. There's a performance
// cost to this, so we'll split the replace blocks into handling the old behavior
// directly and the new behavior separately.
//
//
let mut only_renderers = HashMap::<CustomBlockId, RenderBlock>::default();
let mut full_replace = HashMap::<CustomBlockId, (u8, RenderBlock)>::default();
for (id, (height, render)) in heights_and_renderers {
if let Some(height) = height {
full_replace.insert(id, (height, render));
} else {
only_renderers.insert(id, render);
}
}
self.block_map.replace_renderers(only_renderers);
if full_replace.is_empty() {
return;
}
let snapshot = self.buffer.read(cx).snapshot(cx); let snapshot = self.buffer.read(cx).snapshot(cx);
let edits = self.buffer_subscription.consume().into_inner(); let edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx); let tab_size = Self::tab_size(&self.buffer, cx);
@ -334,7 +301,11 @@ impl DisplayMap {
.wrap_map .wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx)); .update(cx, |map, cx| map.sync(snapshot, edits, cx));
let mut block_map = self.block_map.write(snapshot, edits); let mut block_map = self.block_map.write(snapshot, edits);
block_map.replace(full_replace); block_map.resize(heights);
}
pub fn replace_blocks(&mut self, renderers: HashMap<CustomBlockId, RenderBlock>) {
self.block_map.replace_blocks(renderers);
} }
pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, cx: &mut ModelContext<Self>) { pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, cx: &mut ModelContext<Self>) {
@ -1051,6 +1022,18 @@ impl DisplaySnapshot {
let type_id = TypeId::of::<Tag>(); let type_id = TypeId::of::<Tag>();
self.inlay_highlights.get(&type_id) self.inlay_highlights.get(&type_id)
} }
pub fn buffer_header_height(&self) -> u32 {
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
}
} }
#[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)] #[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)]

View File

@ -35,9 +35,9 @@ pub struct BlockMap {
custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>, custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
transforms: RefCell<SumTree<Transform>>, transforms: RefCell<SumTree<Transform>>,
show_excerpt_controls: bool, show_excerpt_controls: bool,
buffer_header_height: u8, buffer_header_height: u32,
excerpt_header_height: u8, excerpt_header_height: u32,
excerpt_footer_height: u8, excerpt_footer_height: u32,
} }
pub struct BlockMapReader<'a> { pub struct BlockMapReader<'a> {
@ -52,6 +52,9 @@ pub struct BlockSnapshot {
wrap_snapshot: WrapSnapshot, wrap_snapshot: WrapSnapshot,
transforms: SumTree<Transform>, transforms: SumTree<Transform>,
custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>, custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
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)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -77,15 +80,15 @@ pub type RenderBlock = Box<dyn Send + FnMut(&mut BlockContext) -> AnyElement>;
pub struct CustomBlock { pub struct CustomBlock {
id: CustomBlockId, id: CustomBlockId,
position: Anchor, position: Anchor,
height: u8, height: u32,
style: BlockStyle, style: BlockStyle,
render: Mutex<RenderBlock>, render: Arc<Mutex<RenderBlock>>,
disposition: BlockDisposition, disposition: BlockDisposition,
} }
pub struct BlockProperties<P> { pub struct BlockProperties<P> {
pub position: P, pub position: P,
pub height: u8, pub height: u32,
pub style: BlockStyle, pub style: BlockStyle,
pub render: RenderBlock, pub render: RenderBlock,
pub disposition: BlockDisposition, pub disposition: BlockDisposition,
@ -189,14 +192,14 @@ pub enum Block {
id: ExcerptId, id: ExcerptId,
buffer: BufferSnapshot, buffer: BufferSnapshot,
range: ExcerptRange<text::Anchor>, range: ExcerptRange<text::Anchor>,
height: u8, height: u32,
starts_new_buffer: bool, starts_new_buffer: bool,
show_excerpt_controls: bool, show_excerpt_controls: bool,
}, },
ExcerptFooter { ExcerptFooter {
id: ExcerptId, id: ExcerptId,
disposition: BlockDisposition, disposition: BlockDisposition,
height: u8, height: u32,
}, },
} }
@ -231,7 +234,7 @@ impl Block {
} }
} }
pub fn height(&self) -> u8 { pub fn height(&self) -> u32 {
match self { match self {
Block::Custom(block) => block.height, Block::Custom(block) => block.height,
Block::ExcerptHeader { height, .. } => *height, Block::ExcerptHeader { height, .. } => *height,
@ -301,9 +304,9 @@ impl BlockMap {
pub fn new( pub fn new(
wrap_snapshot: WrapSnapshot, wrap_snapshot: WrapSnapshot,
show_excerpt_controls: bool, show_excerpt_controls: bool,
buffer_header_height: u8, buffer_header_height: u32,
excerpt_header_height: u8, excerpt_header_height: u32,
excerpt_footer_height: u8, excerpt_footer_height: u32,
) -> Self { ) -> Self {
let row_count = wrap_snapshot.max_point().row() + 1; let row_count = wrap_snapshot.max_point().row() + 1;
let map = Self { let map = Self {
@ -336,6 +339,9 @@ impl BlockMap {
wrap_snapshot, wrap_snapshot,
transforms: self.transforms.borrow().clone(), transforms: self.transforms.borrow().clone(),
custom_blocks_by_id: self.custom_blocks_by_id.clone(), 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,
}, },
} }
} }
@ -551,7 +557,7 @@ impl BlockMap {
*transforms = new_transforms; *transforms = new_transforms;
} }
pub fn replace_renderers(&mut self, mut renderers: HashMap<CustomBlockId, RenderBlock>) { pub fn replace_blocks(&mut self, mut renderers: HashMap<CustomBlockId, RenderBlock>) {
for block in &mut self.custom_blocks { for block in &mut self.custom_blocks {
if let Some(render) = renderers.remove(&block.id) { if let Some(render) = renderers.remove(&block.id) {
*block.render.lock() = render; *block.render.lock() = render;
@ -565,9 +571,9 @@ impl BlockMap {
pub fn header_and_footer_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>( pub fn header_and_footer_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>(
show_excerpt_controls: bool, show_excerpt_controls: bool,
excerpt_footer_height: u8, excerpt_footer_height: u32,
buffer_header_height: u8, buffer_header_height: u32,
excerpt_header_height: u8, excerpt_header_height: u32,
buffer: &'b multi_buffer::MultiBufferSnapshot, buffer: &'b multi_buffer::MultiBufferSnapshot,
range: R, range: R,
wrap_snapshot: &'c WrapSnapshot, wrap_snapshot: &'c WrapSnapshot,
@ -793,7 +799,7 @@ impl<'a> BlockMapWriter<'a> {
id, id,
position, position,
height: block.height, height: block.height,
render: Mutex::new(block.render), render: Arc::new(Mutex::new(block.render)),
disposition: block.disposition, disposition: block.disposition,
style: block.style, style: block.style,
}); });
@ -810,24 +816,21 @@ impl<'a> BlockMapWriter<'a> {
ids ids
} }
pub fn replace( pub fn resize(&mut self, mut heights: HashMap<CustomBlockId, u32>) {
&mut self,
mut heights_and_renderers: HashMap<CustomBlockId, (u8, RenderBlock)>,
) {
let wrap_snapshot = &*self.0.wrap_snapshot.borrow(); let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
let buffer = wrap_snapshot.buffer_snapshot(); let buffer = wrap_snapshot.buffer_snapshot();
let mut edits = Patch::default(); let mut edits = Patch::default();
let mut last_block_buffer_row = None; let mut last_block_buffer_row = None;
for block in &mut self.0.custom_blocks { for block in &mut self.0.custom_blocks {
if let Some((new_height, render)) = heights_and_renderers.remove(&block.id) { if let Some(new_height) = heights.remove(&block.id) {
if block.height != new_height { if block.height != new_height {
let new_block = CustomBlock { let new_block = CustomBlock {
id: block.id, id: block.id,
position: block.position, position: block.position,
height: new_height, height: new_height,
style: block.style, style: block.style,
render: Mutex::new(render), render: block.render.clone(),
disposition: block.disposition, disposition: block.disposition,
}; };
let new_block = Arc::new(new_block); let new_block = Arc::new(new_block);
@ -1174,7 +1177,7 @@ impl Transform {
Self { Self {
summary: TransformSummary { summary: TransformSummary {
input_rows: 0, input_rows: 0,
output_rows: block.height() as u32, output_rows: block.height(),
}, },
block: Some(block), block: Some(block),
} }
@ -1445,7 +1448,7 @@ mod tests {
.blocks_in_range(0..8) .blocks_in_range(0..8)
.map(|(start_row, block)| { .map(|(start_row, block)| {
let block = block.as_custom().unwrap(); let block = block.as_custom().unwrap();
(start_row..start_row + block.height as u32, block.id) (start_row..start_row + block.height, block.id)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -1697,10 +1700,9 @@ mod tests {
let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default()); let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
let mut hash_map = HashMap::default(); let mut new_heights = HashMap::default();
let render: RenderBlock = Box::new(|_| div().into_any()); new_heights.insert(block_ids[0], 2);
hash_map.insert(block_ids[0], (2_u8, render)); block_map_writer.resize(new_heights);
block_map_writer.replace(hash_map);
let snapshot = block_map.read(wraps_snapshot.clone(), Default::default()); let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n"); assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
} }
@ -1708,10 +1710,9 @@ mod tests {
{ {
let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default()); let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
let mut hash_map = HashMap::default(); let mut new_heights = HashMap::default();
let render: RenderBlock = Box::new(|_| div().into_any()); new_heights.insert(block_ids[0], 1);
hash_map.insert(block_ids[0], (1_u8, render)); block_map_writer.resize(new_heights);
block_map_writer.replace(hash_map);
let snapshot = block_map.read(wraps_snapshot.clone(), Default::default()); let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n"); assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
@ -1720,10 +1721,9 @@ mod tests {
{ {
let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default()); let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
let mut hash_map = HashMap::default(); let mut new_heights = HashMap::default();
let render: RenderBlock = Box::new(|_| div().into_any()); new_heights.insert(block_ids[0], 0);
hash_map.insert(block_ids[0], (0_u8, render)); block_map_writer.resize(new_heights);
block_map_writer.replace(hash_map);
let snapshot = block_map.read(wraps_snapshot.clone(), Default::default()); let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n"); assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
@ -1732,10 +1732,9 @@ mod tests {
{ {
let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default()); let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
let mut hash_map = HashMap::default(); let mut new_heights = HashMap::default();
let render: RenderBlock = Box::new(|_| div().into_any()); new_heights.insert(block_ids[0], 3);
hash_map.insert(block_ids[0], (3_u8, render)); block_map_writer.resize(new_heights);
block_map_writer.replace(hash_map);
let snapshot = block_map.read(wraps_snapshot.clone(), Default::default()); let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n"); assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
@ -1744,10 +1743,9 @@ mod tests {
{ {
let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default()); let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
let mut hash_map = HashMap::default(); let mut new_heights = HashMap::default();
let render: RenderBlock = Box::new(|_| div().into_any()); new_heights.insert(block_ids[0], 3);
hash_map.insert(block_ids[0], (3_u8, render)); block_map_writer.resize(new_heights);
block_map_writer.replace(hash_map);
let snapshot = block_map.read(wraps_snapshot.clone(), Default::default()); let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
// Same height as before, should remain the same // Same height as before, should remain the same
@ -2185,17 +2183,17 @@ mod tests {
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
enum ExpectedBlock { enum ExpectedBlock {
ExcerptHeader { ExcerptHeader {
height: u8, height: u32,
starts_new_buffer: bool, starts_new_buffer: bool,
}, },
ExcerptFooter { ExcerptFooter {
height: u8, height: u32,
disposition: BlockDisposition, disposition: BlockDisposition,
}, },
Custom { Custom {
disposition: BlockDisposition, disposition: BlockDisposition,
id: CustomBlockId, id: CustomBlockId,
height: u8, height: u32,
}, },
} }
@ -2214,7 +2212,7 @@ mod tests {
} }
impl ExpectedBlock { impl ExpectedBlock {
fn height(&self) -> u8 { fn height(&self) -> u32 {
match self { match self {
ExpectedBlock::ExcerptHeader { height, .. } => *height, ExpectedBlock::ExcerptHeader { height, .. } => *height,
ExpectedBlock::Custom { height, .. } => *height, ExpectedBlock::Custom { height, .. } => *height,

View File

@ -160,9 +160,9 @@ use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
use crate::hover_links::find_url; use crate::hover_links::find_url;
use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState}; use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
pub const FILE_HEADER_HEIGHT: u8 = 1; pub const FILE_HEADER_HEIGHT: u32 = 1;
pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1; pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u8 = 1; pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2; pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const MAX_LINE_LEN: usize = 1024; const MAX_LINE_LEN: usize = 1024;
@ -558,7 +558,7 @@ pub struct Editor {
tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>, tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
tasks_update_task: Option<Task<()>>, tasks_update_task: Option<Task<()>>,
previous_search_ranges: Option<Arc<[Range<Anchor>]>>, previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
file_header_size: u8, file_header_size: u32,
breadcrumb_header: Option<String>, breadcrumb_header: Option<String>,
focused_block: Option<FocusedBlock>, focused_block: Option<FocusedBlock>,
} }
@ -9805,14 +9805,11 @@ impl Editor {
for (block_id, diagnostic) in &active_diagnostics.blocks { for (block_id, diagnostic) in &active_diagnostics.blocks {
new_styles.insert( new_styles.insert(
*block_id, *block_id,
( diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
None,
diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
),
); );
} }
self.display_map.update(cx, |display_map, cx| { self.display_map.update(cx, |display_map, _cx| {
display_map.replace_blocks(new_styles, cx) display_map.replace_blocks(new_styles)
}); });
} }
} }
@ -9855,7 +9852,7 @@ impl Editor {
.insert_blocks( .insert_blocks(
diagnostic_group.iter().map(|entry| { diagnostic_group.iter().map(|entry| {
let diagnostic = entry.diagnostic.clone(); let diagnostic = entry.diagnostic.clone();
let message_height = diagnostic.message.matches('\n').count() as u8 + 1; let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
BlockProperties { BlockProperties {
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
position: buffer.anchor_after(entry.range.start), position: buffer.anchor_after(entry.range.start),
@ -10170,19 +10167,34 @@ impl Editor {
blocks blocks
} }
pub fn replace_blocks( pub(crate) fn resize_blocks(
&mut self, &mut self,
blocks: HashMap<CustomBlockId, (Option<u8>, RenderBlock)>, heights: HashMap<CustomBlockId, u32>,
autoscroll: Option<Autoscroll>, autoscroll: Option<Autoscroll>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
self.display_map self.display_map
.update(cx, |display_map, cx| display_map.replace_blocks(blocks, cx)); .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
if let Some(autoscroll) = autoscroll { if let Some(autoscroll) = autoscroll {
self.request_autoscroll(autoscroll, cx); self.request_autoscroll(autoscroll, cx);
} }
} }
pub fn replace_blocks(
&mut self,
renderers: HashMap<CustomBlockId, RenderBlock>,
autoscroll: Option<Autoscroll>,
cx: &mut ViewContext<Self>,
) {
self.display_map
.update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
if let Some(autoscroll) = autoscroll {
self.request_autoscroll(autoscroll, cx);
} else {
cx.notify();
}
}
pub fn remove_blocks( pub fn remove_blocks(
&mut self, &mut self,
block_ids: HashSet<CustomBlockId>, block_ids: HashSet<CustomBlockId>,
@ -11755,7 +11767,7 @@ impl Editor {
}) })
} }
pub fn file_header_size(&self) -> u8 { pub fn file_header_size(&self) -> u32 {
self.file_header_size self.file_header_size
} }

View File

@ -17,14 +17,14 @@ use crate::{
hunk_diff::ExpandedHunk, hunk_diff::ExpandedHunk,
hunk_status, hunk_status,
items::BufferSearchHighlights, items::BufferSearchHighlights,
mouse_context_menu::MenuPosition, mouse_context_menu::{self, MenuPosition, MouseContextMenu},
mouse_context_menu::{self, MouseContextMenu},
scroll::scroll_amount::ScrollAmount, scroll::scroll_amount::ScrollAmount,
BlockId, CodeActionsMenu, CursorShape, DisplayPoint, DisplayRow, DocumentHighlightRead, BlockId, CodeActionsMenu, CursorShape, CustomBlockId, DisplayPoint, DisplayRow,
DocumentHighlightWrite, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings,
ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp, HoveredCursor, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown,
HoveredHunk, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point, RangeToAnchorExt, RowExt, HalfPageUp, HoveredCursor, HoveredHunk, LineDown, LineUp, OpenExcerpts, PageDown, PageUp,
RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN, Point, RangeToAnchorExt, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint,
CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
}; };
use client::ParticipantIndex; use client::ParticipantIndex;
use collections::{BTreeMap, HashMap}; use collections::{BTreeMap, HashMap};
@ -1929,7 +1929,7 @@ impl EditorElement {
fn render_block( fn render_block(
&self, &self,
block: &Block, block: &Block,
available_space: Size<AvailableSpace>, available_width: AvailableSpace,
block_id: BlockId, block_id: BlockId,
block_row_start: DisplayRow, block_row_start: DisplayRow,
snapshot: &EditorSnapshot, snapshot: &EditorSnapshot,
@ -1941,6 +1941,7 @@ impl EditorElement {
em_width: Pixels, em_width: Pixels,
text_hitbox: &Hitbox, text_hitbox: &Hitbox,
scroll_width: &mut Pixels, scroll_width: &mut Pixels,
resized_blocks: &mut HashMap<CustomBlockId, u32>,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> (AnyElement, Size<Pixels>) { ) -> (AnyElement, Size<Pixels>) {
let mut element = match block { let mut element = match block {
@ -2021,7 +2022,7 @@ impl EditorElement {
}; };
let line_offset_from_top = let line_offset_from_top =
block_row_start.0 + *height as u32 + offset_from_excerpt_start block_row_start.0 + *height + offset_from_excerpt_start
- snapshot - snapshot
.scroll_anchor .scroll_anchor
.scroll_position(&snapshot.display_snapshot) .scroll_position(&snapshot.display_snapshot)
@ -2054,12 +2055,13 @@ impl EditorElement {
v_flex() v_flex()
.id(("path excerpt header", EntityId::from(block_id))) .id(("path excerpt header", EntityId::from(block_id)))
.size_full() .w_full()
.p(header_padding) .px(header_padding)
.child( .child(
h_flex() h_flex()
.flex_basis(Length::Definite(DefiniteLength::Fraction(0.667))) .flex_basis(Length::Definite(DefiniteLength::Fraction(0.667)))
.id("path header block") .id("path header block")
.h(2. * cx.line_height())
.pl(gpui::px(12.)) .pl(gpui::px(12.))
.pr(gpui::px(8.)) .pr(gpui::px(8.))
.rounded_md() .rounded_md()
@ -2112,6 +2114,7 @@ impl EditorElement {
.children(show_excerpt_controls.then(|| { .children(show_excerpt_controls.then(|| {
h_flex() h_flex()
.flex_basis(Length::Definite(DefiniteLength::Fraction(0.333))) .flex_basis(Length::Definite(DefiniteLength::Fraction(0.333)))
.h(1. * cx.line_height())
.pt_1() .pt_1()
.justify_end() .justify_end()
.flex_none() .flex_none()
@ -2157,7 +2160,8 @@ impl EditorElement {
} else { } else {
v_flex() v_flex()
.id(("excerpt header", EntityId::from(block_id))) .id(("excerpt header", EntityId::from(block_id)))
.size_full() .w_full()
.h(snapshot.excerpt_header_height() as f32 * cx.line_height())
.child( .child(
div() div()
.flex() .flex()
@ -2309,7 +2313,8 @@ impl EditorElement {
Block::ExcerptFooter { id, .. } => { Block::ExcerptFooter { id, .. } => {
let element = v_flex() let element = v_flex()
.id(("excerpt footer", EntityId::from(block_id))) .id(("excerpt footer", EntityId::from(block_id)))
.size_full() .w_full()
.h(snapshot.excerpt_footer_height() as f32 * cx.line_height())
.child( .child(
h_flex() h_flex()
.justify_end() .justify_end()
@ -2357,8 +2362,24 @@ impl EditorElement {
} }
}; };
let size = element.layout_as_root(available_space, cx); // Discover the element's content height, then round up to the nearest multiple of line height.
(element, size) let preliminary_size =
element.layout_as_root(size(available_width, AvailableSpace::MinContent), cx);
let quantized_height = (preliminary_size.height / line_height).ceil() * line_height;
let final_size = if preliminary_size.height == quantized_height {
preliminary_size
} else {
element.layout_as_root(size(available_width, quantized_height.into()), cx)
};
if let BlockId::Custom(custom_block_id) = block_id {
let element_height_in_lines = (final_size.height / line_height).ceil() as u32;
if element_height_in_lines != block.height() {
resized_blocks.insert(custom_block_id, element_height_in_lines);
}
}
(element, final_size)
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -2375,7 +2396,7 @@ impl EditorElement {
line_height: Pixels, line_height: Pixels,
line_layouts: &[LineWithInvisibles], line_layouts: &[LineWithInvisibles],
cx: &mut WindowContext, cx: &mut WindowContext,
) -> Vec<BlockLayout> { ) -> Result<Vec<BlockLayout>, HashMap<CustomBlockId, u32>> {
let (fixed_blocks, non_fixed_blocks) = snapshot let (fixed_blocks, non_fixed_blocks) = snapshot
.blocks_in_range(rows.clone()) .blocks_in_range(rows.clone())
.partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed); .partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed);
@ -2385,11 +2406,9 @@ impl EditorElement {
.update(cx, |editor, _| editor.take_focused_block()); .update(cx, |editor, _| editor.take_focused_block());
let mut fixed_block_max_width = Pixels::ZERO; let mut fixed_block_max_width = Pixels::ZERO;
let mut blocks = Vec::new(); let mut blocks = Vec::new();
let mut resized_blocks = HashMap::default();
for (row, block) in fixed_blocks { for (row, block) in fixed_blocks {
let available_space = size(
AvailableSpace::MinContent,
AvailableSpace::Definite(block.height() as f32 * line_height),
);
let block_id = block.id(); let block_id = block.id();
if focused_block.as_ref().map_or(false, |b| b.id == block_id) { if focused_block.as_ref().map_or(false, |b| b.id == block_id) {
@ -2398,7 +2417,7 @@ impl EditorElement {
let (element, element_size) = self.render_block( let (element, element_size) = self.render_block(
block, block,
available_space, AvailableSpace::MinContent,
block_id, block_id,
row, row,
snapshot, snapshot,
@ -2410,6 +2429,7 @@ impl EditorElement {
em_width, em_width,
text_hitbox, text_hitbox,
scroll_width, scroll_width,
&mut resized_blocks,
cx, cx,
); );
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width); fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
@ -2417,7 +2437,7 @@ impl EditorElement {
id: block_id, id: block_id,
row: Some(row), row: Some(row),
element, element,
available_space, available_space: size(AvailableSpace::MinContent, element_size.height.into()),
style: BlockStyle::Fixed, style: BlockStyle::Fixed,
}); });
} }
@ -2432,19 +2452,15 @@ impl EditorElement {
.max(gutter_dimensions.width + *scroll_width), .max(gutter_dimensions.width + *scroll_width),
BlockStyle::Fixed => unreachable!(), BlockStyle::Fixed => unreachable!(),
}; };
let available_space = size(
AvailableSpace::Definite(width),
AvailableSpace::Definite(block.height() as f32 * line_height),
);
let block_id = block.id(); let block_id = block.id();
if focused_block.as_ref().map_or(false, |b| b.id == block_id) { if focused_block.as_ref().map_or(false, |b| b.id == block_id) {
focused_block = None; focused_block = None;
} }
let (element, _) = self.render_block( let (element, element_size) = self.render_block(
block, block,
available_space, width.into(),
block_id, block_id,
row, row,
snapshot, snapshot,
@ -2456,13 +2472,15 @@ impl EditorElement {
em_width, em_width,
text_hitbox, text_hitbox,
scroll_width, scroll_width,
&mut resized_blocks,
cx, cx,
); );
blocks.push(BlockLayout { blocks.push(BlockLayout {
id: block_id, id: block_id,
row: Some(row), row: Some(row),
element, element,
available_space, available_space: size(width.into(), element_size.height.into()),
style, style,
}); });
} }
@ -2483,14 +2501,10 @@ impl EditorElement {
), ),
BlockStyle::Sticky => AvailableSpace::Definite(hitbox.size.width), BlockStyle::Sticky => AvailableSpace::Definite(hitbox.size.width),
}; };
let available_space = size(
width,
AvailableSpace::Definite(block.height() as f32 * line_height),
);
let (element, _) = self.render_block( let (element, element_size) = self.render_block(
&block, &block,
available_space, width,
focused_block.id, focused_block.id,
rows.end, rows.end,
snapshot, snapshot,
@ -2502,6 +2516,7 @@ impl EditorElement {
em_width, em_width,
text_hitbox, text_hitbox,
scroll_width, scroll_width,
&mut resized_blocks,
cx, cx,
); );
@ -2509,7 +2524,7 @@ impl EditorElement {
id: block.id(), id: block.id(),
row: None, row: None,
element, element,
available_space, available_space: size(width, element_size.height.into()),
style, style,
}); });
} }
@ -2517,10 +2532,16 @@ impl EditorElement {
} }
} }
*scroll_width = (*scroll_width).max(fixed_block_max_width - gutter_dimensions.width); if resized_blocks.is_empty() {
blocks *scroll_width = (*scroll_width).max(fixed_block_max_width - gutter_dimensions.width);
Ok(blocks)
} else {
Err(resized_blocks)
}
} }
/// Returns true if any of the blocks changed size since the previous frame. This will trigger
/// a restart of rendering for the editor based on the new sizes.
fn layout_blocks( fn layout_blocks(
&self, &self,
blocks: &mut Vec<BlockLayout>, blocks: &mut Vec<BlockLayout>,
@ -4938,21 +4959,27 @@ impl Element for EditorElement {
editor.gutter_dimensions = gutter_dimensions; editor.gutter_dimensions = gutter_dimensions;
editor.set_visible_line_count(bounds.size.height / line_height, cx); editor.set_visible_line_count(bounds.size.height / line_height, cx);
let editor_width = if matches!(editor.mode, EditorMode::AutoHeight { .. }) {
text_width - gutter_dimensions.margin - overscroll.width - em_width;
let wrap_width = match editor.soft_wrap_mode(cx) {
SoftWrap::None => None,
SoftWrap::PreferLine => Some((MAX_LINE_LEN / 2) as f32 * em_advance),
SoftWrap::EditorWidth => Some(editor_width),
SoftWrap::Column(column) => {
Some(editor_width.min(column as f32 * em_advance))
}
};
if editor.set_wrap_width(wrap_width, cx) {
editor.snapshot(cx)
} else {
snapshot snapshot
} else {
let editor_width =
text_width - gutter_dimensions.margin - overscroll.width - em_width;
let wrap_width = match editor.soft_wrap_mode(cx) {
SoftWrap::None => None,
SoftWrap::PreferLine => {
Some((MAX_LINE_LEN / 2) as f32 * em_advance)
}
SoftWrap::EditorWidth => Some(editor_width),
SoftWrap::Column(column) => {
Some(editor_width.min(column as f32 * em_advance))
}
};
if editor.set_wrap_width(wrap_width, cx) {
editor.snapshot(cx)
} else {
snapshot
}
} }
}); });
@ -4995,11 +5022,13 @@ impl Element for EditorElement {
} }
}; };
let mut autoscroll_request = None;
let mut autoscroll_containing_element = false; let mut autoscroll_containing_element = false;
let mut autoscroll_horizontally = false; let mut autoscroll_horizontally = false;
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
autoscroll_request = editor.autoscroll_request();
autoscroll_containing_element = autoscroll_containing_element =
editor.autoscroll_requested() || editor.has_pending_selection(); autoscroll_request.is_some() || editor.has_pending_selection();
autoscroll_horizontally = autoscroll_horizontally =
editor.autoscroll_vertically(bounds, line_height, max_scroll_top, cx); editor.autoscroll_vertically(bounds, line_height, max_scroll_top, cx);
snapshot = editor.snapshot(cx); snapshot = editor.snapshot(cx);
@ -5116,7 +5145,7 @@ impl Element for EditorElement {
let mut scroll_width = let mut scroll_width =
longest_line_width.max(max_visible_line_width) + overscroll.width; longest_line_width.max(max_visible_line_width) + overscroll.width;
let mut blocks = cx.with_element_namespace("blocks", |cx| { let blocks = cx.with_element_namespace("blocks", |cx| {
self.render_blocks( self.render_blocks(
start_row..end_row, start_row..end_row,
&snapshot, &snapshot,
@ -5131,6 +5160,15 @@ impl Element for EditorElement {
cx, cx,
) )
}); });
let mut blocks = match blocks {
Ok(blocks) => blocks,
Err(resized_blocks) => {
self.editor.update(cx, |editor, cx| {
editor.resize_blocks(resized_blocks, autoscroll_request, cx)
});
return self.prepaint(None, bounds, &mut (), cx);
}
};
let start_buffer_row = let start_buffer_row =
MultiBufferRow(start_anchor.to_point(&snapshot.buffer_snapshot).row); MultiBufferRow(start_anchor.to_point(&snapshot.buffer_snapshot).row);
@ -6430,7 +6468,7 @@ mod tests {
disposition: BlockDisposition::Above, disposition: BlockDisposition::Above,
height: 3, height: 3,
position: Anchor::min(), position: Anchor::min(),
render: Box::new(|_| div().into_any()), render: Box::new(|cx| div().h(3. * cx.line_height()).into_any()),
}], }],
None, None,
cx, cx,

View File

@ -364,7 +364,7 @@ impl Editor {
.row; .row;
let diff_end_row = diff_base.offset_to_point(hunk.diff_base_byte_range.end).row; let diff_end_row = diff_base.offset_to_point(hunk.diff_base_byte_range.end).row;
let line_count = diff_end_row - diff_start_row; let line_count = diff_end_row - diff_start_row;
line_count as u8 line_count
})?; })?;
Some((diff_base_buffer, deleted_text_lines)) Some((diff_base_buffer, deleted_text_lines))
} else { } else {
@ -422,7 +422,7 @@ impl Editor {
fn insert_deleted_text_block( fn insert_deleted_text_block(
&mut self, &mut self,
diff_base_buffer: Model<Buffer>, diff_base_buffer: Model<Buffer>,
deleted_text_height: u8, deleted_text_height: u32,
hunk: &HoveredHunk, hunk: &HoveredHunk,
cx: &mut ViewContext<'_, Self>, cx: &mut ViewContext<'_, Self>,
) -> Option<CustomBlockId> { ) -> Option<CustomBlockId> {
@ -431,10 +431,11 @@ impl Editor {
editor_with_deleted_text(diff_base_buffer, deleted_hunk_color, hunk, cx); editor_with_deleted_text(diff_base_buffer, deleted_hunk_color, hunk, cx);
let editor = cx.view().clone(); let editor = cx.view().clone();
let hunk = hunk.clone(); let hunk = hunk.clone();
let height = editor_height.max(deleted_text_height);
let mut new_block_ids = self.insert_blocks( let mut new_block_ids = self.insert_blocks(
Some(BlockProperties { Some(BlockProperties {
position: hunk.multi_buffer_range.start, position: hunk.multi_buffer_range.start,
height: editor_height.max(deleted_text_height), height,
style: BlockStyle::Flex, style: BlockStyle::Flex,
disposition: BlockDisposition::Above, disposition: BlockDisposition::Above,
render: Box::new(move |cx| { render: Box::new(move |cx| {
@ -474,7 +475,8 @@ impl Editor {
h_flex() h_flex()
.id("gutter with editor") .id("gutter with editor")
.bg(deleted_hunk_color) .bg(deleted_hunk_color)
.size_full() .h(height as f32 * cx.line_height())
.w_full()
.child( .child(
h_flex() h_flex()
.id("gutter") .id("gutter")
@ -783,7 +785,7 @@ fn editor_with_deleted_text(
deleted_color: Hsla, deleted_color: Hsla,
hunk: &HoveredHunk, hunk: &HoveredHunk,
cx: &mut ViewContext<'_, Editor>, cx: &mut ViewContext<'_, Editor>,
) -> (u8, View<Editor>) { ) -> (u32, View<Editor>) {
let parent_editor = cx.view().downgrade(); let parent_editor = cx.view().downgrade();
let editor = cx.new_view(|cx| { let editor = cx.new_view(|cx| {
let multi_buffer = let multi_buffer =
@ -885,7 +887,7 @@ fn editor_with_deleted_text(
editor editor
}); });
let editor_height = editor.update(cx, |editor, cx| editor.max_point(cx).row().0 as u8); let editor_height = editor.update(cx, |editor, cx| editor.max_point(cx).row().0);
(editor_height, editor) (editor_height, editor)
} }

View File

@ -307,8 +307,8 @@ impl ScrollManager {
self.show_scrollbars self.show_scrollbars
} }
pub fn autoscroll_requested(&self) -> bool { pub fn autoscroll_request(&self) -> Option<Autoscroll> {
self.autoscroll_request.is_some() self.autoscroll_request.map(|(autoscroll, _)| autoscroll)
} }
pub fn is_dragging_scrollbar(&self) -> bool { pub fn is_dragging_scrollbar(&self) -> bool {

View File

@ -61,8 +61,8 @@ impl AutoscrollStrategy {
} }
impl Editor { impl Editor {
pub fn autoscroll_requested(&self) -> bool { pub fn autoscroll_request(&self) -> Option<Autoscroll> {
self.scroll_manager.autoscroll_requested() self.scroll_manager.autoscroll_request()
} }
pub fn autoscroll_vertically( pub fn autoscroll_vertically(

View File

@ -43,11 +43,6 @@ impl FeatureFlag for LanguageModels {
const NAME: &'static str = "language-models"; const NAME: &'static str = "language-models";
} }
pub struct GroupedDiagnostics {}
impl FeatureFlag for GroupedDiagnostics {
const NAME: &'static str = "grouped-diagnostics";
}
pub struct ZedPro {} pub struct ZedPro {}
impl FeatureFlag for ZedPro { impl FeatureFlag for ZedPro {
const NAME: &'static str = "zed-pro"; const NAME: &'static str = "zed-pro";

View File

@ -1366,11 +1366,7 @@ impl<'a> WindowContext<'a> {
/// The line height associated with the current text style. /// The line height associated with the current text style.
pub fn line_height(&self) -> Pixels { pub fn line_height(&self) -> Pixels {
let rem_size = self.rem_size(); self.text_style().line_height_in_pixels(self.rem_size())
let text_style = self.text_style();
text_style
.line_height
.to_pixels(text_style.font_size, rem_size)
} }
/// Call to prevent the default action of an event. Currently only used to prevent /// Call to prevent the default action of an event. Currently only used to prevent

View File

@ -19,7 +19,7 @@ use ui::{div, prelude::*, v_flex, IntoElement, Styled, ViewContext};
/// Given these outputs are destined for the editor with the block decorations API, all of them must report /// Given these outputs are destined for the editor with the block decorations API, all of them must report
/// how many lines they will take up in the editor. /// how many lines they will take up in the editor.
pub trait LineHeight: Sized { pub trait LineHeight: Sized {
fn num_lines(&self, cx: &mut WindowContext) -> u8; fn num_lines(&self, cx: &mut WindowContext) -> usize;
} }
/// When deciding what to render from a collection of mediatypes, we need to rank them in order of importance /// When deciding what to render from a collection of mediatypes, we need to rank them in order of importance
@ -88,15 +88,9 @@ impl ImageView {
} }
impl LineHeight for ImageView { impl LineHeight for ImageView {
fn num_lines(&self, cx: &mut WindowContext) -> u8 { fn num_lines(&self, cx: &mut WindowContext) -> usize {
let line_height = cx.line_height(); let line_height = cx.line_height();
(self.height as f32 / line_height.0) as usize
let lines = self.height as f32 / line_height.0;
if lines > u8::MAX as f32 {
return u8::MAX;
}
lines as u8
} }
} }
@ -257,7 +251,7 @@ impl TableView {
} }
impl LineHeight for TableView { impl LineHeight for TableView {
fn num_lines(&self, _cx: &mut WindowContext) -> u8 { fn num_lines(&self, _cx: &mut WindowContext) -> usize {
let num_rows = match &self.table.data { let num_rows = match &self.table.data {
// Rows + header // Rows + header
Some(data) => data.len() + 1, Some(data) => data.len() + 1,
@ -267,7 +261,7 @@ impl LineHeight for TableView {
}; };
let num_lines = num_rows as f32 * (1.0 + TABLE_Y_PADDING_MULTIPLE) + 1.0; let num_lines = num_rows as f32 * (1.0 + TABLE_Y_PADDING_MULTIPLE) + 1.0;
num_lines.ceil() as u8 num_lines.ceil() as usize
} }
} }
@ -303,12 +297,9 @@ impl ErrorView {
} }
impl LineHeight for ErrorView { impl LineHeight for ErrorView {
fn num_lines(&self, cx: &mut WindowContext) -> u8 { fn num_lines(&self, cx: &mut WindowContext) -> usize {
let mut height: u8 = 1; // Start at 1 to account for the y padding // Start at 1 to account for the y padding
height = height.saturating_add(self.ename.lines().count() as u8); 1 + self.ename.lines().count() + self.evalue.lines().count() + self.traceback.num_lines(cx)
height = height.saturating_add(self.evalue.lines().count() as u8);
height = height.saturating_add(self.traceback.num_lines(cx));
height
} }
} }
@ -357,12 +348,12 @@ impl OutputType {
impl LineHeight for OutputType { impl LineHeight for OutputType {
/// Calculates the expected number of lines /// Calculates the expected number of lines
fn num_lines(&self, cx: &mut WindowContext) -> u8 { fn num_lines(&self, cx: &mut WindowContext) -> usize {
match self { match self {
Self::Plain(stdio) => stdio.num_lines(cx), Self::Plain(stdio) => stdio.num_lines(cx),
Self::Stream(stdio) => stdio.num_lines(cx), Self::Stream(stdio) => stdio.num_lines(cx),
Self::Image(image) => image.num_lines(cx), Self::Image(image) => image.num_lines(cx),
Self::Message(message) => message.lines().count() as u8, Self::Message(message) => message.lines().count(),
Self::Table(table) => table.num_lines(cx), Self::Table(table) => table.num_lines(cx),
Self::ErrorOutput(error_view) => error_view.num_lines(cx), Self::ErrorOutput(error_view) => error_view.num_lines(cx),
Self::ClearOutputWaitMarker => 0, Self::ClearOutputWaitMarker => 0,
@ -572,7 +563,7 @@ impl Render for ExecutionView {
} }
impl LineHeight for ExecutionView { impl LineHeight for ExecutionView {
fn num_lines(&self, cx: &mut WindowContext) -> u8 { fn num_lines(&self, cx: &mut WindowContext) -> usize {
if self.outputs.is_empty() { if self.outputs.is_empty() {
return 1; // For the status message if outputs are not there return 1; // For the status message if outputs are not there
} }
@ -581,9 +572,7 @@ impl LineHeight for ExecutionView {
.outputs .outputs
.iter() .iter()
.map(|output| output.num_lines(cx)) .map(|output| output.num_lines(cx))
.fold(0_u8, |acc, additional_height| { .sum::<usize>()
acc.saturating_add(additional_height)
})
.max(1); .max(1);
let num_lines = match self.status { let num_lines = match self.status {
@ -597,7 +586,7 @@ impl LineHeight for ExecutionView {
} }
impl LineHeight for View<ExecutionView> { impl LineHeight for View<ExecutionView> {
fn num_lines(&self, cx: &mut WindowContext) -> u8 { fn num_lines(&self, cx: &mut WindowContext) -> usize {
self.update(cx, |execution_view, cx| execution_view.num_lines(cx)) self.update(cx, |execution_view, cx| execution_view.num_lines(cx))
} }
} }

View File

@ -42,12 +42,10 @@ pub struct Session {
} }
struct EditorBlock { struct EditorBlock {
editor: WeakView<Editor>,
code_range: Range<Anchor>, code_range: Range<Anchor>,
invalidation_anchor: Anchor, invalidation_anchor: Anchor,
block_id: CustomBlockId, block_id: CustomBlockId,
execution_view: View<ExecutionView>, execution_view: View<ExecutionView>,
on_close: CloseBlockFn,
} }
type CloseBlockFn = type CloseBlockFn =
@ -84,7 +82,7 @@ impl EditorBlock {
let invalidation_anchor = buffer.read(cx).read(cx).anchor_before(next_row_start); let invalidation_anchor = buffer.read(cx).read(cx).anchor_before(next_row_start);
let block = BlockProperties { let block = BlockProperties {
position: code_range.end, position: code_range.end,
height: execution_view.num_lines(cx).saturating_add(1), height: (execution_view.num_lines(cx) + 1) as u32,
style: BlockStyle::Sticky, style: BlockStyle::Sticky,
render: Self::create_output_area_renderer(execution_view.clone(), on_close.clone()), render: Self::create_output_area_renderer(execution_view.clone(), on_close.clone()),
disposition: BlockDisposition::Below, disposition: BlockDisposition::Below,
@ -95,12 +93,10 @@ impl EditorBlock {
})?; })?;
anyhow::Ok(Self { anyhow::Ok(Self {
editor,
code_range, code_range,
invalidation_anchor, invalidation_anchor,
block_id, block_id,
execution_view, execution_view,
on_close,
}) })
} }
@ -108,24 +104,6 @@ impl EditorBlock {
self.execution_view.update(cx, |execution_view, cx| { self.execution_view.update(cx, |execution_view, cx| {
execution_view.push_message(&message.content, cx); execution_view.push_message(&message.content, cx);
}); });
self.editor
.update(cx, |editor, cx| {
let mut replacements = HashMap::default();
replacements.insert(
self.block_id,
(
Some(self.execution_view.num_lines(cx).saturating_add(1)),
Self::create_output_area_renderer(
self.execution_view.clone(),
self.on_close.clone(),
),
),
);
editor.replace_blocks(replacements, None, cx);
})
.ok();
} }
fn create_output_area_renderer( fn create_output_area_renderer(

View File

@ -96,8 +96,8 @@ impl TerminalOutput {
} }
impl LineHeight for TerminalOutput { impl LineHeight for TerminalOutput {
fn num_lines(&self, _cx: &mut WindowContext) -> u8 { fn num_lines(&self, _cx: &mut WindowContext) -> usize {
self.handler.buffer.lines().count().max(1) as u8 self.handler.buffer.lines().count().max(1)
} }
} }